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) {
4954 Roo.lib.UndoManager.prototype = {
4965 * To push and execute a transaction, the method undoManager.transact
4966 * must be called by passing a transaction object as the first argument, and a merge
4967 * flag as the second argument. A transaction object has the following properties:
4971 undoManager.transact({
4973 execute: function() { ... },
4974 undo: function() { ... },
4975 // redo same as execute
4976 redo: function() { this.execute(); }
4979 // merge transaction
4980 undoManager.transact({
4982 execute: function() { ... }, // this will be run...
4983 undo: function() { ... }, // what to do when undo is run.
4984 // redo same as execute
4985 redo: function() { this.execute(); }
4990 * @param {Object} transaction The transaction to add to the stack.
4991 * @return {String} The HTML fragment
4995 transact : function (transaction, merge)
4997 if (arguments.length < 2) {
4998 throw new TypeError('Not enough arguments to UndoManager.transact.');
5001 transaction.execute();
5003 this.stack.splice(0, this.position);
5004 if (merge && this.length) {
5005 this.stack[0].push(transaction);
5007 this.stack.unshift([transaction]);
5012 if (this.limit && this.stack.length > this.limit) {
5013 this.length = this.stack.length = this.limit;
5015 this.length = this.stack.length;
5018 if (this.fireEvent) {
5019 this.scope.dispatchEvent(
5020 new CustomEvent('DOMTransaction', {
5022 transactions: this.stack[0].slice()
5030 Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5037 Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5039 if (this.position < this.length) {
5040 for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5041 this.stack[this.position][i].undo();
5045 if (this.fireEvent) {
5046 this.scope.dispatchEvent(
5047 new CustomEvent('undo', {
5049 transactions: this.stack[this.position - 1].slice()
5061 if (this.position > 0) {
5062 for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5063 this.stack[this.position - 1][i].redo();
5067 if (this.fireEvent) {
5068 this.scope.dispatchEvent(
5069 new CustomEvent('redo', {
5071 transactions: this.stack[this.position].slice()
5081 item : function (index)
5083 if (index >= 0 && index < this.length) {
5084 return this.stack[index].slice();
5089 clearUndo : function () {
5090 this.stack.length = this.length = this.position;
5093 clearRedo : function () {
5094 this.stack.splice(0, this.position);
5096 this.length = this.stack.length;
5099 * Reset the undo - probaly done on load to clear all history.
5106 this.current_html = this.scope.innerHTML;
5107 if (this.timer !== false) {
5108 clearTimeout(this.timer);
5120 // this will handle the undo/redo on the element.?
5121 bindEvents : function()
5123 var el = this.scope;
5124 el.undoManager = this;
5127 this.scope.addEventListener('keydown', function(e) {
5128 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5130 el.undoManager.redo(); // Ctrl/Command + Shift + Z
5132 el.undoManager.undo(); // Ctrl/Command + Z
5139 this.scope.addEventListener('keyup', function(e) {
5140 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5149 el.addEventListener('input', function(e) {
5150 if(el.innerHTML == t.current_html) {
5153 // only record events every second.
5154 if (t.timer !== false) {
5155 clearTimeout(t.timer);
5158 t.timer = setTimeout(function() { t.merge = false; }, 1000);
5160 t.addEvent(t.merge);
5161 t.merge = true; // ignore changes happening every second..
5165 * Manually add an event.
5166 * Normall called without arguements - and it will just get added to the stack.
5170 addEvent : function(merge)
5172 Roo.log("undomanager +" + (merge ? 'Y':'n'));
5173 // not sure if this should clear the timer
5174 merge = typeof(merge) == 'undefined' ? false : merge;
5176 this.scope.undoManager.transact({
5178 oldHTML: this.current_html,
5179 newHTML: this.scope.innerHTML,
5180 // nothing to execute (content already changed when input is fired)
5181 execute: function() { },
5183 this.scope.innerHTML = this.current_html = this.oldHTML;
5186 this.scope.innerHTML = this.current_html = this.newHTML;
5188 }, false); //merge);
5192 this.current_html = this.scope.innerHTML;
5203 * Ext JS Library 1.1.1
5204 * Copyright(c) 2006-2007, Ext JS, LLC.
5206 * Originally Released Under LGPL - original licence link has changed is not relivant.
5209 * <script type="text/javascript">
5213 // nasty IE9 hack - what a pile of crap that is..
5215 if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5216 Range.prototype.createContextualFragment = function (html) {
5217 var doc = window.document;
5218 var container = doc.createElement("div");
5219 container.innerHTML = html;
5220 var frag = doc.createDocumentFragment(), n;
5221 while ((n = container.firstChild)) {
5222 frag.appendChild(n);
5229 * @class Roo.DomHelper
5230 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5231 * 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>.
5234 Roo.DomHelper = function(){
5235 var tempTableEl = null;
5236 var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5237 var tableRe = /^table|tbody|tr|td$/i;
5239 // build as innerHTML where available
5241 var createHtml = function(o){
5242 if(typeof o == 'string'){
5251 if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5252 if(attr == "style"){
5254 if(typeof s == "function"){
5257 if(typeof s == "string"){
5258 b += ' style="' + s + '"';
5259 }else if(typeof s == "object"){
5262 if(typeof s[key] != "function"){
5263 b += key + ":" + s[key] + ";";
5270 b += ' class="' + o["cls"] + '"';
5271 }else if(attr == "htmlFor"){
5272 b += ' for="' + o["htmlFor"] + '"';
5274 b += " " + attr + '="' + o[attr] + '"';
5278 if(emptyTags.test(o.tag)){
5282 var cn = o.children || o.cn;
5284 //http://bugs.kde.org/show_bug.cgi?id=71506
5285 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5286 for(var i = 0, len = cn.length; i < len; i++) {
5287 b += createHtml(cn[i], b);
5290 b += createHtml(cn, b);
5296 b += "</" + o.tag + ">";
5303 var createDom = function(o, parentNode){
5305 // defininition craeted..
5307 if (o.ns && o.ns != 'html') {
5309 if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5310 xmlns[o.ns] = o.xmlns;
5313 if (typeof(xmlns[o.ns]) == 'undefined') {
5314 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5320 if (typeof(o) == 'string') {
5321 return parentNode.appendChild(document.createTextNode(o));
5323 o.tag = o.tag || div;
5324 if (o.ns && Roo.isIE) {
5326 o.tag = o.ns + ':' + o.tag;
5329 var el = ns ? document.createElementNS( ns, o.tag||'div') : document.createElement(o.tag||'div');
5330 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5333 if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" ||
5334 attr == "style" || typeof o[attr] == "function") { continue; }
5336 if(attr=="cls" && Roo.isIE){
5337 el.className = o["cls"];
5339 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5345 Roo.DomHelper.applyStyles(el, o.style);
5346 var cn = o.children || o.cn;
5348 //http://bugs.kde.org/show_bug.cgi?id=71506
5349 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5350 for(var i = 0, len = cn.length; i < len; i++) {
5351 createDom(cn[i], el);
5358 el.innerHTML = o.html;
5361 parentNode.appendChild(el);
5366 var ieTable = function(depth, s, h, e){
5367 tempTableEl.innerHTML = [s, h, e].join('');
5368 var i = -1, el = tempTableEl;
5369 while(++i < depth && el.firstChild){
5375 // kill repeat to save bytes
5379 tbe = '</tbody>'+te,
5385 * Nasty code for IE's broken table implementation
5387 var insertIntoTable = function(tag, where, el, html){
5389 tempTableEl = document.createElement('div');
5394 if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5397 if(where == 'beforebegin'){
5401 before = el.nextSibling;
5404 node = ieTable(4, trs, html, tre);
5406 else if(tag == 'tr'){
5407 if(where == 'beforebegin'){
5410 node = ieTable(3, tbs, html, tbe);
5411 } else if(where == 'afterend'){
5412 before = el.nextSibling;
5414 node = ieTable(3, tbs, html, tbe);
5415 } else{ // INTO a TR
5416 if(where == 'afterbegin'){
5417 before = el.firstChild;
5419 node = ieTable(4, trs, html, tre);
5421 } else if(tag == 'tbody'){
5422 if(where == 'beforebegin'){
5425 node = ieTable(2, ts, html, te);
5426 } else if(where == 'afterend'){
5427 before = el.nextSibling;
5429 node = ieTable(2, ts, html, te);
5431 if(where == 'afterbegin'){
5432 before = el.firstChild;
5434 node = ieTable(3, tbs, html, tbe);
5437 if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5440 if(where == 'afterbegin'){
5441 before = el.firstChild;
5443 node = ieTable(2, ts, html, te);
5445 el.insertBefore(node, before);
5449 // this is a bit like the react update code...
5452 var updateNode = function(from, to)
5454 // should we handle non-standard elements?
5455 Roo.log(["UpdateNode" , from, to]);
5456 if (from.nodeType != to.nodeType) {
5457 Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5458 from.parentNode.replaceChild(to, from);
5461 if (from.nodeType == 3) {
5462 // assume it's text?!
5463 if (from.data == to.data) {
5466 from.data = to.data;
5470 // assume 'to' doesnt have '1/3 nodetypes!
5471 if (from.nodeType !=1 || from.tagName != to.tagName) {
5472 Roo.log(["ReplaceChild" , from, to ]);
5473 from.parentNode.replaceChild(to, from);
5476 // compare attributes
5477 var ar = Array.from(from.attributes);
5478 for(var i = 0; i< ar.length;i++) {
5479 if (to.hasAttribute(ar[i].name)) {
5482 if (ar[i].name == 'id') { // always keep ids?
5485 if (ar[i].name == 'style') {
5486 throw "style removed?";
5488 Roo.log("removeAttribute" + ar[i].name);
5489 from.removeAttribute(ar[i].name);
5492 for(var i = 0; i< ar.length;i++) {
5493 if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5496 Roo.log("updateAttribute " + from.getAttribute(ar[i].name) + '=>' + to.getAttribute(ar[i].name));
5497 from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5500 var far = Array.from(from.childNodes);
5501 var tar = Array.from(to.childNodes);
5502 // if the lengths are different.. then it's probably a editable content change, rather than
5503 // a change of the block definition..
5505 // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5506 /*if (from.innerHTML == to.innerHTML) {
5509 if (far.length != tar.length) {
5510 from.innerHTML = to.innerHTML;
5515 for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5516 if (i >= far.length) {
5517 from.appendChild(tar[i]);
5518 Roo.log(["add", tar[i]]);
5520 } else if ( i >= tar.length) {
5521 from.removeChild(far[i]);
5522 Roo.log(["remove", far[i]]);
5525 updateNode(far[i], tar[i]);
5537 /** True to force the use of DOM instead of html fragments @type Boolean */
5541 * Returns the markup for the passed Element(s) config
5542 * @param {Object} o The Dom object spec (and children)
5545 markup : function(o){
5546 return createHtml(o);
5550 * Applies a style specification to an element
5551 * @param {String/HTMLElement} el The element to apply styles to
5552 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5553 * a function which returns such a specification.
5555 applyStyles : function(el, styles){
5558 if(typeof styles == "string"){
5559 var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5561 while ((matches = re.exec(styles)) != null){
5562 el.setStyle(matches[1], matches[2]);
5564 }else if (typeof styles == "object"){
5565 for (var style in styles){
5566 el.setStyle(style, styles[style]);
5568 }else if (typeof styles == "function"){
5569 Roo.DomHelper.applyStyles(el, styles.call());
5575 * Inserts an HTML fragment into the Dom
5576 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5577 * @param {HTMLElement} el The context element
5578 * @param {String} html The HTML fragmenet
5579 * @return {HTMLElement} The new node
5581 insertHtml : function(where, el, html){
5582 where = where.toLowerCase();
5583 if(el.insertAdjacentHTML){
5584 if(tableRe.test(el.tagName)){
5586 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5592 el.insertAdjacentHTML('BeforeBegin', html);
5593 return el.previousSibling;
5595 el.insertAdjacentHTML('AfterBegin', html);
5596 return el.firstChild;
5598 el.insertAdjacentHTML('BeforeEnd', html);
5599 return el.lastChild;
5601 el.insertAdjacentHTML('AfterEnd', html);
5602 return el.nextSibling;
5604 throw 'Illegal insertion point -> "' + where + '"';
5606 var range = el.ownerDocument.createRange();
5610 range.setStartBefore(el);
5611 frag = range.createContextualFragment(html);
5612 el.parentNode.insertBefore(frag, el);
5613 return el.previousSibling;
5616 range.setStartBefore(el.firstChild);
5617 frag = range.createContextualFragment(html);
5618 el.insertBefore(frag, el.firstChild);
5619 return el.firstChild;
5621 el.innerHTML = html;
5622 return el.firstChild;
5626 range.setStartAfter(el.lastChild);
5627 frag = range.createContextualFragment(html);
5628 el.appendChild(frag);
5629 return el.lastChild;
5631 el.innerHTML = html;
5632 return el.lastChild;
5635 range.setStartAfter(el);
5636 frag = range.createContextualFragment(html);
5637 el.parentNode.insertBefore(frag, el.nextSibling);
5638 return el.nextSibling;
5640 throw 'Illegal insertion point -> "' + where + '"';
5644 * Creates new Dom element(s) and inserts them before 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 insertBefore : function(el, o, returnElement){
5651 return this.doInsert(el, o, returnElement, "beforeBegin");
5655 * Creates new Dom element(s) and inserts them after el
5656 * @param {String/HTMLElement/Element} el The context element
5657 * @param {Object} o The Dom object spec (and children)
5658 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5659 * @return {HTMLElement/Roo.Element} The new node
5661 insertAfter : function(el, o, returnElement){
5662 return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5666 * Creates new Dom element(s) and inserts them as the first child of el
5667 * @param {String/HTMLElement/Element} el The context element
5668 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5669 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5670 * @return {HTMLElement/Roo.Element} The new node
5672 insertFirst : function(el, o, returnElement){
5673 return this.doInsert(el, o, returnElement, "afterBegin");
5677 doInsert : function(el, o, returnElement, pos, sibling){
5678 el = Roo.getDom(el);
5680 if(this.useDom || o.ns){
5681 newNode = createDom(o, null);
5682 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5684 var html = createHtml(o);
5685 newNode = this.insertHtml(pos, el, html);
5687 return returnElement ? Roo.get(newNode, true) : newNode;
5691 * Creates new Dom element(s) and appends them to el
5692 * @param {String/HTMLElement/Element} el The context element
5693 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5694 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5695 * @return {HTMLElement/Roo.Element} The new node
5697 append : function(el, o, returnElement){
5698 el = Roo.getDom(el);
5700 if(this.useDom || o.ns){
5701 newNode = createDom(o, null);
5702 el.appendChild(newNode);
5704 var html = createHtml(o);
5705 newNode = this.insertHtml("beforeEnd", el, html);
5707 return returnElement ? Roo.get(newNode, true) : newNode;
5711 * Creates new Dom element(s) and overwrites the contents of el with them
5712 * @param {String/HTMLElement/Element} el The context element
5713 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5714 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5715 * @return {HTMLElement/Roo.Element} The new node
5717 overwrite : function(el, o, returnElement)
5719 el = Roo.getDom(el);
5722 while (el.childNodes.length) {
5723 el.removeChild(el.firstChild);
5727 el.innerHTML = createHtml(o);
5730 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5734 * Creates a new Roo.DomHelper.Template from the Dom object spec
5735 * @param {Object} o The Dom object spec (and children)
5736 * @return {Roo.DomHelper.Template} The new template
5738 createTemplate : function(o){
5739 var html = createHtml(o);
5740 return new Roo.Template(html);
5743 * Updates the first element with the spec from the o (replacing if necessary)
5744 * This iterates through the children, and updates attributes / children etc..
5745 * @param {String/HTMLElement/Element} el The context element
5746 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5749 update : function(el, o)
5751 updateNode(Roo.getDom(el), createDom(o));
5760 * Ext JS Library 1.1.1
5761 * Copyright(c) 2006-2007, Ext JS, LLC.
5763 * Originally Released Under LGPL - original licence link has changed is not relivant.
5766 * <script type="text/javascript">
5770 * @class Roo.Template
5771 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5772 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5775 var t = new Roo.Template({
5776 html : '<div name="{id}">' +
5777 '<span class="{cls}">{name:trim} {someval:this.myformat}{value:ellipsis(10)}</span>' +
5779 myformat: function (value, allValues) {
5780 return 'XX' + value;
5783 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5785 * For more information see this blog post with examples:
5786 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5787 - Create Elements using DOM, HTML fragments and Templates</a>.
5789 * @param {Object} cfg - Configuration object.
5791 Roo.Template = function(cfg){
5793 if(cfg instanceof Array){
5795 }else if(arguments.length > 1){
5796 cfg = Array.prototype.join.call(arguments, "");
5800 if (typeof(cfg) == 'object') {
5811 Roo.Template.prototype = {
5814 * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5820 * @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..
5821 * it should be fixed so that template is observable...
5825 * @cfg {String} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5833 * Returns an HTML fragment of this template with the specified values applied.
5834 * @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'})
5835 * @return {String} The HTML fragment
5840 applyTemplate : function(values){
5841 //Roo.log(["applyTemplate", values]);
5845 return this.compiled(values);
5847 var useF = this.disableFormats !== true;
5848 var fm = Roo.util.Format, tpl = this;
5849 var fn = function(m, name, format, args){
5851 if(format.substr(0, 5) == "this."){
5852 return tpl.call(format.substr(5), values[name], values);
5855 // quoted values are required for strings in compiled templates,
5856 // but for non compiled we need to strip them
5857 // quoted reversed for jsmin
5858 var re = /^\s*['"](.*)["']\s*$/;
5859 args = args.split(',');
5860 for(var i = 0, len = args.length; i < len; i++){
5861 args[i] = args[i].replace(re, "$1");
5863 args = [values[name]].concat(args);
5865 args = [values[name]];
5867 return fm[format].apply(fm, args);
5870 return values[name] !== undefined ? values[name] : "";
5873 return this.html.replace(this.re, fn);
5891 this.loading = true;
5892 this.compiled = false;
5894 var cx = new Roo.data.Connection();
5898 success : function (response) {
5902 _t.set(response.responseText,true);
5908 failure : function(response) {
5909 Roo.log("Template failed to load from " + _t.url);
5916 * Sets the HTML used as the template and optionally compiles it.
5917 * @param {String} html
5918 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5919 * @return {Roo.Template} this
5921 set : function(html, compile){
5923 this.compiled = false;
5931 * True to disable format functions (defaults to false)
5934 disableFormats : false,
5937 * The regular expression used to match template variables
5941 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5944 * Compiles the template into an internal function, eliminating the RegEx overhead.
5945 * @return {Roo.Template} this
5947 compile : function(){
5948 var fm = Roo.util.Format;
5949 var useF = this.disableFormats !== true;
5950 var sep = Roo.isGecko ? "+" : ",";
5951 var fn = function(m, name, format, args){
5953 args = args ? ',' + args : "";
5954 if(format.substr(0, 5) != "this."){
5955 format = "fm." + format + '(';
5957 format = 'this.call("'+ format.substr(5) + '", ';
5961 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5963 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5966 // branched to use + in gecko and [].join() in others
5968 body = "this.compiled = function(values){ return '" +
5969 this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5972 body = ["this.compiled = function(values){ return ['"];
5973 body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5974 body.push("'].join('');};");
5975 body = body.join('');
5985 // private function used to call members
5986 call : function(fnName, value, allValues){
5987 return this[fnName](value, allValues);
5991 * Applies the supplied values to the template and inserts the new node(s) as the first child of 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 insertFirst: function(el, values, returnElement){
5998 return this.doInsert('afterBegin', el, values, returnElement);
6002 * Applies the supplied values to the template and inserts the new node(s) before 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 insertBefore: function(el, values, returnElement){
6009 return this.doInsert('beforeBegin', el, values, returnElement);
6013 * Applies the supplied values to the template and inserts the new node(s) after el.
6014 * @param {String/HTMLElement/Roo.Element} el The context element
6015 * @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'})
6016 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6017 * @return {HTMLElement/Roo.Element} The new node or Element
6019 insertAfter : function(el, values, returnElement){
6020 return this.doInsert('afterEnd', el, values, returnElement);
6024 * Applies the supplied values to the template and appends the new node(s) to el.
6025 * @param {String/HTMLElement/Roo.Element} el The context element
6026 * @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'})
6027 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6028 * @return {HTMLElement/Roo.Element} The new node or Element
6030 append : function(el, values, returnElement){
6031 return this.doInsert('beforeEnd', el, values, returnElement);
6034 doInsert : function(where, el, values, returnEl){
6035 el = Roo.getDom(el);
6036 var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6037 return returnEl ? Roo.get(newNode, true) : newNode;
6041 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6042 * @param {String/HTMLElement/Roo.Element} el The context element
6043 * @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'})
6044 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6045 * @return {HTMLElement/Roo.Element} The new node or Element
6047 overwrite : function(el, values, returnElement){
6048 el = Roo.getDom(el);
6049 el.innerHTML = this.applyTemplate(values);
6050 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6054 * Alias for {@link #applyTemplate}
6057 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6060 Roo.DomHelper.Template = Roo.Template;
6063 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6064 * @param {String/HTMLElement} el A DOM element or its id
6065 * @returns {Roo.Template} The created template
6068 Roo.Template.from = function(el){
6069 el = Roo.getDom(el);
6070 return new Roo.Template(el.value || el.innerHTML);
6073 * Ext JS Library 1.1.1
6074 * Copyright(c) 2006-2007, Ext JS, LLC.
6076 * Originally Released Under LGPL - original licence link has changed is not relivant.
6079 * <script type="text/javascript">
6084 * This is code is also distributed under MIT license for use
6085 * with jQuery and prototype JavaScript libraries.
6088 * @class Roo.DomQuery
6089 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).
6091 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>
6094 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.
6096 <h4>Element Selectors:</h4>
6098 <li> <b>*</b> any element</li>
6099 <li> <b>E</b> an element with the tag E</li>
6100 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6101 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6102 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6103 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6105 <h4>Attribute Selectors:</h4>
6106 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6108 <li> <b>E[foo]</b> has an attribute "foo"</li>
6109 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6110 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6111 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6112 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6113 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6114 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6116 <h4>Pseudo Classes:</h4>
6118 <li> <b>E:first-child</b> E is the first child of its parent</li>
6119 <li> <b>E:last-child</b> E is the last child of its parent</li>
6120 <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>
6121 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6122 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6123 <li> <b>E:only-child</b> E is the only child of its parent</li>
6124 <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>
6125 <li> <b>E:first</b> the first E in the resultset</li>
6126 <li> <b>E:last</b> the last E in the resultset</li>
6127 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6128 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6129 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6130 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6131 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6132 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6133 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6134 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6135 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6137 <h4>CSS Value Selectors:</h4>
6139 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6140 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6141 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6142 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6143 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6144 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6148 Roo.DomQuery = function(){
6149 var cache = {}, simpleCache = {}, valueCache = {};
6150 var nonSpace = /\S/;
6151 var trimRe = /^\s+|\s+$/g;
6152 var tplRe = /\{(\d+)\}/g;
6153 var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6154 var tagTokenRe = /^(#)?([\w-\*]+)/;
6155 var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6157 function child(p, index){
6159 var n = p.firstChild;
6161 if(n.nodeType == 1){
6172 while((n = n.nextSibling) && n.nodeType != 1);
6177 while((n = n.previousSibling) && n.nodeType != 1);
6181 function children(d){
6182 var n = d.firstChild, ni = -1;
6184 var nx = n.nextSibling;
6185 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6195 function byClassName(c, a, v){
6199 var r = [], ri = -1, cn;
6200 for(var i = 0, ci; ci = c[i]; i++){
6204 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6205 +' ').indexOf(v) != -1){
6212 function attrValue(n, attr){
6213 if(!n.tagName && typeof n.length != "undefined"){
6222 if(attr == "class" || attr == "className"){
6223 return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6225 return n.getAttribute(attr) || n[attr];
6229 function getNodes(ns, mode, tagName){
6230 var result = [], ri = -1, cs;
6234 tagName = tagName || "*";
6235 if(typeof ns.getElementsByTagName != "undefined"){
6239 for(var i = 0, ni; ni = ns[i]; i++){
6240 cs = ni.getElementsByTagName(tagName);
6241 for(var j = 0, ci; ci = cs[j]; j++){
6245 }else if(mode == "/" || mode == ">"){
6246 var utag = tagName.toUpperCase();
6247 for(var i = 0, ni, cn; ni = ns[i]; i++){
6248 cn = ni.children || ni.childNodes;
6249 for(var j = 0, cj; cj = cn[j]; j++){
6250 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
6255 }else if(mode == "+"){
6256 var utag = tagName.toUpperCase();
6257 for(var i = 0, n; n = ns[i]; i++){
6258 while((n = n.nextSibling) && n.nodeType != 1);
6259 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6263 }else if(mode == "~"){
6264 for(var i = 0, n; n = ns[i]; i++){
6265 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6274 function concat(a, b){
6278 for(var i = 0, l = b.length; i < l; i++){
6284 function byTag(cs, tagName){
6285 if(cs.tagName || cs == document){
6291 var r = [], ri = -1;
6292 tagName = tagName.toLowerCase();
6293 for(var i = 0, ci; ci = cs[i]; i++){
6294 if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6301 function byId(cs, attr, id){
6302 if(cs.tagName || cs == document){
6308 var r = [], ri = -1;
6309 for(var i = 0,ci; ci = cs[i]; i++){
6310 if(ci && ci.id == id){
6318 function byAttribute(cs, attr, value, op, custom){
6319 var r = [], ri = -1, st = custom=="{";
6320 var f = Roo.DomQuery.operators[op];
6321 for(var i = 0, ci; ci = cs[i]; i++){
6324 a = Roo.DomQuery.getStyle(ci, attr);
6326 else if(attr == "class" || attr == "className"){
6327 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6328 }else if(attr == "for"){
6330 }else if(attr == "href"){
6331 a = ci.getAttribute("href", 2);
6333 a = ci.getAttribute(attr);
6335 if((f && f(a, value)) || (!f && a)){
6342 function byPseudo(cs, name, value){
6343 return Roo.DomQuery.pseudos[name](cs, value);
6346 // This is for IE MSXML which does not support expandos.
6347 // IE runs the same speed using setAttribute, however FF slows way down
6348 // and Safari completely fails so they need to continue to use expandos.
6349 var isIE = window.ActiveXObject ? true : false;
6351 // this eval is stop the compressor from
6352 // renaming the variable to something shorter
6354 /** eval:var:batch */
6359 function nodupIEXml(cs){
6361 cs[0].setAttribute("_nodup", d);
6363 for(var i = 1, len = cs.length; i < len; i++){
6365 if(!c.getAttribute("_nodup") != d){
6366 c.setAttribute("_nodup", d);
6370 for(var i = 0, len = cs.length; i < len; i++){
6371 cs[i].removeAttribute("_nodup");
6380 var len = cs.length, c, i, r = cs, cj, ri = -1;
6381 if(!len || typeof cs.nodeType != "undefined" || len == 1){
6384 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6385 return nodupIEXml(cs);
6389 for(i = 1; c = cs[i]; i++){
6394 for(var j = 0; j < i; j++){
6397 for(j = i+1; cj = cs[j]; j++){
6409 function quickDiffIEXml(c1, c2){
6411 for(var i = 0, len = c1.length; i < len; i++){
6412 c1[i].setAttribute("_qdiff", d);
6415 for(var i = 0, len = c2.length; i < len; i++){
6416 if(c2[i].getAttribute("_qdiff") != d){
6417 r[r.length] = c2[i];
6420 for(var i = 0, len = c1.length; i < len; i++){
6421 c1[i].removeAttribute("_qdiff");
6426 function quickDiff(c1, c2){
6427 var len1 = c1.length;
6431 if(isIE && c1[0].selectSingleNode){
6432 return quickDiffIEXml(c1, c2);
6435 for(var i = 0; i < len1; i++){
6439 for(var i = 0, len = c2.length; i < len; i++){
6440 if(c2[i]._qdiff != d){
6441 r[r.length] = c2[i];
6447 function quickId(ns, mode, root, id){
6449 var d = root.ownerDocument || root;
6450 return d.getElementById(id);
6452 ns = getNodes(ns, mode, "*");
6453 return byId(ns, null, id);
6457 getStyle : function(el, name){
6458 return Roo.fly(el).getStyle(name);
6461 * Compiles a selector/xpath query into a reusable function. The returned function
6462 * takes one parameter "root" (optional), which is the context node from where the query should start.
6463 * @param {String} selector The selector/xpath query
6464 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6465 * @return {Function}
6467 compile : function(path, type){
6468 type = type || "select";
6470 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6471 var q = path, mode, lq;
6472 var tk = Roo.DomQuery.matchers;
6473 var tklen = tk.length;
6476 // accept leading mode switch
6477 var lmode = q.match(modeRe);
6478 if(lmode && lmode[1]){
6479 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6480 q = q.replace(lmode[1], "");
6482 // strip leading slashes
6483 while(path.substr(0, 1)=="/"){
6484 path = path.substr(1);
6487 while(q && lq != q){
6489 var tm = q.match(tagTokenRe);
6490 if(type == "select"){
6493 fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6495 fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6497 q = q.replace(tm[0], "");
6498 }else if(q.substr(0, 1) != '@'){
6499 fn[fn.length] = 'n = getNodes(n, mode, "*");';
6504 fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6506 fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6508 q = q.replace(tm[0], "");
6511 while(!(mm = q.match(modeRe))){
6512 var matched = false;
6513 for(var j = 0; j < tklen; j++){
6515 var m = q.match(t.re);
6517 fn[fn.length] = t.select.replace(tplRe, function(x, i){
6520 q = q.replace(m[0], "");
6525 // prevent infinite loop on bad selector
6527 throw 'Error parsing selector, parsing failed at "' + q + '"';
6531 fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6532 q = q.replace(mm[1], "");
6535 fn[fn.length] = "return nodup(n);\n}";
6538 * list of variables that need from compression as they are used by eval.
6548 * eval:var:byClassName
6550 * eval:var:byAttribute
6551 * eval:var:attrValue
6559 * Selects a group of elements.
6560 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6561 * @param {Node} root (optional) The start of the query (defaults to document).
6564 select : function(path, root, type){
6565 if(!root || root == document){
6568 if(typeof root == "string"){
6569 root = document.getElementById(root);
6571 var paths = path.split(",");
6573 for(var i = 0, len = paths.length; i < len; i++){
6574 var p = paths[i].replace(trimRe, "");
6576 cache[p] = Roo.DomQuery.compile(p);
6578 throw p + " is not a valid selector";
6581 var result = cache[p](root);
6582 if(result && result != document){
6583 results = results.concat(result);
6586 if(paths.length > 1){
6587 return nodup(results);
6593 * Selects a single element.
6594 * @param {String} selector The selector/xpath query
6595 * @param {Node} root (optional) The start of the query (defaults to document).
6598 selectNode : function(path, root){
6599 return Roo.DomQuery.select(path, root)[0];
6603 * Selects the value of a node, optionally replacing null with the defaultValue.
6604 * @param {String} selector The selector/xpath query
6605 * @param {Node} root (optional) The start of the query (defaults to document).
6606 * @param {String} defaultValue
6608 selectValue : function(path, root, defaultValue){
6609 path = path.replace(trimRe, "");
6610 if(!valueCache[path]){
6611 valueCache[path] = Roo.DomQuery.compile(path, "select");
6613 var n = valueCache[path](root);
6614 n = n[0] ? n[0] : n;
6615 var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6616 return ((v === null||v === undefined||v==='') ? defaultValue : v);
6620 * Selects the value of a node, parsing integers and floats.
6621 * @param {String} selector The selector/xpath query
6622 * @param {Node} root (optional) The start of the query (defaults to document).
6623 * @param {Number} defaultValue
6626 selectNumber : function(path, root, defaultValue){
6627 var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6628 return parseFloat(v);
6632 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6633 * @param {String/HTMLElement/Array} el An element id, element or array of elements
6634 * @param {String} selector The simple selector to test
6637 is : function(el, ss){
6638 if(typeof el == "string"){
6639 el = document.getElementById(el);
6641 var isArray = (el instanceof Array);
6642 var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6643 return isArray ? (result.length == el.length) : (result.length > 0);
6647 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6648 * @param {Array} el An array of elements to filter
6649 * @param {String} selector The simple selector to test
6650 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6651 * the selector instead of the ones that match
6654 filter : function(els, ss, nonMatches){
6655 ss = ss.replace(trimRe, "");
6656 if(!simpleCache[ss]){
6657 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6659 var result = simpleCache[ss](els);
6660 return nonMatches ? quickDiff(result, els) : result;
6664 * Collection of matching regular expressions and code snippets.
6668 select: 'n = byClassName(n, null, " {1} ");'
6670 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6671 select: 'n = byPseudo(n, "{1}", "{2}");'
6673 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6674 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6677 select: 'n = byId(n, null, "{1}");'
6680 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6685 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6686 * 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, > <.
6689 "=" : function(a, v){
6692 "!=" : function(a, v){
6695 "^=" : function(a, v){
6696 return a && a.substr(0, v.length) == v;
6698 "$=" : function(a, v){
6699 return a && a.substr(a.length-v.length) == v;
6701 "*=" : function(a, v){
6702 return a && a.indexOf(v) !== -1;
6704 "%=" : function(a, v){
6705 return (a % v) == 0;
6707 "|=" : function(a, v){
6708 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6710 "~=" : function(a, v){
6711 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6716 * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6717 * and the argument (if any) supplied in the selector.
6720 "first-child" : function(c){
6721 var r = [], ri = -1, n;
6722 for(var i = 0, ci; ci = n = c[i]; i++){
6723 while((n = n.previousSibling) && n.nodeType != 1);
6731 "last-child" : function(c){
6732 var r = [], ri = -1, n;
6733 for(var i = 0, ci; ci = n = c[i]; i++){
6734 while((n = n.nextSibling) && n.nodeType != 1);
6742 "nth-child" : function(c, a) {
6743 var r = [], ri = -1;
6744 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6745 var f = (m[1] || 1) - 0, l = m[2] - 0;
6746 for(var i = 0, n; n = c[i]; i++){
6747 var pn = n.parentNode;
6748 if (batch != pn._batch) {
6750 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6751 if(cn.nodeType == 1){
6758 if (l == 0 || n.nodeIndex == l){
6761 } else if ((n.nodeIndex + l) % f == 0){
6769 "only-child" : function(c){
6770 var r = [], ri = -1;;
6771 for(var i = 0, ci; ci = c[i]; i++){
6772 if(!prev(ci) && !next(ci)){
6779 "empty" : function(c){
6780 var r = [], ri = -1;
6781 for(var i = 0, ci; ci = c[i]; i++){
6782 var cns = ci.childNodes, j = 0, cn, empty = true;
6785 if(cn.nodeType == 1 || cn.nodeType == 3){
6797 "contains" : function(c, v){
6798 var r = [], ri = -1;
6799 for(var i = 0, ci; ci = c[i]; i++){
6800 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6807 "nodeValue" : function(c, v){
6808 var r = [], ri = -1;
6809 for(var i = 0, ci; ci = c[i]; i++){
6810 if(ci.firstChild && ci.firstChild.nodeValue == v){
6817 "checked" : function(c){
6818 var r = [], ri = -1;
6819 for(var i = 0, ci; ci = c[i]; i++){
6820 if(ci.checked == true){
6827 "not" : function(c, ss){
6828 return Roo.DomQuery.filter(c, ss, true);
6831 "odd" : function(c){
6832 return this["nth-child"](c, "odd");
6835 "even" : function(c){
6836 return this["nth-child"](c, "even");
6839 "nth" : function(c, a){
6840 return c[a-1] || [];
6843 "first" : function(c){
6847 "last" : function(c){
6848 return c[c.length-1] || [];
6851 "has" : function(c, ss){
6852 var s = Roo.DomQuery.select;
6853 var r = [], ri = -1;
6854 for(var i = 0, ci; ci = c[i]; i++){
6855 if(s(ss, ci).length > 0){
6862 "next" : function(c, ss){
6863 var is = Roo.DomQuery.is;
6864 var r = [], ri = -1;
6865 for(var i = 0, ci; ci = c[i]; i++){
6874 "prev" : function(c, ss){
6875 var is = Roo.DomQuery.is;
6876 var r = [], ri = -1;
6877 for(var i = 0, ci; ci = c[i]; i++){
6890 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6891 * @param {String} path The selector/xpath query
6892 * @param {Node} root (optional) The start of the query (defaults to document).
6897 Roo.query = Roo.DomQuery.select;
6900 * Ext JS Library 1.1.1
6901 * Copyright(c) 2006-2007, Ext JS, LLC.
6903 * Originally Released Under LGPL - original licence link has changed is not relivant.
6906 * <script type="text/javascript">
6910 * @class Roo.util.Observable
6911 * Base class that provides a common interface for publishing events. Subclasses are expected to
6912 * to have a property "events" with all the events defined.<br>
6915 Employee = function(name){
6922 Roo.extend(Employee, Roo.util.Observable);
6924 * @param {Object} config properties to use (incuding events / listeners)
6927 Roo.util.Observable = function(cfg){
6930 this.addEvents(cfg.events || {});
6932 delete cfg.events; // make sure
6935 Roo.apply(this, cfg);
6938 this.on(this.listeners);
6939 delete this.listeners;
6942 Roo.util.Observable.prototype = {
6944 * @cfg {Object} listeners list of events and functions to call for this object,
6948 'click' : function(e) {
6958 * Fires the specified event with the passed parameters (minus the event name).
6959 * @param {String} eventName
6960 * @param {Object...} args Variable number of parameters are passed to handlers
6961 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6963 fireEvent : function(){
6964 var ce = this.events[arguments[0].toLowerCase()];
6965 if(typeof ce == "object"){
6966 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6973 filterOptRe : /^(?:scope|delay|buffer|single)$/,
6976 * Appends an event handler to this component
6977 * @param {String} eventName The type of event to listen for
6978 * @param {Function} handler The method the event invokes
6979 * @param {Object} scope (optional) The scope in which to execute the handler
6980 * function. The handler function's "this" context.
6981 * @param {Object} options (optional) An object containing handler configuration
6982 * properties. This may contain any of the following properties:<ul>
6983 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6984 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6985 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6986 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6987 * by the specified number of milliseconds. If the event fires again within that time, the original
6988 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6991 * <b>Combining Options</b><br>
6992 * Using the options argument, it is possible to combine different types of listeners:<br>
6994 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6996 el.on('click', this.onClick, this, {
7003 * <b>Attaching multiple handlers in 1 call</b><br>
7004 * The method also allows for a single argument to be passed which is a config object containing properties
7005 * which specify multiple handlers.
7014 fn: this.onMouseOver,
7018 fn: this.onMouseOut,
7024 * Or a shorthand syntax which passes the same scope object to all handlers:
7027 'click': this.onClick,
7028 'mouseover': this.onMouseOver,
7029 'mouseout': this.onMouseOut,
7034 addListener : function(eventName, fn, scope, o){
7035 if(typeof eventName == "object"){
7038 if(this.filterOptRe.test(e)){
7041 if(typeof o[e] == "function"){
7043 this.addListener(e, o[e], o.scope, o);
7045 // individual options
7046 this.addListener(e, o[e].fn, o[e].scope, o[e]);
7051 o = (!o || typeof o == "boolean") ? {} : o;
7052 eventName = eventName.toLowerCase();
7053 var ce = this.events[eventName] || true;
7054 if(typeof ce == "boolean"){
7055 ce = new Roo.util.Event(this, eventName);
7056 this.events[eventName] = ce;
7058 ce.addListener(fn, scope, o);
7062 * Removes a listener
7063 * @param {String} eventName The type of event to listen for
7064 * @param {Function} handler The handler to remove
7065 * @param {Object} scope (optional) The scope (this object) for the handler
7067 removeListener : function(eventName, fn, scope){
7068 var ce = this.events[eventName.toLowerCase()];
7069 if(typeof ce == "object"){
7070 ce.removeListener(fn, scope);
7075 * Removes all listeners for this object
7077 purgeListeners : function(){
7078 for(var evt in this.events){
7079 if(typeof this.events[evt] == "object"){
7080 this.events[evt].clearListeners();
7085 relayEvents : function(o, events){
7086 var createHandler = function(ename){
7089 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7092 for(var i = 0, len = events.length; i < len; i++){
7093 var ename = events[i];
7094 if(!this.events[ename]){
7095 this.events[ename] = true;
7097 o.on(ename, createHandler(ename), this);
7102 * Used to define events on this Observable
7103 * @param {Object} object The object with the events defined
7105 addEvents : function(o){
7109 Roo.applyIf(this.events, o);
7113 * Checks to see if this object has any listeners for a specified event
7114 * @param {String} eventName The name of the event to check for
7115 * @return {Boolean} True if the event is being listened for, else false
7117 hasListener : function(eventName){
7118 var e = this.events[eventName];
7119 return typeof e == "object" && e.listeners.length > 0;
7123 * Appends an event handler to this element (shorthand for addListener)
7124 * @param {String} eventName The type of event to listen for
7125 * @param {Function} handler The method the event invokes
7126 * @param {Object} scope (optional) The scope in which to execute the handler
7127 * function. The handler function's "this" context.
7128 * @param {Object} options (optional)
7131 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7133 * Removes a listener (shorthand for removeListener)
7134 * @param {String} eventName The type of event to listen for
7135 * @param {Function} handler The handler to remove
7136 * @param {Object} scope (optional) The scope (this object) for the handler
7139 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7142 * Starts capture on the specified Observable. All events will be passed
7143 * to the supplied function with the event name + standard signature of the event
7144 * <b>before</b> the event is fired. If the supplied function returns false,
7145 * the event will not fire.
7146 * @param {Observable} o The Observable to capture
7147 * @param {Function} fn The function to call
7148 * @param {Object} scope (optional) The scope (this object) for the fn
7151 Roo.util.Observable.capture = function(o, fn, scope){
7152 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7156 * Removes <b>all</b> added captures from the Observable.
7157 * @param {Observable} o The Observable to release
7160 Roo.util.Observable.releaseCapture = function(o){
7161 o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7166 var createBuffered = function(h, o, scope){
7167 var task = new Roo.util.DelayedTask();
7169 task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7173 var createSingle = function(h, e, fn, scope){
7175 e.removeListener(fn, scope);
7176 return h.apply(scope, arguments);
7180 var createDelayed = function(h, o, scope){
7182 var args = Array.prototype.slice.call(arguments, 0);
7183 setTimeout(function(){
7184 h.apply(scope, args);
7189 Roo.util.Event = function(obj, name){
7192 this.listeners = [];
7195 Roo.util.Event.prototype = {
7196 addListener : function(fn, scope, options){
7197 var o = options || {};
7198 scope = scope || this.obj;
7199 if(!this.isListening(fn, scope)){
7200 var l = {fn: fn, scope: scope, options: o};
7203 h = createDelayed(h, o, scope);
7206 h = createSingle(h, this, fn, scope);
7209 h = createBuffered(h, o, scope);
7212 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7213 this.listeners.push(l);
7215 this.listeners = this.listeners.slice(0);
7216 this.listeners.push(l);
7221 findListener : function(fn, scope){
7222 scope = scope || this.obj;
7223 var ls = this.listeners;
7224 for(var i = 0, len = ls.length; i < len; i++){
7226 if(l.fn == fn && l.scope == scope){
7233 isListening : function(fn, scope){
7234 return this.findListener(fn, scope) != -1;
7237 removeListener : function(fn, scope){
7239 if((index = this.findListener(fn, scope)) != -1){
7241 this.listeners.splice(index, 1);
7243 this.listeners = this.listeners.slice(0);
7244 this.listeners.splice(index, 1);
7251 clearListeners : function(){
7252 this.listeners = [];
7256 var ls = this.listeners, scope, len = ls.length;
7259 var args = Array.prototype.slice.call(arguments, 0);
7260 for(var i = 0; i < len; i++){
7262 if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7263 this.firing = false;
7267 this.firing = false;
7274 * Copyright(c) 2007-2017, Roo J Solutions Ltd
7281 * @class Roo.Document
7282 * @extends Roo.util.Observable
7283 * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7285 * @param {Object} config the methods and properties of the 'base' class for the application.
7287 * Generic Page handler - implement this to start your app..
7290 * MyProject = new Roo.Document({
7292 'load' : true // your events..
7295 'ready' : function() {
7296 // fired on Roo.onReady()
7301 Roo.Document = function(cfg) {
7306 Roo.util.Observable.call(this,cfg);
7310 Roo.onReady(function() {
7311 _this.fireEvent('ready');
7317 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7319 * Ext JS Library 1.1.1
7320 * Copyright(c) 2006-2007, Ext JS, LLC.
7322 * Originally Released Under LGPL - original licence link has changed is not relivant.
7325 * <script type="text/javascript">
7329 * @class Roo.EventManager
7330 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
7331 * several useful events directly.
7332 * See {@link Roo.EventObject} for more details on normalized event objects.
7335 Roo.EventManager = function(){
7336 var docReadyEvent, docReadyProcId, docReadyState = false;
7337 var resizeEvent, resizeTask, textEvent, textSize;
7338 var E = Roo.lib.Event;
7339 var D = Roo.lib.Dom;
7344 var fireDocReady = function(){
7346 docReadyState = true;
7349 clearInterval(docReadyProcId);
7351 if(Roo.isGecko || Roo.isOpera) {
7352 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7355 var defer = document.getElementById("ie-deferred-loader");
7357 defer.onreadystatechange = null;
7358 defer.parentNode.removeChild(defer);
7362 docReadyEvent.fire();
7363 docReadyEvent.clearListeners();
7368 var initDocReady = function(){
7369 docReadyEvent = new Roo.util.Event();
7370 if(Roo.isGecko || Roo.isOpera) {
7371 document.addEventListener("DOMContentLoaded", fireDocReady, false);
7373 document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7374 var defer = document.getElementById("ie-deferred-loader");
7375 defer.onreadystatechange = function(){
7376 if(this.readyState == "complete"){
7380 }else if(Roo.isSafari){
7381 docReadyProcId = setInterval(function(){
7382 var rs = document.readyState;
7383 if(rs == "complete") {
7388 // no matter what, make sure it fires on load
7389 E.on(window, "load", fireDocReady);
7392 var createBuffered = function(h, o){
7393 var task = new Roo.util.DelayedTask(h);
7395 // create new event object impl so new events don't wipe out properties
7396 e = new Roo.EventObjectImpl(e);
7397 task.delay(o.buffer, h, null, [e]);
7401 var createSingle = function(h, el, ename, fn){
7403 Roo.EventManager.removeListener(el, ename, fn);
7408 var createDelayed = function(h, o){
7410 // create new event object impl so new events don't wipe out properties
7411 e = new Roo.EventObjectImpl(e);
7412 setTimeout(function(){
7417 var transitionEndVal = false;
7419 var transitionEnd = function()
7421 if (transitionEndVal) {
7422 return transitionEndVal;
7424 var el = document.createElement('div');
7426 var transEndEventNames = {
7427 WebkitTransition : 'webkitTransitionEnd',
7428 MozTransition : 'transitionend',
7429 OTransition : 'oTransitionEnd otransitionend',
7430 transition : 'transitionend'
7433 for (var name in transEndEventNames) {
7434 if (el.style[name] !== undefined) {
7435 transitionEndVal = transEndEventNames[name];
7436 return transitionEndVal ;
7443 var listen = function(element, ename, opt, fn, scope)
7445 var o = (!opt || typeof opt == "boolean") ? {} : opt;
7446 fn = fn || o.fn; scope = scope || o.scope;
7447 var el = Roo.getDom(element);
7451 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7454 if (ename == 'transitionend') {
7455 ename = transitionEnd();
7457 var h = function(e){
7458 e = Roo.EventObject.setEvent(e);
7461 t = e.getTarget(o.delegate, el);
7468 if(o.stopEvent === true){
7471 if(o.preventDefault === true){
7474 if(o.stopPropagation === true){
7475 e.stopPropagation();
7478 if(o.normalized === false){
7482 fn.call(scope || el, e, t, o);
7485 h = createDelayed(h, o);
7488 h = createSingle(h, el, ename, fn);
7491 h = createBuffered(h, o);
7494 fn._handlers = fn._handlers || [];
7497 fn._handlers.push([Roo.id(el), ename, h]);
7501 E.on(el, ename, h); // this adds the actuall listener to the object..
7504 if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7505 el.addEventListener("DOMMouseScroll", h, false);
7506 E.on(window, 'unload', function(){
7507 el.removeEventListener("DOMMouseScroll", h, false);
7510 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7511 Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7516 var stopListening = function(el, ename, fn){
7517 var id = Roo.id(el), hds = fn._handlers, hd = fn;
7519 for(var i = 0, len = hds.length; i < len; i++){
7521 if(h[0] == id && h[1] == ename){
7528 E.un(el, ename, hd);
7529 el = Roo.getDom(el);
7530 if(ename == "mousewheel" && el.addEventListener){
7531 el.removeEventListener("DOMMouseScroll", hd, false);
7533 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7534 Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7538 var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7545 * @scope Roo.EventManager
7550 * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7551 * object with a Roo.EventObject
7552 * @param {Function} fn The method the event invokes
7553 * @param {Object} scope An object that becomes the scope of the handler
7554 * @param {boolean} override If true, the obj passed in becomes
7555 * the execution scope of the listener
7556 * @return {Function} The wrapped function
7559 wrap : function(fn, scope, override){
7561 Roo.EventObject.setEvent(e);
7562 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7567 * Appends an event handler to an element (shorthand for addListener)
7568 * @param {String/HTMLElement} element The html element or id to assign the
7569 * @param {String} eventName The type of event to listen for
7570 * @param {Function} handler The method the event invokes
7571 * @param {Object} scope (optional) The scope in which to execute the handler
7572 * function. The handler function's "this" context.
7573 * @param {Object} options (optional) An object containing handler configuration
7574 * properties. This may contain any of the following properties:<ul>
7575 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7576 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7577 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7578 * <li>preventDefault {Boolean} True to prevent the default action</li>
7579 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7580 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7581 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7582 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7583 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7584 * by the specified number of milliseconds. If the event fires again within that time, the original
7585 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7588 * <b>Combining Options</b><br>
7589 * Using the options argument, it is possible to combine different types of listeners:<br>
7591 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7593 el.on('click', this.onClick, this, {
7600 * <b>Attaching multiple handlers in 1 call</b><br>
7601 * The method also allows for a single argument to be passed which is a config object containing properties
7602 * which specify multiple handlers.
7612 fn: this.onMouseOver
7621 * Or a shorthand syntax:<br>
7624 'click' : this.onClick,
7625 'mouseover' : this.onMouseOver,
7626 'mouseout' : this.onMouseOut
7630 addListener : function(element, eventName, fn, scope, options){
7631 if(typeof eventName == "object"){
7637 if(typeof o[e] == "function"){
7639 listen(element, e, o, o[e], o.scope);
7641 // individual options
7642 listen(element, e, o[e]);
7647 return listen(element, eventName, options, fn, scope);
7651 * Removes an event handler
7653 * @param {String/HTMLElement} element The id or html element to remove the
7655 * @param {String} eventName The type of event
7656 * @param {Function} fn
7657 * @return {Boolean} True if a listener was actually removed
7659 removeListener : function(element, eventName, fn){
7660 return stopListening(element, eventName, fn);
7664 * Fires when the document is ready (before onload and before images are loaded). Can be
7665 * accessed shorthanded Roo.onReady().
7666 * @param {Function} fn The method the event invokes
7667 * @param {Object} scope An object that becomes the scope of the handler
7668 * @param {boolean} options
7670 onDocumentReady : function(fn, scope, options){
7671 if(docReadyState){ // if it already fired
7672 docReadyEvent.addListener(fn, scope, options);
7673 docReadyEvent.fire();
7674 docReadyEvent.clearListeners();
7680 docReadyEvent.addListener(fn, scope, options);
7684 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7685 * @param {Function} fn The method the event invokes
7686 * @param {Object} scope An object that becomes the scope of the handler
7687 * @param {boolean} options
7689 onWindowResize : function(fn, scope, options)
7692 resizeEvent = new Roo.util.Event();
7693 resizeTask = new Roo.util.DelayedTask(function(){
7694 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7696 E.on(window, "resize", function()
7699 resizeTask.delay(50);
7701 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7705 resizeEvent.addListener(fn, scope, options);
7709 * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7710 * @param {Function} fn The method the event invokes
7711 * @param {Object} scope An object that becomes the scope of the handler
7712 * @param {boolean} options
7714 onTextResize : function(fn, scope, options){
7716 textEvent = new Roo.util.Event();
7717 var textEl = new Roo.Element(document.createElement('div'));
7718 textEl.dom.className = 'x-text-resize';
7719 textEl.dom.innerHTML = 'X';
7720 textEl.appendTo(document.body);
7721 textSize = textEl.dom.offsetHeight;
7722 setInterval(function(){
7723 if(textEl.dom.offsetHeight != textSize){
7724 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7726 }, this.textResizeInterval);
7728 textEvent.addListener(fn, scope, options);
7732 * Removes the passed window resize listener.
7733 * @param {Function} fn The method the event invokes
7734 * @param {Object} scope The scope of handler
7736 removeResizeListener : function(fn, scope){
7738 resizeEvent.removeListener(fn, scope);
7743 fireResize : function(){
7745 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7749 * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7753 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7755 textResizeInterval : 50
7760 * @scopeAlias pub=Roo.EventManager
7764 * Appends an event handler to an element (shorthand for addListener)
7765 * @param {String/HTMLElement} element The html element or id to assign the
7766 * @param {String} eventName The type of event to listen for
7767 * @param {Function} handler The method the event invokes
7768 * @param {Object} scope (optional) The scope in which to execute the handler
7769 * function. The handler function's "this" context.
7770 * @param {Object} options (optional) An object containing handler configuration
7771 * properties. This may contain any of the following properties:<ul>
7772 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7773 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7774 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7775 * <li>preventDefault {Boolean} True to prevent the default action</li>
7776 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7777 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7778 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7779 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7780 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7781 * by the specified number of milliseconds. If the event fires again within that time, the original
7782 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7785 * <b>Combining Options</b><br>
7786 * Using the options argument, it is possible to combine different types of listeners:<br>
7788 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7790 el.on('click', this.onClick, this, {
7797 * <b>Attaching multiple handlers in 1 call</b><br>
7798 * The method also allows for a single argument to be passed which is a config object containing properties
7799 * which specify multiple handlers.
7809 fn: this.onMouseOver
7818 * Or a shorthand syntax:<br>
7821 'click' : this.onClick,
7822 'mouseover' : this.onMouseOver,
7823 'mouseout' : this.onMouseOut
7827 pub.on = pub.addListener;
7828 pub.un = pub.removeListener;
7830 pub.stoppedMouseDownEvent = new Roo.util.Event();
7834 * Fires when the document is ready (before onload and before images are loaded). Shorthand of {@link Roo.EventManager#onDocumentReady}.
7835 * @param {Function} fn The method the event invokes
7836 * @param {Object} scope An object that becomes the scope of the handler
7837 * @param {boolean} override If true, the obj passed in becomes
7838 * the execution scope of the listener
7842 Roo.onReady = Roo.EventManager.onDocumentReady;
7844 Roo.onReady(function(){
7845 var bd = Roo.get(document.body);
7850 : Roo.isIE11 ? "roo-ie11"
7851 : Roo.isEdge ? "roo-edge"
7852 : Roo.isGecko ? "roo-gecko"
7853 : Roo.isOpera ? "roo-opera"
7854 : Roo.isSafari ? "roo-safari" : ""];
7857 cls.push("roo-mac");
7860 cls.push("roo-linux");
7863 cls.push("roo-ios");
7866 cls.push("roo-touch");
7868 if(Roo.isBorderBox){
7869 cls.push('roo-border-box');
7871 if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7872 var p = bd.dom.parentNode;
7874 p.className += ' roo-strict';
7877 bd.addClass(cls.join(' '));
7881 * @class Roo.EventObject
7882 * EventObject exposes the Yahoo! UI Event functionality directly on the object
7883 * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code
7886 function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7888 var target = e.getTarget();
7891 var myDiv = Roo.get("myDiv");
7892 myDiv.on("click", handleClick);
7894 Roo.EventManager.on("myDiv", 'click', handleClick);
7895 Roo.EventManager.addListener("myDiv", 'click', handleClick);
7899 Roo.EventObject = function(){
7901 var E = Roo.lib.Event;
7903 // safari keypress events for special keys return bad keycodes
7906 63235 : 39, // right
7909 63276 : 33, // page up
7910 63277 : 34, // page down
7911 63272 : 46, // delete
7916 // normalize button clicks
7917 var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7918 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7920 Roo.EventObjectImpl = function(e){
7922 this.setEvent(e.browserEvent || e);
7925 Roo.EventObjectImpl.prototype = {
7927 * Used to fix doc tools.
7928 * @scope Roo.EventObject.prototype
7934 /** The normal browser event */
7935 browserEvent : null,
7936 /** The button pressed in a mouse event */
7938 /** True if the shift key was down during the event */
7940 /** True if the control key was down during the event */
7942 /** True if the alt key was down during the event */
8001 setEvent : function(e){
8002 if(e == this || (e && e.browserEvent)){ // already wrapped
8005 this.browserEvent = e;
8007 // normalize buttons
8008 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8009 if(e.type == 'click' && this.button == -1){
8013 this.shiftKey = e.shiftKey;
8014 // mac metaKey behaves like ctrlKey
8015 this.ctrlKey = e.ctrlKey || e.metaKey;
8016 this.altKey = e.altKey;
8017 // in getKey these will be normalized for the mac
8018 this.keyCode = e.keyCode;
8019 // keyup warnings on firefox.
8020 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8021 // cache the target for the delayed and or buffered events
8022 this.target = E.getTarget(e);
8024 this.xy = E.getXY(e);
8027 this.shiftKey = false;
8028 this.ctrlKey = false;
8029 this.altKey = false;
8039 * Stop the event (preventDefault and stopPropagation)
8041 stopEvent : function(){
8042 if(this.browserEvent){
8043 if(this.browserEvent.type == 'mousedown'){
8044 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8046 E.stopEvent(this.browserEvent);
8051 * Prevents the browsers default handling of the event.
8053 preventDefault : function(){
8054 if(this.browserEvent){
8055 E.preventDefault(this.browserEvent);
8060 isNavKeyPress : function(){
8061 var k = this.keyCode;
8062 k = Roo.isSafari ? (safariKeys[k] || k) : k;
8063 return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8066 isSpecialKey : function(){
8067 var k = this.keyCode;
8068 return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13 || k == 40 || k == 27 ||
8069 (k == 16) || (k == 17) ||
8070 (k >= 18 && k <= 20) ||
8071 (k >= 33 && k <= 35) ||
8072 (k >= 36 && k <= 39) ||
8073 (k >= 44 && k <= 45);
8076 * Cancels bubbling of the event.
8078 stopPropagation : function(){
8079 if(this.browserEvent){
8080 if(this.type == 'mousedown'){
8081 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8083 E.stopPropagation(this.browserEvent);
8088 * Gets the key code for the event.
8091 getCharCode : function(){
8092 return this.charCode || this.keyCode;
8096 * Returns a normalized keyCode for the event.
8097 * @return {Number} The key code
8099 getKey : function(){
8100 var k = this.keyCode || this.charCode;
8101 return Roo.isSafari ? (safariKeys[k] || k) : k;
8105 * Gets the x coordinate of the event.
8108 getPageX : function(){
8113 * Gets the y coordinate of the event.
8116 getPageY : function(){
8121 * Gets the time of the event.
8124 getTime : function(){
8125 if(this.browserEvent){
8126 return E.getTime(this.browserEvent);
8132 * Gets the page coordinates of the event.
8133 * @return {Array} The xy values like [x, y]
8140 * Gets the target for the event.
8141 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8142 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8143 search as a number or element (defaults to 10 || document.body)
8144 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8145 * @return {HTMLelement}
8147 getTarget : function(selector, maxDepth, returnEl){
8148 return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8151 * Gets the related target.
8152 * @return {HTMLElement}
8154 getRelatedTarget : function(){
8155 if(this.browserEvent){
8156 return E.getRelatedTarget(this.browserEvent);
8162 * Normalizes mouse wheel delta across browsers
8163 * @return {Number} The delta
8165 getWheelDelta : function(){
8166 var e = this.browserEvent;
8168 if(e.wheelDelta){ /* IE/Opera. */
8169 delta = e.wheelDelta/120;
8170 }else if(e.detail){ /* Mozilla case. */
8171 delta = -e.detail/3;
8177 * Returns true if the control, meta, shift or alt key was pressed during this event.
8180 hasModifier : function(){
8181 return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8185 * Returns true if the target of this event equals el or is a child of el
8186 * @param {String/HTMLElement/Element} el
8187 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8190 within : function(el, related){
8191 var t = this[related ? "getRelatedTarget" : "getTarget"]();
8192 return t && Roo.fly(el).contains(t);
8195 getPoint : function(){
8196 return new Roo.lib.Point(this.xy[0], this.xy[1]);
8200 return new Roo.EventObjectImpl();
8205 * Ext JS Library 1.1.1
8206 * Copyright(c) 2006-2007, Ext JS, LLC.
8208 * Originally Released Under LGPL - original licence link has changed is not relivant.
8211 * <script type="text/javascript">
8215 // was in Composite Element!??!?!
8218 var D = Roo.lib.Dom;
8219 var E = Roo.lib.Event;
8220 var A = Roo.lib.Anim;
8222 // local style camelizing for speed
8224 var camelRe = /(-[a-z])/gi;
8225 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8226 var view = document.defaultView;
8229 * @class Roo.Element
8230 * Represents an Element in the DOM.<br><br>
8233 var el = Roo.get("my-div");
8236 var el = getEl("my-div");
8238 // or with a DOM element
8239 var el = Roo.get(myDivElement);
8241 * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8242 * each call instead of constructing a new one.<br><br>
8243 * <b>Animations</b><br />
8244 * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8245 * should either be a boolean (true) or an object literal with animation options. The animation options are:
8247 Option Default Description
8248 --------- -------- ---------------------------------------------
8249 duration .35 The duration of the animation in seconds
8250 easing easeOut The YUI easing method
8251 callback none A function to execute when the anim completes
8252 scope this The scope (this) of the callback function
8254 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8255 * manipulate the animation. Here's an example:
8257 var el = Roo.get("my-div");
8262 // default animation
8263 el.setWidth(100, true);
8265 // animation with some options set
8272 // using the "anim" property to get the Anim object
8278 el.setWidth(100, opt);
8280 if(opt.anim.isAnimated()){
8284 * <b> Composite (Collections of) Elements</b><br />
8285 * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8286 * @constructor Create a new Element directly.
8287 * @param {String/HTMLElement} element
8288 * @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).
8290 Roo.Element = function(element, forceNew)
8292 var dom = typeof element == "string" ?
8293 document.getElementById(element) : element;
8295 this.listeners = {};
8297 if(!dom){ // invalid id/element
8301 if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8302 return Roo.Element.cache[id];
8312 * The DOM element ID
8315 this.id = id || Roo.id(dom);
8317 return this; // assumed for cctor?
8320 var El = Roo.Element;
8324 * The element's default display mode (defaults to "")
8327 originalDisplay : "",
8330 // note this is overridden in BS version..
8333 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8339 * Sets the element's visibility mode. When setVisible() is called it
8340 * will use this to determine whether to set the visibility or the display property.
8341 * @param visMode Element.VISIBILITY or Element.DISPLAY
8342 * @return {Roo.Element} this
8344 setVisibilityMode : function(visMode){
8345 this.visibilityMode = visMode;
8349 * Convenience method for setVisibilityMode(Element.DISPLAY)
8350 * @param {String} display (optional) What to set display to when visible
8351 * @return {Roo.Element} this
8353 enableDisplayMode : function(display){
8354 this.setVisibilityMode(El.DISPLAY);
8355 if(typeof display != "undefined") { this.originalDisplay = display; }
8360 * 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)
8361 * @param {String} selector The simple selector to test
8362 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8363 search as a number or element (defaults to 10 || document.body)
8364 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8365 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8367 findParent : function(simpleSelector, maxDepth, returnEl){
8368 var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8369 maxDepth = maxDepth || 50;
8370 if(typeof maxDepth != "number"){
8371 stopEl = Roo.getDom(maxDepth);
8374 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8375 if(dq.is(p, simpleSelector)){
8376 return returnEl ? Roo.get(p) : p;
8386 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8387 * @param {String} selector The simple selector to test
8388 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8389 search as a number or element (defaults to 10 || document.body)
8390 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8391 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8393 findParentNode : function(simpleSelector, maxDepth, returnEl){
8394 var p = Roo.fly(this.dom.parentNode, '_internal');
8395 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8399 * Looks at the scrollable parent element
8401 findScrollableParent : function()
8403 var overflowRegex = /(auto|scroll)/;
8405 if(this.getStyle('position') === 'fixed'){
8406 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8409 var excludeStaticParent = this.getStyle('position') === "absolute";
8411 for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8413 if (excludeStaticParent && parent.getStyle('position') === "static") {
8417 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8421 if(parent.dom.nodeName.toLowerCase() == 'body'){
8422 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8426 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8430 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8431 * This is a shortcut for findParentNode() that always returns an Roo.Element.
8432 * @param {String} selector The simple selector to test
8433 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8434 search as a number or element (defaults to 10 || document.body)
8435 * @return {Roo.Element} The matching DOM node (or null if no match was found)
8437 up : function(simpleSelector, maxDepth){
8438 return this.findParentNode(simpleSelector, maxDepth, true);
8444 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8445 * @param {String} selector The simple selector to test
8446 * @return {Boolean} True if this element matches the selector, else false
8448 is : function(simpleSelector){
8449 return Roo.DomQuery.is(this.dom, simpleSelector);
8453 * Perform animation on this element.
8454 * @param {Object} args The YUI animation control args
8455 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8456 * @param {Function} onComplete (optional) Function to call when animation completes
8457 * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8458 * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8459 * @return {Roo.Element} this
8461 animate : function(args, duration, onComplete, easing, animType){
8462 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8467 * @private Internal animation call
8469 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8470 animType = animType || 'run';
8472 var anim = Roo.lib.Anim[animType](
8474 (opt.duration || defaultDur) || .35,
8475 (opt.easing || defaultEase) || 'easeOut',
8477 Roo.callback(cb, this);
8478 Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8486 // private legacy anim prep
8487 preanim : function(a, i){
8488 return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8492 * Removes worthless text nodes
8493 * @param {Boolean} forceReclean (optional) By default the element
8494 * keeps track if it has been cleaned already so
8495 * you can call this over and over. However, if you update the element and
8496 * need to force a reclean, you can pass true.
8498 clean : function(forceReclean){
8499 if(this.isCleaned && forceReclean !== true){
8503 var d = this.dom, n = d.firstChild, ni = -1;
8505 var nx = n.nextSibling;
8506 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8513 this.isCleaned = true;
8518 calcOffsetsTo : function(el){
8521 var restorePos = false;
8522 if(el.getStyle('position') == 'static'){
8523 el.position('relative');
8528 while(op && op != d && op.tagName != 'HTML'){
8531 op = op.offsetParent;
8534 el.position('static');
8540 * Scrolls this element into view within the passed container.
8541 * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8542 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8543 * @return {Roo.Element} this
8545 scrollIntoView : function(container, hscroll){
8546 var c = Roo.getDom(container) || document.body;
8549 var o = this.calcOffsetsTo(c),
8552 b = t+el.offsetHeight,
8553 r = l+el.offsetWidth;
8555 var ch = c.clientHeight;
8556 var ct = parseInt(c.scrollTop, 10);
8557 var cl = parseInt(c.scrollLeft, 10);
8559 var cr = cl + c.clientWidth;
8567 if(hscroll !== false){
8571 c.scrollLeft = r-c.clientWidth;
8578 scrollChildIntoView : function(child, hscroll){
8579 Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8583 * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8584 * the new height may not be available immediately.
8585 * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8586 * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8587 * @param {Function} onComplete (optional) Function to call when animation completes
8588 * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8589 * @return {Roo.Element} this
8591 autoHeight : function(animate, duration, onComplete, easing){
8592 var oldHeight = this.getHeight();
8594 this.setHeight(1); // force clipping
8595 setTimeout(function(){
8596 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8598 this.setHeight(height);
8600 if(typeof onComplete == "function"){
8604 this.setHeight(oldHeight); // restore original height
8605 this.setHeight(height, animate, duration, function(){
8607 if(typeof onComplete == "function") { onComplete(); }
8608 }.createDelegate(this), easing);
8610 }.createDelegate(this), 0);
8615 * Returns true if this element is an ancestor of the passed element
8616 * @param {HTMLElement/String} el The element to check
8617 * @return {Boolean} True if this element is an ancestor of el, else false
8619 contains : function(el){
8620 if(!el){return false;}
8621 return D.isAncestor(this.dom, el.dom ? el.dom : el);
8625 * Checks whether the element is currently visible using both visibility and display properties.
8626 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8627 * @return {Boolean} True if the element is currently visible, else false
8629 isVisible : function(deep) {
8630 var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8631 if(deep !== true || !vis){
8634 var p = this.dom.parentNode;
8635 while(p && p.tagName.toLowerCase() != "body"){
8636 if(!Roo.fly(p, '_isVisible').isVisible()){
8645 * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8646 * @param {String} selector The CSS selector
8647 * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8648 * @return {CompositeElement/CompositeElementLite} The composite element
8650 select : function(selector, unique){
8651 return El.select(selector, unique, this.dom);
8655 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8656 * @param {String} selector The CSS selector
8657 * @return {Array} An array of the matched nodes
8659 query : function(selector, unique){
8660 return Roo.DomQuery.select(selector, this.dom);
8664 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8665 * @param {String} selector The CSS selector
8666 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8667 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8669 child : function(selector, returnDom){
8670 var n = Roo.DomQuery.selectNode(selector, this.dom);
8671 return returnDom ? n : Roo.get(n);
8675 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8676 * @param {String} selector The CSS selector
8677 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8678 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8680 down : function(selector, returnDom){
8681 var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8682 return returnDom ? n : Roo.get(n);
8686 * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8687 * @param {String} group The group the DD object is member of
8688 * @param {Object} config The DD config object
8689 * @param {Object} overrides An object containing methods to override/implement on the DD object
8690 * @return {Roo.dd.DD} The DD object
8692 initDD : function(group, config, overrides){
8693 var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8694 return Roo.apply(dd, overrides);
8698 * Initializes a {@link Roo.dd.DDProxy} object for this element.
8699 * @param {String} group The group the DDProxy object is member of
8700 * @param {Object} config The DDProxy config object
8701 * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8702 * @return {Roo.dd.DDProxy} The DDProxy object
8704 initDDProxy : function(group, config, overrides){
8705 var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8706 return Roo.apply(dd, overrides);
8710 * Initializes a {@link Roo.dd.DDTarget} object for this element.
8711 * @param {String} group The group the DDTarget object is member of
8712 * @param {Object} config The DDTarget config object
8713 * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8714 * @return {Roo.dd.DDTarget} The DDTarget object
8716 initDDTarget : function(group, config, overrides){
8717 var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8718 return Roo.apply(dd, overrides);
8722 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8723 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8724 * @param {Boolean} visible Whether the element is visible
8725 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8726 * @return {Roo.Element} this
8728 setVisible : function(visible, animate){
8730 if(this.visibilityMode == El.DISPLAY){
8731 this.setDisplayed(visible);
8734 this.dom.style.visibility = visible ? "visible" : "hidden";
8737 // closure for composites
8739 var visMode = this.visibilityMode;
8741 this.setOpacity(.01);
8742 this.setVisible(true);
8744 this.anim({opacity: { to: (visible?1:0) }},
8745 this.preanim(arguments, 1),
8746 null, .35, 'easeIn', function(){
8748 if(visMode == El.DISPLAY){
8749 dom.style.display = "none";
8751 dom.style.visibility = "hidden";
8753 Roo.get(dom).setOpacity(1);
8761 * Returns true if display is not "none"
8764 isDisplayed : function() {
8765 return this.getStyle("display") != "none";
8769 * Toggles the element's visibility or display, depending on visibility mode.
8770 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8771 * @return {Roo.Element} this
8773 toggle : function(animate){
8774 this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8779 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8780 * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8781 * @return {Roo.Element} this
8783 setDisplayed : function(value) {
8784 if(typeof value == "boolean"){
8785 value = value ? this.originalDisplay : "none";
8787 this.setStyle("display", value);
8792 * Tries to focus the element. Any exceptions are caught and ignored.
8793 * @return {Roo.Element} this
8795 focus : function() {
8803 * Tries to blur the element. Any exceptions are caught and ignored.
8804 * @return {Roo.Element} this
8814 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8815 * @param {String/Array} className The CSS class to add, or an array of classes
8816 * @return {Roo.Element} this
8818 addClass : function(className){
8819 if(className instanceof Array){
8820 for(var i = 0, len = className.length; i < len; i++) {
8821 this.addClass(className[i]);
8824 if(className && !this.hasClass(className)){
8825 if (this.dom instanceof SVGElement) {
8826 this.dom.className.baseVal =this.dom.className.baseVal + " " + className;
8828 this.dom.className = this.dom.className + " " + className;
8836 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8837 * @param {String/Array} className The CSS class to add, or an array of classes
8838 * @return {Roo.Element} this
8840 radioClass : function(className){
8841 var siblings = this.dom.parentNode.childNodes;
8842 for(var i = 0; i < siblings.length; i++) {
8843 var s = siblings[i];
8844 if(s.nodeType == 1){
8845 Roo.get(s).removeClass(className);
8848 this.addClass(className);
8853 * Removes one or more CSS classes from the element.
8854 * @param {String/Array} className The CSS class to remove, or an array of classes
8855 * @return {Roo.Element} this
8857 removeClass : function(className){
8859 var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8860 if(!className || !cn){
8863 if(className instanceof Array){
8864 for(var i = 0, len = className.length; i < len; i++) {
8865 this.removeClass(className[i]);
8868 if(this.hasClass(className)){
8869 var re = this.classReCache[className];
8871 re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8872 this.classReCache[className] = re;
8874 if (this.dom instanceof SVGElement) {
8875 this.dom.className.baseVal = cn.replace(re, " ");
8877 this.dom.className = cn.replace(re, " ");
8888 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8889 * @param {String} className The CSS class to toggle
8890 * @return {Roo.Element} this
8892 toggleClass : function(className){
8893 if(this.hasClass(className)){
8894 this.removeClass(className);
8896 this.addClass(className);
8902 * Checks if the specified CSS class exists on this element's DOM node.
8903 * @param {String} className The CSS class to check for
8904 * @return {Boolean} True if the class exists, else false
8906 hasClass : function(className){
8907 if (this.dom instanceof SVGElement) {
8908 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1;
8910 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8914 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
8915 * @param {String} oldClassName The CSS class to replace
8916 * @param {String} newClassName The replacement CSS class
8917 * @return {Roo.Element} this
8919 replaceClass : function(oldClassName, newClassName){
8920 this.removeClass(oldClassName);
8921 this.addClass(newClassName);
8926 * Returns an object with properties matching the styles requested.
8927 * For example, el.getStyles('color', 'font-size', 'width') might return
8928 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8929 * @param {String} style1 A style name
8930 * @param {String} style2 A style name
8931 * @param {String} etc.
8932 * @return {Object} The style object
8934 getStyles : function(){
8935 var a = arguments, len = a.length, r = {};
8936 for(var i = 0; i < len; i++){
8937 r[a[i]] = this.getStyle(a[i]);
8943 * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8944 * @param {String} property The style property whose value is returned.
8945 * @return {String} The current value of the style property for this element.
8947 getStyle : function(){
8948 return view && view.getComputedStyle ?
8950 var el = this.dom, v, cs, camel;
8951 if(prop == 'float'){
8954 if(el.style && (v = el.style[prop])){
8957 if(cs = view.getComputedStyle(el, "")){
8958 if(!(camel = propCache[prop])){
8959 camel = propCache[prop] = prop.replace(camelRe, camelFn);
8966 var el = this.dom, v, cs, camel;
8967 if(prop == 'opacity'){
8968 if(typeof el.style.filter == 'string'){
8969 var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8971 var fv = parseFloat(m[1]);
8973 return fv ? fv / 100 : 0;
8978 }else if(prop == 'float'){
8979 prop = "styleFloat";
8981 if(!(camel = propCache[prop])){
8982 camel = propCache[prop] = prop.replace(camelRe, camelFn);
8984 if(v = el.style[camel]){
8987 if(cs = el.currentStyle){
8995 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8996 * @param {String/Object} property The style property to be set, or an object of multiple styles.
8997 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8998 * @return {Roo.Element} this
9000 setStyle : function(prop, value){
9001 if(typeof prop == "string"){
9003 if (prop == 'float') {
9004 this.setStyle(Roo.isIE ? 'styleFloat' : 'cssFloat', value);
9009 if(!(camel = propCache[prop])){
9010 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9013 if(camel == 'opacity') {
9014 this.setOpacity(value);
9016 this.dom.style[camel] = value;
9019 for(var style in prop){
9020 if(typeof prop[style] != "function"){
9021 this.setStyle(style, prop[style]);
9029 * More flexible version of {@link #setStyle} for setting style properties.
9030 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9031 * a function which returns such a specification.
9032 * @return {Roo.Element} this
9034 applyStyles : function(style){
9035 Roo.DomHelper.applyStyles(this.dom, style);
9040 * 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).
9041 * @return {Number} The X position of the element
9044 return D.getX(this.dom);
9048 * 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).
9049 * @return {Number} The Y position of the element
9052 return D.getY(this.dom);
9056 * 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).
9057 * @return {Array} The XY position of the element
9060 return D.getXY(this.dom);
9064 * 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).
9065 * @param {Number} The X position of the element
9066 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9067 * @return {Roo.Element} this
9069 setX : function(x, animate){
9071 D.setX(this.dom, x);
9073 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9079 * 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).
9080 * @param {Number} The Y position of the element
9081 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9082 * @return {Roo.Element} this
9084 setY : function(y, animate){
9086 D.setY(this.dom, y);
9088 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9094 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9095 * @param {String} left The left CSS property value
9096 * @return {Roo.Element} this
9098 setLeft : function(left){
9099 this.setStyle("left", this.addUnits(left));
9104 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9105 * @param {String} top The top CSS property value
9106 * @return {Roo.Element} this
9108 setTop : function(top){
9109 this.setStyle("top", this.addUnits(top));
9114 * Sets the element's CSS right style.
9115 * @param {String} right The right CSS property value
9116 * @return {Roo.Element} this
9118 setRight : function(right){
9119 this.setStyle("right", this.addUnits(right));
9124 * Sets the element's CSS bottom style.
9125 * @param {String} bottom The bottom CSS property value
9126 * @return {Roo.Element} this
9128 setBottom : function(bottom){
9129 this.setStyle("bottom", this.addUnits(bottom));
9134 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9135 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9136 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9137 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9138 * @return {Roo.Element} this
9140 setXY : function(pos, animate){
9142 D.setXY(this.dom, pos);
9144 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9150 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9151 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9152 * @param {Number} x X value for new position (coordinates are page-based)
9153 * @param {Number} y Y value for new position (coordinates are page-based)
9154 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9155 * @return {Roo.Element} this
9157 setLocation : function(x, y, animate){
9158 this.setXY([x, y], this.preanim(arguments, 2));
9163 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9164 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9165 * @param {Number} x X value for new position (coordinates are page-based)
9166 * @param {Number} y Y value for new position (coordinates are page-based)
9167 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9168 * @return {Roo.Element} this
9170 moveTo : function(x, y, animate){
9171 this.setXY([x, y], this.preanim(arguments, 2));
9176 * Returns the region of the given element.
9177 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9178 * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9180 getRegion : function(){
9181 return D.getRegion(this.dom);
9185 * Returns the offset height of the element
9186 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9187 * @return {Number} The element's height
9189 getHeight : function(contentHeight){
9190 var h = this.dom.offsetHeight || 0;
9191 return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9195 * Returns the offset width of the element
9196 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9197 * @return {Number} The element's width
9199 getWidth : function(contentWidth){
9200 var w = this.dom.offsetWidth || 0;
9201 return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9205 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9206 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9207 * if a height has not been set using CSS.
9210 getComputedHeight : function(){
9211 var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9213 h = parseInt(this.getStyle('height'), 10) || 0;
9214 if(!this.isBorderBox()){
9215 h += this.getFrameWidth('tb');
9222 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9223 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9224 * if a width has not been set using CSS.
9227 getComputedWidth : function(){
9228 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9230 w = parseInt(this.getStyle('width'), 10) || 0;
9231 if(!this.isBorderBox()){
9232 w += this.getFrameWidth('lr');
9239 * Returns the size of the element.
9240 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9241 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9243 getSize : function(contentSize){
9244 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9248 * Returns the width and height of the viewport.
9249 * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9251 getViewSize : function(){
9252 var d = this.dom, doc = document, aw = 0, ah = 0;
9253 if(d == doc || d == doc.body){
9254 return {width : D.getViewWidth(), height: D.getViewHeight()};
9257 width : d.clientWidth,
9258 height: d.clientHeight
9264 * Returns the value of the "value" attribute
9265 * @param {Boolean} asNumber true to parse the value as a number
9266 * @return {String/Number}
9268 getValue : function(asNumber){
9269 return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9273 adjustWidth : function(width){
9274 if(typeof width == "number"){
9275 if(this.autoBoxAdjust && !this.isBorderBox()){
9276 width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9286 adjustHeight : function(height){
9287 if(typeof height == "number"){
9288 if(this.autoBoxAdjust && !this.isBorderBox()){
9289 height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9299 * Set the width of the element
9300 * @param {Number} width The new width
9301 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9302 * @return {Roo.Element} this
9304 setWidth : function(width, animate){
9305 width = this.adjustWidth(width);
9307 this.dom.style.width = this.addUnits(width);
9309 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9315 * Set the height of the element
9316 * @param {Number} height The new height
9317 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9318 * @return {Roo.Element} this
9320 setHeight : function(height, animate){
9321 height = this.adjustHeight(height);
9323 this.dom.style.height = this.addUnits(height);
9325 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9331 * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9332 * @param {Number} width The new width
9333 * @param {Number} height The new height
9334 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9335 * @return {Roo.Element} this
9337 setSize : function(width, height, animate){
9338 if(typeof width == "object"){ // in case of object from getSize()
9339 height = width.height; width = width.width;
9341 width = this.adjustWidth(width); height = this.adjustHeight(height);
9343 this.dom.style.width = this.addUnits(width);
9344 this.dom.style.height = this.addUnits(height);
9346 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9352 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9353 * @param {Number} x X value for new position (coordinates are page-based)
9354 * @param {Number} y Y value for new position (coordinates are page-based)
9355 * @param {Number} width The new width
9356 * @param {Number} height The new height
9357 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9358 * @return {Roo.Element} this
9360 setBounds : function(x, y, width, height, animate){
9362 this.setSize(width, height);
9363 this.setLocation(x, y);
9365 width = this.adjustWidth(width); height = this.adjustHeight(height);
9366 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9367 this.preanim(arguments, 4), 'motion');
9373 * 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.
9374 * @param {Roo.lib.Region} region The region to fill
9375 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9376 * @return {Roo.Element} this
9378 setRegion : function(region, animate){
9379 this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9384 * Appends an event handler
9386 * @param {String} eventName The type of event to append
9387 * @param {Function} fn The method the event invokes
9388 * @param {Object} scope (optional) The scope (this object) of the fn
9389 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
9391 addListener : function(eventName, fn, scope, options)
9393 if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9394 this.addListener('touchstart', this.onTapHandler, this);
9397 // we need to handle a special case where dom element is a svg element.
9398 // in this case we do not actua
9403 if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9404 if (typeof(this.listeners[eventName]) == 'undefined') {
9405 this.listeners[eventName] = new Roo.util.Event(this, eventName);
9407 this.listeners[eventName].addListener(fn, scope, options);
9412 Roo.EventManager.on(this.dom, eventName, fn, scope || this, options);
9417 onTapHandler : function(event)
9419 if(!this.tapedTwice) {
9420 this.tapedTwice = true;
9422 setTimeout( function() {
9423 s.tapedTwice = false;
9427 event.preventDefault();
9428 var revent = new MouseEvent('dblclick', {
9434 this.dom.dispatchEvent(revent);
9435 //action on double tap goes below
9440 * Removes an event handler from this element
9441 * @param {String} eventName the type of event to remove
9442 * @param {Function} fn the method the event invokes
9443 * @param {Function} scope (needed for svg fake listeners)
9444 * @return {Roo.Element} this
9446 removeListener : function(eventName, fn, scope){
9447 Roo.EventManager.removeListener(this.dom, eventName, fn);
9448 if (typeof(this.listeners) == 'undefined' || typeof(this.listeners[eventName]) == 'undefined') {
9451 this.listeners[eventName].removeListener(fn, scope);
9456 * Removes all previous added listeners from this element
9457 * @return {Roo.Element} this
9459 removeAllListeners : function(){
9460 E.purgeElement(this.dom);
9461 this.listeners = {};
9465 relayEvent : function(eventName, observable){
9466 this.on(eventName, function(e){
9467 observable.fireEvent(eventName, e);
9473 * Set the opacity of the element
9474 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9475 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9476 * @return {Roo.Element} this
9478 setOpacity : function(opacity, animate){
9480 var s = this.dom.style;
9483 s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9484 (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9486 s.opacity = opacity;
9489 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9495 * Gets the left X coordinate
9496 * @param {Boolean} local True to get the local css position instead of page coordinate
9499 getLeft : function(local){
9503 return parseInt(this.getStyle("left"), 10) || 0;
9508 * Gets the right X coordinate of the element (element X position + element width)
9509 * @param {Boolean} local True to get the local css position instead of page coordinate
9512 getRight : function(local){
9514 return this.getX() + this.getWidth();
9516 return (this.getLeft(true) + this.getWidth()) || 0;
9521 * Gets the top Y coordinate
9522 * @param {Boolean} local True to get the local css position instead of page coordinate
9525 getTop : function(local) {
9529 return parseInt(this.getStyle("top"), 10) || 0;
9534 * Gets the bottom Y coordinate of the element (element Y position + element height)
9535 * @param {Boolean} local True to get the local css position instead of page coordinate
9538 getBottom : function(local){
9540 return this.getY() + this.getHeight();
9542 return (this.getTop(true) + this.getHeight()) || 0;
9547 * Initializes positioning on this element. If a desired position is not passed, it will make the
9548 * the element positioned relative IF it is not already positioned.
9549 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9550 * @param {Number} zIndex (optional) The zIndex to apply
9551 * @param {Number} x (optional) Set the page X position
9552 * @param {Number} y (optional) Set the page Y position
9554 position : function(pos, zIndex, x, y){
9556 if(this.getStyle('position') == 'static'){
9557 this.setStyle('position', 'relative');
9560 this.setStyle("position", pos);
9563 this.setStyle("z-index", zIndex);
9565 if(x !== undefined && y !== undefined){
9567 }else if(x !== undefined){
9569 }else if(y !== undefined){
9575 * Clear positioning back to the default when the document was loaded
9576 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9577 * @return {Roo.Element} this
9579 clearPositioning : function(value){
9587 "position" : "static"
9593 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9594 * snapshot before performing an update and then restoring the element.
9597 getPositioning : function(){
9598 var l = this.getStyle("left");
9599 var t = this.getStyle("top");
9601 "position" : this.getStyle("position"),
9603 "right" : l ? "" : this.getStyle("right"),
9605 "bottom" : t ? "" : this.getStyle("bottom"),
9606 "z-index" : this.getStyle("z-index")
9611 * Gets the width of the border(s) for the specified side(s)
9612 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9613 * passing lr would get the border (l)eft width + the border (r)ight width.
9614 * @return {Number} The width of the sides passed added together
9616 getBorderWidth : function(side){
9617 return this.addStyles(side, El.borders);
9621 * Gets the width of the padding(s) for the specified side(s)
9622 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9623 * passing lr would get the padding (l)eft + the padding (r)ight.
9624 * @return {Number} The padding of the sides passed added together
9626 getPadding : function(side){
9627 return this.addStyles(side, El.paddings);
9631 * Set positioning with an object returned by getPositioning().
9632 * @param {Object} posCfg
9633 * @return {Roo.Element} this
9635 setPositioning : function(pc){
9636 this.applyStyles(pc);
9637 if(pc.right == "auto"){
9638 this.dom.style.right = "";
9640 if(pc.bottom == "auto"){
9641 this.dom.style.bottom = "";
9647 fixDisplay : function(){
9648 if(this.getStyle("display") == "none"){
9649 this.setStyle("visibility", "hidden");
9650 this.setStyle("display", this.originalDisplay); // first try reverting to default
9651 if(this.getStyle("display") == "none"){ // if that fails, default to block
9652 this.setStyle("display", "block");
9658 * Quick set left and top adding default units
9659 * @param {String} left The left CSS property value
9660 * @param {String} top The top CSS property value
9661 * @return {Roo.Element} this
9663 setLeftTop : function(left, top){
9664 this.dom.style.left = this.addUnits(left);
9665 this.dom.style.top = this.addUnits(top);
9670 * Move this element relative to its current position.
9671 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9672 * @param {Number} distance How far to move the element in pixels
9673 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9674 * @return {Roo.Element} this
9676 move : function(direction, distance, animate){
9677 var xy = this.getXY();
9678 direction = direction.toLowerCase();
9682 this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9686 this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9691 this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9696 this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9703 * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9704 * @return {Roo.Element} this
9707 if(!this.isClipped){
9708 this.isClipped = true;
9709 this.originalClip = {
9710 "o": this.getStyle("overflow"),
9711 "x": this.getStyle("overflow-x"),
9712 "y": this.getStyle("overflow-y")
9714 this.setStyle("overflow", "hidden");
9715 this.setStyle("overflow-x", "hidden");
9716 this.setStyle("overflow-y", "hidden");
9722 * Return clipping (overflow) to original clipping before clip() was called
9723 * @return {Roo.Element} this
9725 unclip : function(){
9727 this.isClipped = false;
9728 var o = this.originalClip;
9729 if(o.o){this.setStyle("overflow", o.o);}
9730 if(o.x){this.setStyle("overflow-x", o.x);}
9731 if(o.y){this.setStyle("overflow-y", o.y);}
9738 * Gets the x,y coordinates specified by the anchor position on the element.
9739 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo} for details on supported anchor positions.
9740 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9741 * {width: (target width), height: (target height)} (defaults to the element's current size)
9742 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9743 * @return {Array} [x, y] An array containing the element's x and y coordinates
9745 getAnchorXY : function(anchor, local, s){
9746 //Passing a different size is useful for pre-calculating anchors,
9747 //especially for anchored animations that change the el size.
9749 var w, h, vp = false;
9752 if(d == document.body || d == document){
9754 w = D.getViewWidth(); h = D.getViewHeight();
9756 w = this.getWidth(); h = this.getHeight();
9759 w = s.width; h = s.height;
9761 var x = 0, y = 0, r = Math.round;
9762 switch((anchor || "tl").toLowerCase()){
9804 var sc = this.getScroll();
9805 return [x + sc.left, y + sc.top];
9807 //Add the element's offset xy
9808 var o = this.getXY();
9809 return [x+o[0], y+o[1]];
9813 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9814 * supported position values.
9815 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9816 * @param {String} position The position to align to.
9817 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9818 * @return {Array} [x, y]
9820 getAlignToXY : function(el, p, o)
9825 throw "Element.alignTo with an element that doesn't exist";
9827 var c = false; //constrain to viewport
9828 var p1 = "", p2 = "";
9835 }else if(p.indexOf("-") == -1){
9838 p = p.toLowerCase();
9839 var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9841 throw "Element.alignTo with an invalid alignment " + p;
9843 p1 = m[1]; p2 = m[2]; c = !!m[3];
9845 //Subtract the aligned el's internal xy from the target's offset xy
9846 //plus custom offset to get the aligned el's new offset xy
9847 var a1 = this.getAnchorXY(p1, true);
9848 var a2 = el.getAnchorXY(p2, false);
9849 var x = a2[0] - a1[0] + o[0];
9850 var y = a2[1] - a1[1] + o[1];
9852 //constrain the aligned el to viewport if necessary
9853 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9854 // 5px of margin for ie
9855 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9857 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9858 //perpendicular to the vp border, allow the aligned el to slide on that border,
9859 //otherwise swap the aligned el to the opposite border of the target.
9860 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9861 var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9862 var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t") );
9863 var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9866 var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9867 var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9869 if((x+w) > dw + scrollX){
9870 x = swapX ? r.left-w : dw+scrollX-w;
9873 x = swapX ? r.right : scrollX;
9875 if((y+h) > dh + scrollY){
9876 y = swapY ? r.top-h : dh+scrollY-h;
9879 y = swapY ? r.bottom : scrollY;
9886 getConstrainToXY : function(){
9887 var os = {top:0, left:0, bottom:0, right: 0};
9889 return function(el, local, offsets, proposedXY){
9891 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9893 var vw, vh, vx = 0, vy = 0;
9894 if(el.dom == document.body || el.dom == document){
9895 vw = Roo.lib.Dom.getViewWidth();
9896 vh = Roo.lib.Dom.getViewHeight();
9898 vw = el.dom.clientWidth;
9899 vh = el.dom.clientHeight;
9901 var vxy = el.getXY();
9907 var s = el.getScroll();
9909 vx += offsets.left + s.left;
9910 vy += offsets.top + s.top;
9912 vw -= offsets.right;
9913 vh -= offsets.bottom;
9918 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9919 var x = xy[0], y = xy[1];
9920 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9922 // only move it if it needs it
9925 // first validate right/bottom
9934 // then make sure top/left isn't negative
9943 return moved ? [x, y] : false;
9948 adjustForConstraints : function(xy, parent, offsets){
9949 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
9953 * Aligns this element with another element relative to the specified anchor points. If the other element is the
9954 * document it aligns it to the viewport.
9955 * The position parameter is optional, and can be specified in any one of the following formats:
9957 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9958 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9959 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
9960 * deprecated in favor of the newer two anchor syntax below</i>.</li>
9961 * <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
9962 * element's anchor point, and the second value is used as the target's anchor point.</li>
9964 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
9965 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9966 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
9967 * that specified in order to enforce the viewport constraints.
9968 * Following are all of the supported anchor positions:
9971 ----- -----------------------------
9972 tl The top left corner (default)
9973 t The center of the top edge
9974 tr The top right corner
9975 l The center of the left edge
9976 c In the center of the element
9977 r The center of the right edge
9978 bl The bottom left corner
9979 b The center of the bottom edge
9980 br The bottom right corner
9984 // align el to other-el using the default positioning ("tl-bl", non-constrained)
9985 el.alignTo("other-el");
9987 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9988 el.alignTo("other-el", "tr?");
9990 // align the bottom right corner of el with the center left edge of other-el
9991 el.alignTo("other-el", "br-l?");
9993 // align the center of el with the bottom left corner of other-el and
9994 // adjust the x position by -6 pixels (and the y position by 0)
9995 el.alignTo("other-el", "c-bl", [-6, 0]);
9997 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9998 * @param {String} position The position to align to.
9999 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10000 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10001 * @return {Roo.Element} this
10003 alignTo : function(element, position, offsets, animate){
10004 var xy = this.getAlignToXY(element, position, offsets);
10005 this.setXY(xy, this.preanim(arguments, 3));
10010 * Anchors an element to another element and realigns it when the window is resized.
10011 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10012 * @param {String} position The position to align to.
10013 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10014 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10015 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10016 * is a number, it is used as the buffer delay (defaults to 50ms).
10017 * @param {Function} callback The function to call after the animation finishes
10018 * @return {Roo.Element} this
10020 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10021 var action = function(){
10022 this.alignTo(el, alignment, offsets, animate);
10023 Roo.callback(callback, this);
10025 Roo.EventManager.onWindowResize(action, this);
10026 var tm = typeof monitorScroll;
10027 if(tm != 'undefined'){
10028 Roo.EventManager.on(window, 'scroll', action, this,
10029 {buffer: tm == 'number' ? monitorScroll : 50});
10031 action.call(this); // align immediately
10035 * Clears any opacity settings from this element. Required in some cases for IE.
10036 * @return {Roo.Element} this
10038 clearOpacity : function(){
10039 if (window.ActiveXObject) {
10040 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10041 this.dom.style.filter = "";
10044 this.dom.style.opacity = "";
10045 this.dom.style["-moz-opacity"] = "";
10046 this.dom.style["-khtml-opacity"] = "";
10052 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10053 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10054 * @return {Roo.Element} this
10056 hide : function(animate){
10057 this.setVisible(false, this.preanim(arguments, 0));
10062 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10063 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10064 * @return {Roo.Element} this
10066 show : function(animate){
10067 this.setVisible(true, this.preanim(arguments, 0));
10072 * @private Test if size has a unit, otherwise appends the default
10074 addUnits : function(size){
10075 return Roo.Element.addUnits(size, this.defaultUnit);
10079 * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10080 * @return {Roo.Element} this
10082 beginMeasure : function(){
10084 if(el.offsetWidth || el.offsetHeight){
10085 return this; // offsets work already
10088 var p = this.dom, b = document.body; // start with this element
10089 while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10090 var pe = Roo.get(p);
10091 if(pe.getStyle('display') == 'none'){
10092 changed.push({el: p, visibility: pe.getStyle("visibility")});
10093 p.style.visibility = "hidden";
10094 p.style.display = "block";
10098 this._measureChanged = changed;
10104 * Restores displays to before beginMeasure was called
10105 * @return {Roo.Element} this
10107 endMeasure : function(){
10108 var changed = this._measureChanged;
10110 for(var i = 0, len = changed.length; i < len; i++) {
10111 var r = changed[i];
10112 r.el.style.visibility = r.visibility;
10113 r.el.style.display = "none";
10115 this._measureChanged = null;
10121 * Update the innerHTML of this element, optionally searching for and processing scripts
10122 * @param {String} html The new HTML
10123 * @param {Boolean} loadScripts (optional) true to look for and process scripts
10124 * @param {Function} callback For async script loading you can be noticed when the update completes
10125 * @return {Roo.Element} this
10127 update : function(html, loadScripts, callback){
10128 if(typeof html == "undefined"){
10131 if(loadScripts !== true){
10132 this.dom.innerHTML = html;
10133 if(typeof callback == "function"){
10139 var dom = this.dom;
10141 html += '<span id="' + id + '"></span>';
10143 E.onAvailable(id, function(){
10144 var hd = document.getElementsByTagName("head")[0];
10145 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10146 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10147 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10150 while(match = re.exec(html)){
10151 var attrs = match[1];
10152 var srcMatch = attrs ? attrs.match(srcRe) : false;
10153 if(srcMatch && srcMatch[2]){
10154 var s = document.createElement("script");
10155 s.src = srcMatch[2];
10156 var typeMatch = attrs.match(typeRe);
10157 if(typeMatch && typeMatch[2]){
10158 s.type = typeMatch[2];
10161 }else if(match[2] && match[2].length > 0){
10162 if(window.execScript) {
10163 window.execScript(match[2]);
10171 window.eval(match[2]);
10175 var el = document.getElementById(id);
10176 if(el){el.parentNode.removeChild(el);}
10177 if(typeof callback == "function"){
10181 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10186 * Direct access to the UpdateManager update() method (takes the same parameters).
10187 * @param {String/Function} url The url for this request or a function to call to get the url
10188 * @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}
10189 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10190 * @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.
10191 * @return {Roo.Element} this
10194 var um = this.getUpdateManager();
10195 um.update.apply(um, arguments);
10200 * Gets this element's UpdateManager
10201 * @return {Roo.UpdateManager} The UpdateManager
10203 getUpdateManager : function(){
10204 if(!this.updateManager){
10205 this.updateManager = new Roo.UpdateManager(this);
10207 return this.updateManager;
10211 * Disables text selection for this element (normalized across browsers)
10212 * @return {Roo.Element} this
10214 unselectable : function(){
10215 this.dom.unselectable = "on";
10216 this.swallowEvent("selectstart", true);
10217 this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10218 this.addClass("x-unselectable");
10223 * Calculates the x, y to center this element on the screen
10224 * @return {Array} The x, y values [x, y]
10226 getCenterXY : function(){
10227 return this.getAlignToXY(document, 'c-c');
10231 * Centers the Element in either the viewport, or another Element.
10232 * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10234 center : function(centerIn){
10235 this.alignTo(centerIn || document, 'c-c');
10240 * Tests various css rules/browsers to determine if this element uses a border box
10241 * @return {Boolean}
10243 isBorderBox : function(){
10244 return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10248 * Return a box {x, y, width, height} that can be used to set another elements
10249 * size/location to match this element.
10250 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10251 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10252 * @return {Object} box An object in the format {x, y, width, height}
10254 getBox : function(contentBox, local){
10259 var left = parseInt(this.getStyle("left"), 10) || 0;
10260 var top = parseInt(this.getStyle("top"), 10) || 0;
10263 var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10265 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10267 var l = this.getBorderWidth("l")+this.getPadding("l");
10268 var r = this.getBorderWidth("r")+this.getPadding("r");
10269 var t = this.getBorderWidth("t")+this.getPadding("t");
10270 var b = this.getBorderWidth("b")+this.getPadding("b");
10271 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)};
10273 bx.right = bx.x + bx.width;
10274 bx.bottom = bx.y + bx.height;
10279 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10280 for more information about the sides.
10281 * @param {String} sides
10284 getFrameWidth : function(sides, onlyContentBox){
10285 return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10289 * 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.
10290 * @param {Object} box The box to fill {x, y, width, height}
10291 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10292 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10293 * @return {Roo.Element} this
10295 setBox : function(box, adjust, animate){
10296 var w = box.width, h = box.height;
10297 if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10298 w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10299 h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10301 this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10306 * Forces the browser to repaint this element
10307 * @return {Roo.Element} this
10309 repaint : function(){
10310 var dom = this.dom;
10311 this.addClass("x-repaint");
10312 setTimeout(function(){
10313 Roo.get(dom).removeClass("x-repaint");
10319 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10320 * then it returns the calculated width of the sides (see getPadding)
10321 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10322 * @return {Object/Number}
10324 getMargins : function(side){
10327 top: parseInt(this.getStyle("margin-top"), 10) || 0,
10328 left: parseInt(this.getStyle("margin-left"), 10) || 0,
10329 bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10330 right: parseInt(this.getStyle("margin-right"), 10) || 0
10333 return this.addStyles(side, El.margins);
10338 addStyles : function(sides, styles){
10340 for(var i = 0, len = sides.length; i < len; i++){
10341 v = this.getStyle(styles[sides.charAt(i)]);
10343 w = parseInt(v, 10);
10351 * Creates a proxy element of this element
10352 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10353 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10354 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10355 * @return {Roo.Element} The new proxy element
10357 createProxy : function(config, renderTo, matchBox){
10359 renderTo = Roo.getDom(renderTo);
10361 renderTo = document.body;
10363 config = typeof config == "object" ?
10364 config : {tag : "div", cls: config};
10365 var proxy = Roo.DomHelper.append(renderTo, config, true);
10367 proxy.setBox(this.getBox());
10373 * Puts a mask over this element to disable user interaction. Requires core.css.
10374 * This method can only be applied to elements which accept child nodes.
10375 * @param {String} msg (optional) A message to display in the mask
10376 * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10377 * @return {Element} The mask element
10379 mask : function(msg, msgCls)
10381 if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10382 this.setStyle("position", "relative");
10385 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10388 this.addClass("x-masked");
10389 this._mask.setDisplayed(true);
10393 var dom = this.dom;
10394 while (dom && dom.style) {
10395 if (!isNaN(parseInt(dom.style.zIndex))) {
10396 z = Math.max(z, parseInt(dom.style.zIndex));
10398 dom = dom.parentNode;
10400 // if we are masking the body - then it hides everything..
10401 if (this.dom == document.body) {
10403 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10404 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10407 if(typeof msg == 'string'){
10408 if(!this._maskMsg){
10409 this._maskMsg = Roo.DomHelper.append(this.dom, {
10410 cls: "roo-el-mask-msg",
10414 cls: 'fa fa-spinner fa-spin'
10422 var mm = this._maskMsg;
10423 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10424 if (mm.dom.lastChild) { // weird IE issue?
10425 mm.dom.lastChild.innerHTML = msg;
10427 mm.setDisplayed(true);
10429 mm.setStyle('z-index', z + 102);
10431 if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10432 this._mask.setHeight(this.getHeight());
10434 this._mask.setStyle('z-index', z + 100);
10440 * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10441 * it is cached for reuse.
10443 unmask : function(removeEl){
10445 if(removeEl === true){
10446 this._mask.remove();
10449 this._maskMsg.remove();
10450 delete this._maskMsg;
10453 this._mask.setDisplayed(false);
10455 this._maskMsg.setDisplayed(false);
10459 this.removeClass("x-masked");
10463 * Returns true if this element is masked
10464 * @return {Boolean}
10466 isMasked : function(){
10467 return this._mask && this._mask.isVisible();
10471 * Creates an iframe shim for this element to keep selects and other windowed objects from
10473 * @return {Roo.Element} The new shim element
10475 createShim : function(){
10476 var el = document.createElement('iframe');
10477 el.frameBorder = 'no';
10478 el.className = 'roo-shim';
10479 if(Roo.isIE && Roo.isSecure){
10480 el.src = Roo.SSL_SECURE_URL;
10482 var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10483 shim.autoBoxAdjust = false;
10488 * Removes this element from the DOM and deletes it from the cache
10490 remove : function(){
10491 if(this.dom.parentNode){
10492 this.dom.parentNode.removeChild(this.dom);
10494 delete El.cache[this.dom.id];
10498 * Sets up event handlers to add and remove a css class when the mouse is over this element
10499 * @param {String} className
10500 * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10501 * mouseout events for children elements
10502 * @return {Roo.Element} this
10504 addClassOnOver : function(className, preventFlicker){
10505 this.on("mouseover", function(){
10506 Roo.fly(this, '_internal').addClass(className);
10508 var removeFn = function(e){
10509 if(preventFlicker !== true || !e.within(this, true)){
10510 Roo.fly(this, '_internal').removeClass(className);
10513 this.on("mouseout", removeFn, this.dom);
10518 * Sets up event handlers to add and remove a css class when this element has the focus
10519 * @param {String} className
10520 * @return {Roo.Element} this
10522 addClassOnFocus : function(className){
10523 this.on("focus", function(){
10524 Roo.fly(this, '_internal').addClass(className);
10526 this.on("blur", function(){
10527 Roo.fly(this, '_internal').removeClass(className);
10532 * 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)
10533 * @param {String} className
10534 * @return {Roo.Element} this
10536 addClassOnClick : function(className){
10537 var dom = this.dom;
10538 this.on("mousedown", function(){
10539 Roo.fly(dom, '_internal').addClass(className);
10540 var d = Roo.get(document);
10541 var fn = function(){
10542 Roo.fly(dom, '_internal').removeClass(className);
10543 d.removeListener("mouseup", fn);
10545 d.on("mouseup", fn);
10551 * Stops the specified event from bubbling and optionally prevents the default action
10552 * @param {String} eventName
10553 * @param {Boolean} preventDefault (optional) true to prevent the default action too
10554 * @return {Roo.Element} this
10556 swallowEvent : function(eventName, preventDefault){
10557 var fn = function(e){
10558 e.stopPropagation();
10559 if(preventDefault){
10560 e.preventDefault();
10563 if(eventName instanceof Array){
10564 for(var i = 0, len = eventName.length; i < len; i++){
10565 this.on(eventName[i], fn);
10569 this.on(eventName, fn);
10576 fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10579 * Sizes this element to its parent element's dimensions performing
10580 * neccessary box adjustments.
10581 * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10582 * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10583 * @return {Roo.Element} this
10585 fitToParent : function(monitorResize, targetParent) {
10586 Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10587 this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10588 if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10591 var p = Roo.get(targetParent || this.dom.parentNode);
10592 this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10593 if (monitorResize === true) {
10594 this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10595 Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10601 * Gets the next sibling, skipping text nodes
10602 * @return {HTMLElement} The next sibling or null
10604 getNextSibling : function(){
10605 var n = this.dom.nextSibling;
10606 while(n && n.nodeType != 1){
10613 * Gets the previous sibling, skipping text nodes
10614 * @return {HTMLElement} The previous sibling or null
10616 getPrevSibling : function(){
10617 var n = this.dom.previousSibling;
10618 while(n && n.nodeType != 1){
10619 n = n.previousSibling;
10626 * Appends the passed element(s) to this element
10627 * @param {String/HTMLElement/Array/Element/CompositeElement} el
10628 * @return {Roo.Element} this
10630 appendChild: function(el){
10637 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10638 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
10639 * automatically generated with the specified attributes.
10640 * @param {HTMLElement} insertBefore (optional) a child element of this element
10641 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10642 * @return {Roo.Element} The new child element
10644 createChild: function(config, insertBefore, returnDom){
10645 config = config || {tag:'div'};
10647 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10649 return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
10653 * Appends this element to the passed element
10654 * @param {String/HTMLElement/Element} el The new parent element
10655 * @return {Roo.Element} this
10657 appendTo: function(el){
10658 el = Roo.getDom(el);
10659 el.appendChild(this.dom);
10664 * Inserts this element before the passed element in the DOM
10665 * @param {String/HTMLElement/Element} el The element to insert before
10666 * @return {Roo.Element} this
10668 insertBefore: function(el){
10669 el = Roo.getDom(el);
10670 el.parentNode.insertBefore(this.dom, el);
10675 * Inserts this element after the passed element in the DOM
10676 * @param {String/HTMLElement/Element} el The element to insert after
10677 * @return {Roo.Element} this
10679 insertAfter: function(el){
10680 el = Roo.getDom(el);
10681 el.parentNode.insertBefore(this.dom, el.nextSibling);
10686 * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10687 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10688 * @return {Roo.Element} The new child
10690 insertFirst: function(el, returnDom){
10692 if(typeof el == 'object' && !el.nodeType){ // dh config
10693 return this.createChild(el, this.dom.firstChild, returnDom);
10695 el = Roo.getDom(el);
10696 this.dom.insertBefore(el, this.dom.firstChild);
10697 return !returnDom ? Roo.get(el) : el;
10702 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10703 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10704 * @param {String} where (optional) 'before' or 'after' defaults to before
10705 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10706 * @return {Roo.Element} the inserted Element
10708 insertSibling: function(el, where, returnDom){
10709 where = where ? where.toLowerCase() : 'before';
10711 var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10713 if(typeof el == 'object' && !el.nodeType){ // dh config
10714 if(where == 'after' && !this.dom.nextSibling){
10715 rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10717 rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10721 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10722 where == 'before' ? this.dom : this.dom.nextSibling);
10731 * Creates and wraps this element with another element
10732 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10733 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10734 * @return {HTMLElement/Element} The newly created wrapper element
10736 wrap: function(config, returnDom){
10738 config = {tag: "div"};
10740 var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10741 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10746 * Replaces the passed element with this element
10747 * @param {String/HTMLElement/Element} el The element to replace
10748 * @return {Roo.Element} this
10750 replace: function(el){
10752 this.insertBefore(el);
10758 * Inserts an html fragment into this element
10759 * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10760 * @param {String} html The HTML fragment
10761 * @param {Boolean} returnEl True to return an Roo.Element
10762 * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10764 insertHtml : function(where, html, returnEl){
10765 var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10766 return returnEl ? Roo.get(el) : el;
10770 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10771 * @param {Object} o The object with the attributes
10772 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10773 * @return {Roo.Element} this
10775 set : function(o, useSet){
10777 useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10778 for(var attr in o){
10779 if(attr == "style" || typeof o[attr] == "function") { continue; }
10781 el.className = o["cls"];
10784 el.setAttribute(attr, o[attr]);
10786 el[attr] = o[attr];
10791 Roo.DomHelper.applyStyles(el, o.style);
10797 * Convenience method for constructing a KeyMap
10798 * @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:
10799 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10800 * @param {Function} fn The function to call
10801 * @param {Object} scope (optional) The scope of the function
10802 * @return {Roo.KeyMap} The KeyMap created
10804 addKeyListener : function(key, fn, scope){
10806 if(typeof key != "object" || key instanceof Array){
10822 return new Roo.KeyMap(this, config);
10826 * Creates a KeyMap for this element
10827 * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10828 * @return {Roo.KeyMap} The KeyMap created
10830 addKeyMap : function(config){
10831 return new Roo.KeyMap(this, config);
10835 * Returns true if this element is scrollable.
10836 * @return {Boolean}
10838 isScrollable : function(){
10839 var dom = this.dom;
10840 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10844 * 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().
10845 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10846 * @param {Number} value The new scroll value
10847 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10848 * @return {Element} this
10851 scrollTo : function(side, value, animate){
10852 var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10853 if(!animate || !A){
10854 this.dom[prop] = value;
10856 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10857 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10863 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10864 * within this element's scrollable range.
10865 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10866 * @param {Number} distance How far to scroll the element in pixels
10867 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10868 * @return {Boolean} Returns true if a scroll was triggered or false if the element
10869 * was scrolled as far as it could go.
10871 scroll : function(direction, distance, animate){
10872 if(!this.isScrollable()){
10876 var l = el.scrollLeft, t = el.scrollTop;
10877 var w = el.scrollWidth, h = el.scrollHeight;
10878 var cw = el.clientWidth, ch = el.clientHeight;
10879 direction = direction.toLowerCase();
10880 var scrolled = false;
10881 var a = this.preanim(arguments, 2);
10886 var v = Math.min(l + distance, w-cw);
10887 this.scrollTo("left", v, a);
10894 var v = Math.max(l - distance, 0);
10895 this.scrollTo("left", v, a);
10903 var v = Math.max(t - distance, 0);
10904 this.scrollTo("top", v, a);
10912 var v = Math.min(t + distance, h-ch);
10913 this.scrollTo("top", v, a);
10922 * Translates the passed page coordinates into left/top css values for this element
10923 * @param {Number/Array} x The page x or an array containing [x, y]
10924 * @param {Number} y The page y
10925 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10927 translatePoints : function(x, y){
10928 if(typeof x == 'object' || x instanceof Array){
10929 y = x[1]; x = x[0];
10931 var p = this.getStyle('position');
10932 var o = this.getXY();
10934 var l = parseInt(this.getStyle('left'), 10);
10935 var t = parseInt(this.getStyle('top'), 10);
10938 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10941 t = (p == "relative") ? 0 : this.dom.offsetTop;
10944 return {left: (x - o[0] + l), top: (y - o[1] + t)};
10948 * Returns the current scroll position of the element.
10949 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10951 getScroll : function(){
10952 var d = this.dom, doc = document;
10953 if(d == doc || d == doc.body){
10954 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10955 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10956 return {left: l, top: t};
10958 return {left: d.scrollLeft, top: d.scrollTop};
10963 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10964 * are convert to standard 6 digit hex color.
10965 * @param {String} attr The css attribute
10966 * @param {String} defaultValue The default value to use when a valid color isn't found
10967 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10970 getColor : function(attr, defaultValue, prefix){
10971 var v = this.getStyle(attr);
10972 if(!v || v == "transparent" || v == "inherit") {
10973 return defaultValue;
10975 var color = typeof prefix == "undefined" ? "#" : prefix;
10976 if(v.substr(0, 4) == "rgb("){
10977 var rvs = v.slice(4, v.length -1).split(",");
10978 for(var i = 0; i < 3; i++){
10979 var h = parseInt(rvs[i]).toString(16);
10986 if(v.substr(0, 1) == "#"){
10987 if(v.length == 4) {
10988 for(var i = 1; i < 4; i++){
10989 var c = v.charAt(i);
10992 }else if(v.length == 7){
10993 color += v.substr(1);
10997 return(color.length > 5 ? color.toLowerCase() : defaultValue);
11001 * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11002 * gradient background, rounded corners and a 4-way shadow.
11003 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11004 * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11005 * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11006 * @return {Roo.Element} this
11008 boxWrap : function(cls){
11009 cls = cls || 'x-box';
11010 var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11011 el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11016 * Returns the value of a namespaced attribute from the element's underlying DOM node.
11017 * @param {String} namespace The namespace in which to look for the attribute
11018 * @param {String} name The attribute name
11019 * @return {String} The attribute value
11021 getAttributeNS : Roo.isIE ? function(ns, name){
11023 var type = typeof d[ns+":"+name];
11024 if(type != 'undefined' && type != 'unknown'){
11025 return d[ns+":"+name];
11028 } : function(ns, name){
11030 return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11035 * Sets or Returns the value the dom attribute value
11036 * @param {String|Object} name The attribute name (or object to set multiple attributes)
11037 * @param {String} value (optional) The value to set the attribute to
11038 * @return {String} The attribute value
11040 attr : function(name){
11041 if (arguments.length > 1) {
11042 this.dom.setAttribute(name, arguments[1]);
11043 return arguments[1];
11045 if (typeof(name) == 'object') {
11046 for(var i in name) {
11047 this.attr(i, name[i]);
11053 if (!this.dom.hasAttribute(name)) {
11056 return this.dom.getAttribute(name);
11063 var ep = El.prototype;
11066 * Appends an event handler (Shorthand for addListener)
11067 * @param {String} eventName The type of event to append
11068 * @param {Function} fn The method the event invokes
11069 * @param {Object} scope (optional) The scope (this object) of the fn
11070 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
11073 ep.on = ep.addListener;
11074 // backwards compat
11075 ep.mon = ep.addListener;
11078 * Removes an event handler from this element (shorthand for removeListener)
11079 * @param {String} eventName the type of event to remove
11080 * @param {Function} fn the method the event invokes
11081 * @return {Roo.Element} this
11084 ep.un = ep.removeListener;
11087 * true to automatically adjust width and height settings for box-model issues (default to true)
11089 ep.autoBoxAdjust = true;
11092 El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11095 El.addUnits = function(v, defaultUnit){
11096 if(v === "" || v == "auto"){
11099 if(v === undefined){
11102 if(typeof v == "number" || !El.unitPattern.test(v)){
11103 return v + (defaultUnit || 'px');
11108 // special markup used throughout Roo when box wrapping elements
11109 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>';
11111 * Visibility mode constant - Use visibility to hide element
11117 * Visibility mode constant - Use display to hide element
11123 El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11124 El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11125 El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11137 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11138 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11139 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11140 * @return {Element} The Element object
11143 El.get = function(el){
11145 if(!el){ return null; }
11146 if(typeof el == "string"){ // element id
11147 if(!(elm = document.getElementById(el))){
11150 if(ex = El.cache[el]){
11153 ex = El.cache[el] = new El(elm);
11156 }else if(el.tagName){ // dom element
11160 if(ex = El.cache[id]){
11163 ex = El.cache[id] = new El(el);
11166 }else if(el instanceof El){
11168 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11169 // catch case where it hasn't been appended
11170 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11173 }else if(el.isComposite){
11175 }else if(el instanceof Array){
11176 return El.select(el);
11177 }else if(el == document){
11178 // create a bogus element object representing the document object
11180 var f = function(){};
11181 f.prototype = El.prototype;
11183 docEl.dom = document;
11191 El.uncache = function(el){
11192 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11194 delete El.cache[a[i].id || a[i]];
11200 // Garbage collection - uncache elements/purge listeners on orphaned elements
11201 // so we don't hold a reference and cause the browser to retain them
11202 El.garbageCollect = function(){
11203 if(!Roo.enableGarbageCollector){
11204 clearInterval(El.collectorThread);
11207 for(var eid in El.cache){
11208 var el = El.cache[eid], d = el.dom;
11209 // -------------------------------------------------------
11210 // Determining what is garbage:
11211 // -------------------------------------------------------
11213 // dom node is null, definitely garbage
11214 // -------------------------------------------------------
11216 // no parentNode == direct orphan, definitely garbage
11217 // -------------------------------------------------------
11218 // !d.offsetParent && !document.getElementById(eid)
11219 // display none elements have no offsetParent so we will
11220 // also try to look it up by it's id. However, check
11221 // offsetParent first so we don't do unneeded lookups.
11222 // This enables collection of elements that are not orphans
11223 // directly, but somewhere up the line they have an orphan
11225 // -------------------------------------------------------
11226 if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11227 delete El.cache[eid];
11228 if(d && Roo.enableListenerCollection){
11234 El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11238 El.Flyweight = function(dom){
11241 El.Flyweight.prototype = El.prototype;
11243 El._flyweights = {};
11245 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11246 * the dom node can be overwritten by other code.
11247 * @param {String/HTMLElement} el The dom node or id
11248 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11249 * prevent conflicts (e.g. internally Roo uses "_internal")
11251 * @return {Element} The shared Element object
11253 El.fly = function(el, named){
11254 named = named || '_global';
11255 el = Roo.getDom(el);
11259 if(!El._flyweights[named]){
11260 El._flyweights[named] = new El.Flyweight();
11262 El._flyweights[named].dom = el;
11263 return El._flyweights[named];
11267 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11268 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11269 * Shorthand of {@link Roo.Element#get}
11270 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11271 * @return {Element} The Element object
11277 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11278 * the dom node can be overwritten by other code.
11279 * Shorthand of {@link Roo.Element#fly}
11280 * @param {String/HTMLElement} el The dom node or id
11281 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11282 * prevent conflicts (e.g. internally Roo uses "_internal")
11284 * @return {Element} The shared Element object
11290 // speedy lookup for elements never to box adjust
11291 var noBoxAdjust = Roo.isStrict ? {
11294 input:1, select:1, textarea:1
11296 if(Roo.isIE || Roo.isGecko){
11297 noBoxAdjust['button'] = 1;
11301 Roo.EventManager.on(window, 'unload', function(){
11303 delete El._flyweights;
11311 Roo.Element.selectorFunction = Roo.DomQuery.select;
11314 Roo.Element.select = function(selector, unique, root){
11316 if(typeof selector == "string"){
11317 els = Roo.Element.selectorFunction(selector, root);
11318 }else if(selector.length !== undefined){
11321 throw "Invalid selector";
11323 if(unique === true){
11324 return new Roo.CompositeElement(els);
11326 return new Roo.CompositeElementLite(els);
11330 * Selects elements based on the passed CSS selector to enable working on them as 1.
11331 * @param {String/Array} selector The CSS selector or an array of elements
11332 * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11333 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11334 * @return {CompositeElementLite/CompositeElement}
11338 Roo.select = Roo.Element.select;
11355 * Ext JS Library 1.1.1
11356 * Copyright(c) 2006-2007, Ext JS, LLC.
11358 * Originally Released Under LGPL - original licence link has changed is not relivant.
11361 * <script type="text/javascript">
11366 //Notifies Element that fx methods are available
11367 Roo.enableFx = true;
11371 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
11372 * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11373 * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the
11374 * Element effects to work.</p><br/>
11376 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11377 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11378 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11379 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
11380 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11381 * expected results and should be done with care.</p><br/>
11383 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11384 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
11387 ----- -----------------------------
11388 tl The top left corner
11389 t The center of the top edge
11390 tr The top right corner
11391 l The center of the left edge
11392 r The center of the right edge
11393 bl The bottom left corner
11394 b The center of the bottom edge
11395 br The bottom right corner
11397 * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11398 * below are common options that can be passed to any Fx method.</b>
11399 * @cfg {Function} callback A function called when the effect is finished
11400 * @cfg {Object} scope The scope of the effect function
11401 * @cfg {String} easing A valid Easing value for the effect
11402 * @cfg {String} afterCls A css class to apply after the effect
11403 * @cfg {Number} duration The length of time (in seconds) that the effect should last
11404 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11405 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
11406 * effects that end with the element being visually hidden, ignored otherwise)
11407 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11408 * a function which returns such a specification that will be applied to the Element after the effect finishes
11409 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11410 * @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
11411 * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11415 * Slides the element into view. An anchor point can be optionally passed to set the point of
11416 * origin for the slide effect. This function automatically handles wrapping the element with
11417 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11420 // default: slide the element in from the top
11423 // custom: slide the element in from the right with a 2-second duration
11424 el.slideIn('r', { duration: 2 });
11426 // common config options shown with default values
11432 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11433 * @param {Object} options (optional) Object literal with any of the Fx config options
11434 * @return {Roo.Element} The Element
11436 slideIn : function(anchor, o){
11437 var el = this.getFxEl();
11440 el.queueFx(o, function(){
11442 anchor = anchor || "t";
11444 // fix display to visibility
11447 // restore values after effect
11448 var r = this.getFxRestore();
11449 var b = this.getBox();
11450 // fixed size for slide
11454 var wrap = this.fxWrap(r.pos, o, "hidden");
11456 var st = this.dom.style;
11457 st.visibility = "visible";
11458 st.position = "absolute";
11460 // clear out temp styles after slide and unwrap
11461 var after = function(){
11462 el.fxUnwrap(wrap, r.pos, o);
11463 st.width = r.width;
11464 st.height = r.height;
11467 // time to calc the positions
11468 var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11470 switch(anchor.toLowerCase()){
11472 wrap.setSize(b.width, 0);
11473 st.left = st.bottom = "0";
11477 wrap.setSize(0, b.height);
11478 st.right = st.top = "0";
11482 wrap.setSize(0, b.height);
11483 wrap.setX(b.right);
11484 st.left = st.top = "0";
11485 a = {width: bw, points: pt};
11488 wrap.setSize(b.width, 0);
11489 wrap.setY(b.bottom);
11490 st.left = st.top = "0";
11491 a = {height: bh, points: pt};
11494 wrap.setSize(0, 0);
11495 st.right = st.bottom = "0";
11496 a = {width: bw, height: bh};
11499 wrap.setSize(0, 0);
11500 wrap.setY(b.y+b.height);
11501 st.right = st.top = "0";
11502 a = {width: bw, height: bh, points: pt};
11505 wrap.setSize(0, 0);
11506 wrap.setXY([b.right, b.bottom]);
11507 st.left = st.top = "0";
11508 a = {width: bw, height: bh, points: pt};
11511 wrap.setSize(0, 0);
11512 wrap.setX(b.x+b.width);
11513 st.left = st.bottom = "0";
11514 a = {width: bw, height: bh, points: pt};
11517 this.dom.style.visibility = "visible";
11520 arguments.callee.anim = wrap.fxanim(a,
11530 * Slides the element out of view. An anchor point can be optionally passed to set the end point
11531 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
11532 * 'hidden') but block elements will still take up space in the document. The element must be removed
11533 * from the DOM using the 'remove' config option if desired. This function automatically handles
11534 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11537 // default: slide the element out to the top
11540 // custom: slide the element out to the right with a 2-second duration
11541 el.slideOut('r', { duration: 2 });
11543 // common config options shown with default values
11551 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11552 * @param {Object} options (optional) Object literal with any of the Fx config options
11553 * @return {Roo.Element} The Element
11555 slideOut : function(anchor, o){
11556 var el = this.getFxEl();
11559 el.queueFx(o, function(){
11561 anchor = anchor || "t";
11563 // restore values after effect
11564 var r = this.getFxRestore();
11566 var b = this.getBox();
11567 // fixed size for slide
11571 var wrap = this.fxWrap(r.pos, o, "visible");
11573 var st = this.dom.style;
11574 st.visibility = "visible";
11575 st.position = "absolute";
11579 var after = function(){
11581 el.setDisplayed(false);
11586 el.fxUnwrap(wrap, r.pos, o);
11588 st.width = r.width;
11589 st.height = r.height;
11594 var a, zero = {to: 0};
11595 switch(anchor.toLowerCase()){
11597 st.left = st.bottom = "0";
11598 a = {height: zero};
11601 st.right = st.top = "0";
11605 st.left = st.top = "0";
11606 a = {width: zero, points: {to:[b.right, b.y]}};
11609 st.left = st.top = "0";
11610 a = {height: zero, points: {to:[b.x, b.bottom]}};
11613 st.right = st.bottom = "0";
11614 a = {width: zero, height: zero};
11617 st.right = st.top = "0";
11618 a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11621 st.left = st.top = "0";
11622 a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11625 st.left = st.bottom = "0";
11626 a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11630 arguments.callee.anim = wrap.fxanim(a,
11640 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
11641 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
11642 * The element must be removed from the DOM using the 'remove' config option if desired.
11648 // common config options shown with default values
11656 * @param {Object} options (optional) Object literal with any of the Fx config options
11657 * @return {Roo.Element} The Element
11659 puff : function(o){
11660 var el = this.getFxEl();
11663 el.queueFx(o, function(){
11664 this.clearOpacity();
11667 // restore values after effect
11668 var r = this.getFxRestore();
11669 var st = this.dom.style;
11671 var after = function(){
11673 el.setDisplayed(false);
11680 el.setPositioning(r.pos);
11681 st.width = r.width;
11682 st.height = r.height;
11687 var width = this.getWidth();
11688 var height = this.getHeight();
11690 arguments.callee.anim = this.fxanim({
11691 width : {to: this.adjustWidth(width * 2)},
11692 height : {to: this.adjustHeight(height * 2)},
11693 points : {by: [-(width * .5), -(height * .5)]},
11695 fontSize: {to:200, unit: "%"}
11706 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11707 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
11708 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11714 // all config options shown with default values
11722 * @param {Object} options (optional) Object literal with any of the Fx config options
11723 * @return {Roo.Element} The Element
11725 switchOff : function(o){
11726 var el = this.getFxEl();
11729 el.queueFx(o, function(){
11730 this.clearOpacity();
11733 // restore values after effect
11734 var r = this.getFxRestore();
11735 var st = this.dom.style;
11737 var after = function(){
11739 el.setDisplayed(false);
11745 el.setPositioning(r.pos);
11746 st.width = r.width;
11747 st.height = r.height;
11752 this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11753 this.clearOpacity();
11757 points:{by:[0, this.getHeight() * .5]}
11758 }, o, 'motion', 0.3, 'easeIn', after);
11759 }).defer(100, this);
11766 * Highlights the Element by setting a color (applies to the background-color by default, but can be
11767 * changed using the "attr" config option) and then fading back to the original color. If no original
11768 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11771 // default: highlight background to yellow
11774 // custom: highlight foreground text to blue for 2 seconds
11775 el.highlight("0000ff", { attr: 'color', duration: 2 });
11777 // common config options shown with default values
11778 el.highlight("ffff9c", {
11779 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11780 endColor: (current color) or "ffffff",
11785 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11786 * @param {Object} options (optional) Object literal with any of the Fx config options
11787 * @return {Roo.Element} The Element
11789 highlight : function(color, o){
11790 var el = this.getFxEl();
11793 el.queueFx(o, function(){
11794 color = color || "ffff9c";
11795 attr = o.attr || "backgroundColor";
11797 this.clearOpacity();
11800 var origColor = this.getColor(attr);
11801 var restoreColor = this.dom.style[attr];
11802 endColor = (o.endColor || origColor) || "ffffff";
11804 var after = function(){
11805 el.dom.style[attr] = restoreColor;
11810 a[attr] = {from: color, to: endColor};
11811 arguments.callee.anim = this.fxanim(a,
11821 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11824 // default: a single light blue ripple
11827 // custom: 3 red ripples lasting 3 seconds total
11828 el.frame("ff0000", 3, { duration: 3 });
11830 // common config options shown with default values
11831 el.frame("C3DAF9", 1, {
11832 duration: 1 //duration of entire animation (not each individual ripple)
11833 // Note: Easing is not configurable and will be ignored if included
11836 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11837 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11838 * @param {Object} options (optional) Object literal with any of the Fx config options
11839 * @return {Roo.Element} The Element
11841 frame : function(color, count, o){
11842 var el = this.getFxEl();
11845 el.queueFx(o, function(){
11846 color = color || "#C3DAF9";
11847 if(color.length == 6){
11848 color = "#" + color;
11850 count = count || 1;
11851 duration = o.duration || 1;
11854 var b = this.getBox();
11855 var animFn = function(){
11856 var proxy = this.createProxy({
11859 visbility:"hidden",
11860 position:"absolute",
11861 "z-index":"35000", // yee haw
11862 border:"0px solid " + color
11865 var scale = Roo.isBorderBox ? 2 : 1;
11867 top:{from:b.y, to:b.y - 20},
11868 left:{from:b.x, to:b.x - 20},
11869 borderWidth:{from:0, to:10},
11870 opacity:{from:1, to:0},
11871 height:{from:b.height, to:(b.height + (20*scale))},
11872 width:{from:b.width, to:(b.width + (20*scale))}
11873 }, duration, function(){
11877 animFn.defer((duration/2)*1000, this);
11888 * Creates a pause before any subsequent queued effects begin. If there are
11889 * no effects queued after the pause it will have no effect.
11894 * @param {Number} seconds The length of time to pause (in seconds)
11895 * @return {Roo.Element} The Element
11897 pause : function(seconds){
11898 var el = this.getFxEl();
11901 el.queueFx(o, function(){
11902 setTimeout(function(){
11904 }, seconds * 1000);
11910 * Fade an element in (from transparent to opaque). The ending opacity can be specified
11911 * using the "endOpacity" config option.
11914 // default: fade in from opacity 0 to 100%
11917 // custom: fade in from opacity 0 to 75% over 2 seconds
11918 el.fadeIn({ endOpacity: .75, duration: 2});
11920 // common config options shown with default values
11922 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11927 * @param {Object} options (optional) Object literal with any of the Fx config options
11928 * @return {Roo.Element} The Element
11930 fadeIn : function(o){
11931 var el = this.getFxEl();
11933 el.queueFx(o, function(){
11934 this.setOpacity(0);
11936 this.dom.style.visibility = 'visible';
11937 var to = o.endOpacity || 1;
11938 arguments.callee.anim = this.fxanim({opacity:{to:to}},
11939 o, null, .5, "easeOut", function(){
11941 this.clearOpacity();
11950 * Fade an element out (from opaque to transparent). The ending opacity can be specified
11951 * using the "endOpacity" config option.
11954 // default: fade out from the element's current opacity to 0
11957 // custom: fade out from the element's current opacity to 25% over 2 seconds
11958 el.fadeOut({ endOpacity: .25, duration: 2});
11960 // common config options shown with default values
11962 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11969 * @param {Object} options (optional) Object literal with any of the Fx config options
11970 * @return {Roo.Element} The Element
11972 fadeOut : function(o){
11973 var el = this.getFxEl();
11975 el.queueFx(o, function(){
11976 arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11977 o, null, .5, "easeOut", function(){
11978 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11979 this.dom.style.display = "none";
11981 this.dom.style.visibility = "hidden";
11983 this.clearOpacity();
11991 * Animates the transition of an element's dimensions from a starting height/width
11992 * to an ending height/width.
11995 // change height and width to 100x100 pixels
11996 el.scale(100, 100);
11998 // common config options shown with default values. The height and width will default to
11999 // the element's existing values if passed as null.
12002 [element's height], {
12007 * @param {Number} width The new width (pass undefined to keep the original width)
12008 * @param {Number} height The new height (pass undefined to keep the original height)
12009 * @param {Object} options (optional) Object literal with any of the Fx config options
12010 * @return {Roo.Element} The Element
12012 scale : function(w, h, o){
12013 this.shift(Roo.apply({}, o, {
12021 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12022 * Any of these properties not specified in the config object will not be changed. This effect
12023 * requires that at least one new dimension, position or opacity setting must be passed in on
12024 * the config object in order for the function to have any effect.
12027 // slide the element horizontally to x position 200 while changing the height and opacity
12028 el.shift({ x: 200, height: 50, opacity: .8 });
12030 // common config options shown with default values.
12032 width: [element's width],
12033 height: [element's height],
12034 x: [element's x position],
12035 y: [element's y position],
12036 opacity: [element's opacity],
12041 * @param {Object} options Object literal with any of the Fx config options
12042 * @return {Roo.Element} The Element
12044 shift : function(o){
12045 var el = this.getFxEl();
12047 el.queueFx(o, function(){
12048 var a = {}, w = o.width, h = o.height, x = o.x, y = o.y, op = o.opacity;
12049 if(w !== undefined){
12050 a.width = {to: this.adjustWidth(w)};
12052 if(h !== undefined){
12053 a.height = {to: this.adjustHeight(h)};
12055 if(x !== undefined || y !== undefined){
12057 x !== undefined ? x : this.getX(),
12058 y !== undefined ? y : this.getY()
12061 if(op !== undefined){
12062 a.opacity = {to: op};
12064 if(o.xy !== undefined){
12065 a.points = {to: o.xy};
12067 arguments.callee.anim = this.fxanim(a,
12068 o, 'motion', .35, "easeOut", function(){
12076 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
12077 * ending point of the effect.
12080 // default: slide the element downward while fading out
12083 // custom: slide the element out to the right with a 2-second duration
12084 el.ghost('r', { duration: 2 });
12086 // common config options shown with default values
12094 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12095 * @param {Object} options (optional) Object literal with any of the Fx config options
12096 * @return {Roo.Element} The Element
12098 ghost : function(anchor, o){
12099 var el = this.getFxEl();
12102 el.queueFx(o, function(){
12103 anchor = anchor || "b";
12105 // restore values after effect
12106 var r = this.getFxRestore();
12107 var w = this.getWidth(),
12108 h = this.getHeight();
12110 var st = this.dom.style;
12112 var after = function(){
12114 el.setDisplayed(false);
12120 el.setPositioning(r.pos);
12121 st.width = r.width;
12122 st.height = r.height;
12127 var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12128 switch(anchor.toLowerCase()){
12155 arguments.callee.anim = this.fxanim(a,
12165 * Ensures that all effects queued after syncFx is called on the element are
12166 * run concurrently. This is the opposite of {@link #sequenceFx}.
12167 * @return {Roo.Element} The Element
12169 syncFx : function(){
12170 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12179 * Ensures that all effects queued after sequenceFx is called on the element are
12180 * run in sequence. This is the opposite of {@link #syncFx}.
12181 * @return {Roo.Element} The Element
12183 sequenceFx : function(){
12184 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12186 concurrent : false,
12193 nextFx : function(){
12194 var ef = this.fxQueue[0];
12201 * Returns true if the element has any effects actively running or queued, else returns false.
12202 * @return {Boolean} True if element has active effects, else false
12204 hasActiveFx : function(){
12205 return this.fxQueue && this.fxQueue[0];
12209 * Stops any running effects and clears the element's internal effects queue if it contains
12210 * any additional effects that haven't started yet.
12211 * @return {Roo.Element} The Element
12213 stopFx : function(){
12214 if(this.hasActiveFx()){
12215 var cur = this.fxQueue[0];
12216 if(cur && cur.anim && cur.anim.isAnimated()){
12217 this.fxQueue = [cur]; // clear out others
12218 cur.anim.stop(true);
12225 beforeFx : function(o){
12226 if(this.hasActiveFx() && !o.concurrent){
12237 * Returns true if the element is currently blocking so that no other effect can be queued
12238 * until this effect is finished, else returns false if blocking is not set. This is commonly
12239 * used to ensure that an effect initiated by a user action runs to completion prior to the
12240 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12241 * @return {Boolean} True if blocking, else false
12243 hasFxBlock : function(){
12244 var q = this.fxQueue;
12245 return q && q[0] && q[0].block;
12249 queueFx : function(o, fn){
12253 if(!this.hasFxBlock()){
12254 Roo.applyIf(o, this.fxDefaults);
12256 var run = this.beforeFx(o);
12257 fn.block = o.block;
12258 this.fxQueue.push(fn);
12270 fxWrap : function(pos, o, vis){
12272 if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12275 wrapXY = this.getXY();
12277 var div = document.createElement("div");
12278 div.style.visibility = vis;
12279 wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12280 wrap.setPositioning(pos);
12281 if(wrap.getStyle("position") == "static"){
12282 wrap.position("relative");
12284 this.clearPositioning('auto');
12286 wrap.dom.appendChild(this.dom);
12288 wrap.setXY(wrapXY);
12295 fxUnwrap : function(wrap, pos, o){
12296 this.clearPositioning();
12297 this.setPositioning(pos);
12299 wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12305 getFxRestore : function(){
12306 var st = this.dom.style;
12307 return {pos: this.getPositioning(), width: st.width, height : st.height};
12311 afterFx : function(o){
12313 this.applyStyles(o.afterStyle);
12316 this.addClass(o.afterCls);
12318 if(o.remove === true){
12321 Roo.callback(o.callback, o.scope, [this]);
12323 this.fxQueue.shift();
12329 getFxEl : function(){ // support for composite element fx
12330 return Roo.get(this.dom);
12334 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12335 animType = animType || 'run';
12337 var anim = Roo.lib.Anim[animType](
12339 (opt.duration || defaultDur) || .35,
12340 (opt.easing || defaultEase) || 'easeOut',
12342 Roo.callback(cb, this);
12351 // backwords compat
12352 Roo.Fx.resize = Roo.Fx.scale;
12354 //When included, Roo.Fx is automatically applied to Element so that all basic
12355 //effects are available directly via the Element API
12356 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12358 * Ext JS Library 1.1.1
12359 * Copyright(c) 2006-2007, Ext JS, LLC.
12361 * Originally Released Under LGPL - original licence link has changed is not relivant.
12364 * <script type="text/javascript">
12369 * @class Roo.CompositeElement
12370 * Standard composite class. Creates a Roo.Element for every element in the collection.
12372 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12373 * actions will be performed on all the elements in this collection.</b>
12375 * All methods return <i>this</i> and can be chained.
12377 var els = Roo.select("#some-el div.some-class", true);
12378 // or select directly from an existing element
12379 var el = Roo.get('some-el');
12380 el.select('div.some-class', true);
12382 els.setWidth(100); // all elements become 100 width
12383 els.hide(true); // all elements fade out and hide
12385 els.setWidth(100).hide(true);
12388 Roo.CompositeElement = function(els){
12389 this.elements = [];
12390 this.addElements(els);
12392 Roo.CompositeElement.prototype = {
12394 addElements : function(els){
12398 if(typeof els == "string"){
12399 els = Roo.Element.selectorFunction(els);
12401 var yels = this.elements;
12402 var index = yels.length-1;
12403 for(var i = 0, len = els.length; i < len; i++) {
12404 yels[++index] = Roo.get(els[i]);
12410 * Clears this composite and adds the elements returned by the passed selector.
12411 * @param {String/Array} els A string CSS selector, an array of elements or an element
12412 * @return {CompositeElement} this
12414 fill : function(els){
12415 this.elements = [];
12421 * Filters this composite to only elements that match the passed selector.
12422 * @param {String} selector A string CSS selector
12423 * @param {Boolean} inverse return inverse filter (not matches)
12424 * @return {CompositeElement} this
12426 filter : function(selector, inverse){
12428 inverse = inverse || false;
12429 this.each(function(el){
12430 var match = inverse ? !el.is(selector) : el.is(selector);
12432 els[els.length] = el.dom;
12439 invoke : function(fn, args){
12440 var els = this.elements;
12441 for(var i = 0, len = els.length; i < len; i++) {
12442 Roo.Element.prototype[fn].apply(els[i], args);
12447 * Adds elements to this composite.
12448 * @param {String/Array} els A string CSS selector, an array of elements or an element
12449 * @return {CompositeElement} this
12451 add : function(els){
12452 if(typeof els == "string"){
12453 this.addElements(Roo.Element.selectorFunction(els));
12454 }else if(els.length !== undefined){
12455 this.addElements(els);
12457 this.addElements([els]);
12462 * Calls the passed function passing (el, this, index) for each element in this composite.
12463 * @param {Function} fn The function to call
12464 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12465 * @return {CompositeElement} this
12467 each : function(fn, scope){
12468 var els = this.elements;
12469 for(var i = 0, len = els.length; i < len; i++){
12470 if(fn.call(scope || els[i], els[i], this, i) === false) {
12478 * Returns the Element object at the specified index
12479 * @param {Number} index
12480 * @return {Roo.Element}
12482 item : function(index){
12483 return this.elements[index] || null;
12487 * Returns the first Element
12488 * @return {Roo.Element}
12490 first : function(){
12491 return this.item(0);
12495 * Returns the last Element
12496 * @return {Roo.Element}
12499 return this.item(this.elements.length-1);
12503 * Returns the number of elements in this composite
12506 getCount : function(){
12507 return this.elements.length;
12511 * Returns true if this composite contains the passed element
12514 contains : function(el){
12515 return this.indexOf(el) !== -1;
12519 * Returns true if this composite contains the passed element
12522 indexOf : function(el){
12523 return this.elements.indexOf(Roo.get(el));
12528 * Removes the specified element(s).
12529 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12530 * or an array of any of those.
12531 * @param {Boolean} removeDom (optional) True to also remove the element from the document
12532 * @return {CompositeElement} this
12534 removeElement : function(el, removeDom){
12535 if(el instanceof Array){
12536 for(var i = 0, len = el.length; i < len; i++){
12537 this.removeElement(el[i]);
12541 var index = typeof el == 'number' ? el : this.indexOf(el);
12544 var d = this.elements[index];
12548 d.parentNode.removeChild(d);
12551 this.elements.splice(index, 1);
12557 * Replaces the specified element with the passed element.
12558 * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12560 * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12561 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12562 * @return {CompositeElement} this
12564 replaceElement : function(el, replacement, domReplace){
12565 var index = typeof el == 'number' ? el : this.indexOf(el);
12568 this.elements[index].replaceWith(replacement);
12570 this.elements.splice(index, 1, Roo.get(replacement))
12577 * Removes all elements.
12579 clear : function(){
12580 this.elements = [];
12584 Roo.CompositeElement.createCall = function(proto, fnName){
12585 if(!proto[fnName]){
12586 proto[fnName] = function(){
12587 return this.invoke(fnName, arguments);
12591 for(var fnName in Roo.Element.prototype){
12592 if(typeof Roo.Element.prototype[fnName] == "function"){
12593 Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12599 * Ext JS Library 1.1.1
12600 * Copyright(c) 2006-2007, Ext JS, LLC.
12602 * Originally Released Under LGPL - original licence link has changed is not relivant.
12605 * <script type="text/javascript">
12609 * @class Roo.CompositeElementLite
12610 * @extends Roo.CompositeElement
12611 * Flyweight composite class. Reuses the same Roo.Element for element operations.
12613 var els = Roo.select("#some-el div.some-class");
12614 // or select directly from an existing element
12615 var el = Roo.get('some-el');
12616 el.select('div.some-class');
12618 els.setWidth(100); // all elements become 100 width
12619 els.hide(true); // all elements fade out and hide
12621 els.setWidth(100).hide(true);
12622 </code></pre><br><br>
12623 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12624 * actions will be performed on all the elements in this collection.</b>
12626 Roo.CompositeElementLite = function(els){
12627 Roo.CompositeElementLite.superclass.constructor.call(this, els);
12628 this.el = new Roo.Element.Flyweight();
12630 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12631 addElements : function(els){
12633 if(els instanceof Array){
12634 this.elements = this.elements.concat(els);
12636 var yels = this.elements;
12637 var index = yels.length-1;
12638 for(var i = 0, len = els.length; i < len; i++) {
12639 yels[++index] = els[i];
12645 invoke : function(fn, args){
12646 var els = this.elements;
12648 for(var i = 0, len = els.length; i < len; i++) {
12650 Roo.Element.prototype[fn].apply(el, args);
12655 * Returns a flyweight Element of the dom element object at the specified index
12656 * @param {Number} index
12657 * @return {Roo.Element}
12659 item : function(index){
12660 if(!this.elements[index]){
12663 this.el.dom = this.elements[index];
12667 // fixes scope with flyweight
12668 addListener : function(eventName, handler, scope, opt){
12669 var els = this.elements;
12670 for(var i = 0, len = els.length; i < len; i++) {
12671 Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12677 * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12678 * passed is the flyweight (shared) Roo.Element instance, so if you require a
12679 * a reference to the dom node, use el.dom.</b>
12680 * @param {Function} fn The function to call
12681 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12682 * @return {CompositeElement} this
12684 each : function(fn, scope){
12685 var els = this.elements;
12687 for(var i = 0, len = els.length; i < len; i++){
12689 if(fn.call(scope || el, el, this, i) === false){
12696 indexOf : function(el){
12697 return this.elements.indexOf(Roo.getDom(el));
12700 replaceElement : function(el, replacement, domReplace){
12701 var index = typeof el == 'number' ? el : this.indexOf(el);
12703 replacement = Roo.getDom(replacement);
12705 var d = this.elements[index];
12706 d.parentNode.insertBefore(replacement, d);
12707 d.parentNode.removeChild(d);
12709 this.elements.splice(index, 1, replacement);
12714 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12718 * Ext JS Library 1.1.1
12719 * Copyright(c) 2006-2007, Ext JS, LLC.
12721 * Originally Released Under LGPL - original licence link has changed is not relivant.
12724 * <script type="text/javascript">
12730 * @class Roo.data.Connection
12731 * @extends Roo.util.Observable
12732 * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12733 * either to a configured URL, or to a URL specified at request time.
12735 * Requests made by this class are asynchronous, and will return immediately. No data from
12736 * the server will be available to the statement immediately following the {@link #request} call.
12737 * To process returned data, use a callback in the request options object, or an event listener.
12739 * Note: If you are doing a file upload, you will not get a normal response object sent back to
12740 * your callback or event handler. Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12741 * The response object is created using the innerHTML of the IFRAME's document as the responseText
12742 * property and, if present, the IFRAME's XML document as the responseXML property.
12744 * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12745 * that it be placed either inside a <textarea> in an HTML document and retrieved from the responseText
12746 * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12747 * standard DOM methods.
12749 * @param {Object} config a configuration object.
12751 Roo.data.Connection = function(config){
12752 Roo.apply(this, config);
12755 * @event beforerequest
12756 * Fires before a network request is made to retrieve a data object.
12757 * @param {Connection} conn This Connection object.
12758 * @param {Object} options The options config object passed to the {@link #request} method.
12760 "beforerequest" : true,
12762 * @event requestcomplete
12763 * Fires if the request was successfully completed.
12764 * @param {Connection} conn This Connection object.
12765 * @param {Object} response The XHR object containing the response data.
12766 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12767 * @param {Object} options The options config object passed to the {@link #request} method.
12769 "requestcomplete" : true,
12771 * @event requestexception
12772 * Fires if an error HTTP status was returned from the server.
12773 * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12774 * @param {Connection} conn This Connection object.
12775 * @param {Object} response The XHR object containing the response data.
12776 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12777 * @param {Object} options The options config object passed to the {@link #request} method.
12779 "requestexception" : true
12781 Roo.data.Connection.superclass.constructor.call(this);
12784 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12786 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12789 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12790 * extra parameters to each request made by this object. (defaults to undefined)
12793 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12794 * to each request made by this object. (defaults to undefined)
12797 * @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)
12800 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12804 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12810 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12813 disableCaching: true,
12816 * Sends an HTTP request to a remote server.
12817 * @param {Object} options An object which may contain the following properties:<ul>
12818 * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12819 * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12820 * request, a url encoded string or a function to call to get either.</li>
12821 * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12822 * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12823 * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12824 * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12825 * <li>options {Object} The parameter to the request call.</li>
12826 * <li>success {Boolean} True if the request succeeded.</li>
12827 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12829 * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12830 * The callback is passed the following parameters:<ul>
12831 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12832 * <li>options {Object} The parameter to the request call.</li>
12834 * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12835 * The callback is passed the following parameters:<ul>
12836 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12837 * <li>options {Object} The parameter to the request call.</li>
12839 * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12840 * for the callback function. Defaults to the browser window.</li>
12841 * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12842 * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12843 * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12844 * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12845 * params for the post data. Any params will be appended to the URL.</li>
12846 * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12848 * @return {Number} transactionId
12850 request : function(o){
12851 if(this.fireEvent("beforerequest", this, o) !== false){
12854 if(typeof p == "function"){
12855 p = p.call(o.scope||window, o);
12857 if(typeof p == "object"){
12858 p = Roo.urlEncode(o.params);
12860 if(this.extraParams){
12861 var extras = Roo.urlEncode(this.extraParams);
12862 p = p ? (p + '&' + extras) : extras;
12865 var url = o.url || this.url;
12866 if(typeof url == 'function'){
12867 url = url.call(o.scope||window, o);
12871 var form = Roo.getDom(o.form);
12872 url = url || form.action;
12874 var enctype = form.getAttribute("enctype");
12877 return this.doFormDataUpload(o, url);
12880 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12881 return this.doFormUpload(o, p, url);
12883 var f = Roo.lib.Ajax.serializeForm(form);
12884 p = p ? (p + '&' + f) : f;
12887 if (!o.form && o.formData) {
12888 o.formData = o.formData === true ? new FormData() : o.formData;
12889 for (var k in o.params) {
12890 o.formData.append(k,o.params[k]);
12893 return this.doFormDataUpload(o, url);
12897 var hs = o.headers;
12898 if(this.defaultHeaders){
12899 hs = Roo.apply(hs || {}, this.defaultHeaders);
12906 success: this.handleResponse,
12907 failure: this.handleFailure,
12909 argument: {options: o},
12910 timeout : o.timeout || this.timeout
12913 var method = o.method||this.method||(p ? "POST" : "GET");
12915 if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12916 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12919 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12923 }else if(this.autoAbort !== false){
12927 if((method == 'GET' && p) || o.xmlData){
12928 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12931 Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12932 this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12933 Roo.lib.Ajax.useDefaultHeader == true;
12934 return this.transId;
12936 Roo.callback(o.callback, o.scope, [o, null, null]);
12942 * Determine whether this object has a request outstanding.
12943 * @param {Number} transactionId (Optional) defaults to the last transaction
12944 * @return {Boolean} True if there is an outstanding request.
12946 isLoading : function(transId){
12948 return Roo.lib.Ajax.isCallInProgress(transId);
12950 return this.transId ? true : false;
12955 * Aborts any outstanding request.
12956 * @param {Number} transactionId (Optional) defaults to the last transaction
12958 abort : function(transId){
12959 if(transId || this.isLoading()){
12960 Roo.lib.Ajax.abort(transId || this.transId);
12965 handleResponse : function(response){
12966 this.transId = false;
12967 var options = response.argument.options;
12968 response.argument = options ? options.argument : null;
12969 this.fireEvent("requestcomplete", this, response, options);
12970 Roo.callback(options.success, options.scope, [response, options]);
12971 Roo.callback(options.callback, options.scope, [options, true, response]);
12975 handleFailure : function(response, e){
12976 this.transId = false;
12977 var options = response.argument.options;
12978 response.argument = options ? options.argument : null;
12979 this.fireEvent("requestexception", this, response, options, e);
12980 Roo.callback(options.failure, options.scope, [response, options]);
12981 Roo.callback(options.callback, options.scope, [options, false, response]);
12985 doFormUpload : function(o, ps, url){
12987 var frame = document.createElement('iframe');
12990 frame.className = 'x-hidden';
12992 frame.src = Roo.SSL_SECURE_URL;
12994 document.body.appendChild(frame);
12997 document.frames[id].name = id;
13000 var form = Roo.getDom(o.form);
13002 form.method = 'POST';
13003 form.enctype = form.encoding = 'multipart/form-data';
13009 if(ps){ // add dynamic params
13011 ps = Roo.urlDecode(ps, false);
13013 if(ps.hasOwnProperty(k)){
13014 hd = document.createElement('input');
13015 hd.type = 'hidden';
13018 form.appendChild(hd);
13025 var r = { // bogus response object
13030 r.argument = o ? o.argument : null;
13035 doc = frame.contentWindow.document;
13037 doc = (frame.contentDocument || window.frames[id].document);
13039 if(doc && doc.body){
13040 r.responseText = doc.body.innerHTML;
13042 if(doc && doc.XMLDocument){
13043 r.responseXML = doc.XMLDocument;
13045 r.responseXML = doc;
13052 Roo.EventManager.removeListener(frame, 'load', cb, this);
13054 this.fireEvent("requestcomplete", this, r, o);
13055 Roo.callback(o.success, o.scope, [r, o]);
13056 Roo.callback(o.callback, o.scope, [o, true, r]);
13058 setTimeout(function(){document.body.removeChild(frame);}, 100);
13061 Roo.EventManager.on(frame, 'load', cb, this);
13064 if(hiddens){ // remove dynamic params
13065 for(var i = 0, len = hiddens.length; i < len; i++){
13066 form.removeChild(hiddens[i]);
13070 // this is a 'formdata version???'
13073 doFormDataUpload : function(o, url)
13077 var form = Roo.getDom(o.form);
13078 form.enctype = form.encoding = 'multipart/form-data';
13079 formData = o.formData === true ? new FormData(form) : o.formData;
13081 formData = o.formData === true ? new FormData() : o.formData;
13086 success: this.handleResponse,
13087 failure: this.handleFailure,
13089 argument: {options: o},
13090 timeout : o.timeout || this.timeout
13093 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13097 }else if(this.autoAbort !== false){
13101 //Roo.lib.Ajax.defaultPostHeader = null;
13102 Roo.lib.Ajax.useDefaultHeader = false;
13103 this.transId = Roo.lib.Ajax.request( "POST", url, cb, formData, o);
13104 Roo.lib.Ajax.useDefaultHeader = true;
13112 * Ext JS Library 1.1.1
13113 * Copyright(c) 2006-2007, Ext JS, LLC.
13115 * Originally Released Under LGPL - original licence link has changed is not relivant.
13118 * <script type="text/javascript">
13122 * Global Ajax request class.
13125 * @extends Roo.data.Connection
13128 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
13129 * @cfg {Object} extraParams An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13130 * @cfg {Object} defaultHeaders An object containing request headers which are added to each request made by this object. (defaults to undefined)
13131 * @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)
13132 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13133 * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13134 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13136 Roo.Ajax = new Roo.data.Connection({
13145 * Serialize the passed form into a url encoded string
13147 * @param {String/HTMLElement} form
13150 serializeForm : function(form){
13151 return Roo.lib.Ajax.serializeForm(form);
13155 * Ext JS Library 1.1.1
13156 * Copyright(c) 2006-2007, Ext JS, LLC.
13158 * Originally Released Under LGPL - original licence link has changed is not relivant.
13161 * <script type="text/javascript">
13166 * @class Roo.UpdateManager
13167 * @extends Roo.util.Observable
13168 * Provides AJAX-style update for Element object.<br><br>
13171 * // Get it from a Roo.Element object
13172 * var el = Roo.get("foo");
13173 * var mgr = el.getUpdateManager();
13174 * mgr.update("http://myserver.com/index.php", "param1=1&param2=2");
13176 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13178 * // or directly (returns the same UpdateManager instance)
13179 * var mgr = new Roo.UpdateManager("myElementId");
13180 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13181 * mgr.on("update", myFcnNeedsToKnow);
13183 // short handed call directly from the element object
13184 Roo.get("foo").load({
13188 text: "Loading Foo..."
13192 * Create new UpdateManager directly.
13193 * @param {String/HTMLElement/Roo.Element} el The element to update
13194 * @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).
13196 Roo.UpdateManager = function(el, forceNew){
13198 if(!forceNew && el.updateManager){
13199 return el.updateManager;
13202 * The Element object
13203 * @type Roo.Element
13207 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13210 this.defaultUrl = null;
13214 * @event beforeupdate
13215 * Fired before an update is made, return false from your handler and the update is cancelled.
13216 * @param {Roo.Element} el
13217 * @param {String/Object/Function} url
13218 * @param {String/Object} params
13220 "beforeupdate": true,
13223 * Fired after successful update is made.
13224 * @param {Roo.Element} el
13225 * @param {Object} oResponseObject The response Object
13230 * Fired on update failure.
13231 * @param {Roo.Element} el
13232 * @param {Object} oResponseObject The response Object
13236 var d = Roo.UpdateManager.defaults;
13238 * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13241 this.sslBlankUrl = d.sslBlankUrl;
13243 * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13246 this.disableCaching = d.disableCaching;
13248 * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '<div class="loading-indicator">Loading...</div>').
13251 this.indicatorText = d.indicatorText;
13253 * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13256 this.showLoadIndicator = d.showLoadIndicator;
13258 * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13261 this.timeout = d.timeout;
13264 * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13267 this.loadScripts = d.loadScripts;
13270 * Transaction object of current executing transaction
13272 this.transaction = null;
13277 this.autoRefreshProcId = null;
13279 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13282 this.refreshDelegate = this.refresh.createDelegate(this);
13284 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13287 this.updateDelegate = this.update.createDelegate(this);
13289 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13292 this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13296 this.successDelegate = this.processSuccess.createDelegate(this);
13300 this.failureDelegate = this.processFailure.createDelegate(this);
13302 if(!this.renderer){
13304 * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13306 this.renderer = new Roo.UpdateManager.BasicRenderer();
13309 Roo.UpdateManager.superclass.constructor.call(this);
13312 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13314 * Get the Element this UpdateManager is bound to
13315 * @return {Roo.Element} The element
13317 getEl : function(){
13321 * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13322 * @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:
13325 url: "your-url.php",<br/>
13326 params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13327 callback: yourFunction,<br/>
13328 scope: yourObject, //(optional scope) <br/>
13329 discardUrl: false, <br/>
13330 nocache: false,<br/>
13331 text: "Loading...",<br/>
13333 scripts: false<br/>
13336 * The only required property is url. The optional properties nocache, text and scripts
13337 * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13338 * @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}
13339 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13340 * @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.
13342 update : function(url, params, callback, discardUrl){
13343 if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13344 var method = this.method,
13346 if(typeof url == "object"){ // must be config object
13349 params = params || cfg.params;
13350 callback = callback || cfg.callback;
13351 discardUrl = discardUrl || cfg.discardUrl;
13352 if(callback && cfg.scope){
13353 callback = callback.createDelegate(cfg.scope);
13355 if(typeof cfg.method != "undefined"){method = cfg.method;};
13356 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13357 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13358 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13359 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13361 this.showLoading();
13363 this.defaultUrl = url;
13365 if(typeof url == "function"){
13366 url = url.call(this);
13369 method = method || (params ? "POST" : "GET");
13370 if(method == "GET"){
13371 url = this.prepareUrl(url);
13374 var o = Roo.apply(cfg ||{}, {
13377 success: this.successDelegate,
13378 failure: this.failureDelegate,
13379 callback: undefined,
13380 timeout: (this.timeout*1000),
13381 argument: {"url": url, "form": null, "callback": callback, "params": params}
13383 Roo.log("updated manager called with timeout of " + o.timeout);
13384 this.transaction = Roo.Ajax.request(o);
13389 * 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.
13390 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13391 * @param {String/HTMLElement} form The form Id or form element
13392 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13393 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13394 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13396 formUpdate : function(form, url, reset, callback){
13397 if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13398 if(typeof url == "function"){
13399 url = url.call(this);
13401 form = Roo.getDom(form);
13402 this.transaction = Roo.Ajax.request({
13405 success: this.successDelegate,
13406 failure: this.failureDelegate,
13407 timeout: (this.timeout*1000),
13408 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13410 this.showLoading.defer(1, this);
13415 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13416 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13418 refresh : function(callback){
13419 if(this.defaultUrl == null){
13422 this.update(this.defaultUrl, null, callback, true);
13426 * Set this element to auto refresh.
13427 * @param {Number} interval How often to update (in seconds).
13428 * @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)
13429 * @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}
13430 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13431 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13433 startAutoRefresh : function(interval, url, params, callback, refreshNow){
13435 this.update(url || this.defaultUrl, params, callback, true);
13437 if(this.autoRefreshProcId){
13438 clearInterval(this.autoRefreshProcId);
13440 this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13444 * Stop auto refresh on this element.
13446 stopAutoRefresh : function(){
13447 if(this.autoRefreshProcId){
13448 clearInterval(this.autoRefreshProcId);
13449 delete this.autoRefreshProcId;
13453 isAutoRefreshing : function(){
13454 return this.autoRefreshProcId ? true : false;
13457 * Called to update the element to "Loading" state. Override to perform custom action.
13459 showLoading : function(){
13460 if(this.showLoadIndicator){
13461 this.el.update(this.indicatorText);
13466 * Adds unique parameter to query string if disableCaching = true
13469 prepareUrl : function(url){
13470 if(this.disableCaching){
13471 var append = "_dc=" + (new Date().getTime());
13472 if(url.indexOf("?") !== -1){
13473 url += "&" + append;
13475 url += "?" + append;
13484 processSuccess : function(response){
13485 this.transaction = null;
13486 if(response.argument.form && response.argument.reset){
13487 try{ // put in try/catch since some older FF releases had problems with this
13488 response.argument.form.reset();
13491 if(this.loadScripts){
13492 this.renderer.render(this.el, response, this,
13493 this.updateComplete.createDelegate(this, [response]));
13495 this.renderer.render(this.el, response, this);
13496 this.updateComplete(response);
13500 updateComplete : function(response){
13501 this.fireEvent("update", this.el, response);
13502 if(typeof response.argument.callback == "function"){
13503 response.argument.callback(this.el, true, response);
13510 processFailure : function(response){
13511 this.transaction = null;
13512 this.fireEvent("failure", this.el, response);
13513 if(typeof response.argument.callback == "function"){
13514 response.argument.callback(this.el, false, response);
13519 * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13520 * @param {Object} renderer The object implementing the render() method
13522 setRenderer : function(renderer){
13523 this.renderer = renderer;
13526 getRenderer : function(){
13527 return this.renderer;
13531 * Set the defaultUrl used for updates
13532 * @param {String/Function} defaultUrl The url or a function to call to get the url
13534 setDefaultUrl : function(defaultUrl){
13535 this.defaultUrl = defaultUrl;
13539 * Aborts the executing transaction
13541 abort : function(){
13542 if(this.transaction){
13543 Roo.Ajax.abort(this.transaction);
13548 * Returns true if an update is in progress
13549 * @return {Boolean}
13551 isUpdating : function(){
13552 if(this.transaction){
13553 return Roo.Ajax.isLoading(this.transaction);
13560 * @class Roo.UpdateManager.defaults
13561 * @static (not really - but it helps the doc tool)
13562 * The defaults collection enables customizing the default properties of UpdateManager
13564 Roo.UpdateManager.defaults = {
13566 * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13572 * True to process scripts by default (Defaults to false).
13575 loadScripts : false,
13578 * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13581 sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13583 * Whether to append unique parameter on get request to disable caching (Defaults to false).
13586 disableCaching : false,
13588 * Whether to show indicatorText when loading (Defaults to true).
13591 showLoadIndicator : true,
13593 * Text for loading indicator (Defaults to '<div class="loading-indicator">Loading...</div>').
13596 indicatorText : '<div class="loading-indicator">Loading...</div>'
13600 * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13602 * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13603 * @param {String/HTMLElement/Roo.Element} el The element to update
13604 * @param {String} url The url
13605 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13606 * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13609 * @member Roo.UpdateManager
13611 Roo.UpdateManager.updateElement = function(el, url, params, options){
13612 var um = Roo.get(el, true).getUpdateManager();
13613 Roo.apply(um, options);
13614 um.update(url, params, options ? options.callback : null);
13616 // alias for backwards compat
13617 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13619 * @class Roo.UpdateManager.BasicRenderer
13620 * Default Content renderer. Updates the elements innerHTML with the responseText.
13622 Roo.UpdateManager.BasicRenderer = function(){};
13624 Roo.UpdateManager.BasicRenderer.prototype = {
13626 * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13627 * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13628 * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13629 * @param {Roo.Element} el The element being rendered
13630 * @param {Object} response The YUI Connect response object
13631 * @param {UpdateManager} updateManager The calling update manager
13632 * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13634 render : function(el, response, updateManager, callback){
13635 el.update(response.responseText, updateManager.loadScripts, callback);
13641 * (c)) Alan Knowles
13647 * @class Roo.DomTemplate
13648 * @extends Roo.Template
13649 * An effort at a dom based template engine..
13651 * Similar to XTemplate, except it uses dom parsing to create the template..
13653 * Supported features:
13658 {a_variable} - output encoded.
13659 {a_variable.format:("Y-m-d")} - call a method on the variable
13660 {a_variable:raw} - unencoded output
13661 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13662 {a_variable:this.method_on_template(...)} - call a method on the template object.
13667 <div roo-for="a_variable or condition.."></div>
13668 <div roo-if="a_variable or condition"></div>
13669 <div roo-exec="some javascript"></div>
13670 <div roo-name="named_template"></div>
13675 Roo.DomTemplate = function()
13677 Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13684 Roo.extend(Roo.DomTemplate, Roo.Template, {
13686 * id counter for sub templates.
13690 * flag to indicate if dom parser is inside a pre,
13691 * it will strip whitespace if not.
13696 * The various sub templates
13704 * basic tag replacing syntax
13707 * // you can fake an object call by doing this
13711 re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13712 //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13714 iterChild : function (node, method) {
13716 var oldPre = this.inPre;
13717 if (node.tagName == 'PRE') {
13720 for( var i = 0; i < node.childNodes.length; i++) {
13721 method.call(this, node.childNodes[i]);
13723 this.inPre = oldPre;
13729 * compile the template
13731 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13734 compile: function()
13738 // covert the html into DOM...
13742 doc = document.implementation.createHTMLDocument("");
13743 doc.documentElement.innerHTML = this.html ;
13744 div = doc.documentElement;
13746 // old IE... - nasty -- it causes all sorts of issues.. with
13747 // images getting pulled from server..
13748 div = document.createElement('div');
13749 div.innerHTML = this.html;
13751 //doc.documentElement.innerHTML = htmlBody
13757 this.iterChild(div, function(n) {_t.compileNode(n, true); });
13759 var tpls = this.tpls;
13761 // create a top level template from the snippet..
13763 //Roo.log(div.innerHTML);
13770 body : div.innerHTML,
13783 Roo.each(tpls, function(tp){
13784 this.compileTpl(tp);
13785 this.tpls[tp.id] = tp;
13788 this.master = tpls[0];
13794 compileNode : function(node, istop) {
13799 // skip anything not a tag..
13800 if (node.nodeType != 1) {
13801 if (node.nodeType == 3 && !this.inPre) {
13802 // reduce white space..
13803 node.nodeValue = node.nodeValue.replace(/\s+/g, ' ');
13826 case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13827 case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13828 case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13829 case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13835 // just itterate children..
13836 this.iterChild(node,this.compileNode);
13839 tpl.uid = this.id++;
13840 tpl.value = node.getAttribute('roo-' + tpl.attr);
13841 node.removeAttribute('roo-'+ tpl.attr);
13842 if (tpl.attr != 'name') {
13843 var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13844 node.parentNode.replaceChild(placeholder, node);
13847 var placeholder = document.createElement('span');
13848 placeholder.className = 'roo-tpl-' + tpl.value;
13849 node.parentNode.replaceChild(placeholder, node);
13852 // parent now sees '{domtplXXXX}
13853 this.iterChild(node,this.compileNode);
13855 // we should now have node body...
13856 var div = document.createElement('div');
13857 div.appendChild(node);
13859 // this has the unfortunate side effect of converting tagged attributes
13860 // eg. href="{...}" into %7C...%7D
13861 // this has been fixed by searching for those combo's although it's a bit hacky..
13864 tpl.body = div.innerHTML;
13871 switch (tpl.value) {
13872 case '.': tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13873 case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13874 default: tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13879 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13883 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13887 tpl.id = tpl.value; // replace non characters???
13893 this.tpls.push(tpl);
13903 * Compile a segment of the template into a 'sub-template'
13909 compileTpl : function(tpl)
13911 var fm = Roo.util.Format;
13912 var useF = this.disableFormats !== true;
13914 var sep = Roo.isGecko ? "+\n" : ",\n";
13916 var undef = function(str) {
13917 Roo.debug && Roo.log("Property not found :" + str);
13921 //Roo.log(tpl.body);
13925 var fn = function(m, lbrace, name, format, args)
13928 //Roo.log(arguments);
13929 args = args ? args.replace(/\\'/g,"'") : args;
13930 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13931 if (typeof(format) == 'undefined') {
13932 format = 'htmlEncode';
13934 if (format == 'raw' ) {
13938 if(name.substr(0, 6) == 'domtpl'){
13939 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13942 // build an array of options to determine if value is undefined..
13944 // basically get 'xxxx.yyyy' then do
13945 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13946 // (function () { Roo.log("Property not found"); return ''; })() :
13951 Roo.each(name.split('.'), function(st) {
13952 lookfor += (lookfor.length ? '.': '') + st;
13953 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
13956 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13959 if(format && useF){
13961 args = args ? ',' + args : "";
13963 if(format.substr(0, 5) != "this."){
13964 format = "fm." + format + '(';
13966 format = 'this.call("'+ format.substr(5) + '", ';
13970 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
13973 if (args && args.length) {
13974 // called with xxyx.yuu:(test,test)
13976 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
13978 // raw.. - :raw modifier..
13979 return "'"+ sep + udef_st + name + ")"+sep+"'";
13983 // branched to use + in gecko and [].join() in others
13985 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
13986 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13989 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
13990 body.push(tpl.body.replace(/(\r\n|\n)/g,
13991 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13992 body.push("'].join('');};};");
13993 body = body.join('');
13996 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13998 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
14005 * same as applyTemplate, except it's done to one of the subTemplates
14006 * when using named templates, you can do:
14008 * var str = pl.applySubTemplate('your-name', values);
14011 * @param {Number} id of the template
14012 * @param {Object} values to apply to template
14013 * @param {Object} parent (normaly the instance of this object)
14015 applySubTemplate : function(id, values, parent)
14019 var t = this.tpls[id];
14023 if(t.ifCall && !t.ifCall.call(this, values, parent)){
14024 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14028 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14035 if(t.execCall && t.execCall.call(this, values, parent)){
14039 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14045 var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14046 parent = t.target ? values : parent;
14047 if(t.forCall && vs instanceof Array){
14049 for(var i = 0, len = vs.length; i < len; i++){
14051 buf[buf.length] = t.compiled.call(this, vs[i], parent);
14053 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14055 //Roo.log(t.compiled);
14059 return buf.join('');
14062 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14067 return t.compiled.call(this, vs, parent);
14069 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14071 //Roo.log(t.compiled);
14079 applyTemplate : function(values){
14080 return this.master.compiled.call(this, values, {});
14081 //var s = this.subs;
14084 apply : function(){
14085 return this.applyTemplate.apply(this, arguments);
14090 Roo.DomTemplate.from = function(el){
14091 el = Roo.getDom(el);
14092 return new Roo.Domtemplate(el.value || el.innerHTML);
14095 * Ext JS Library 1.1.1
14096 * Copyright(c) 2006-2007, Ext JS, LLC.
14098 * Originally Released Under LGPL - original licence link has changed is not relivant.
14101 * <script type="text/javascript">
14105 * @class Roo.util.DelayedTask
14106 * Provides a convenient method of performing setTimeout where a new
14107 * timeout cancels the old timeout. An example would be performing validation on a keypress.
14108 * You can use this class to buffer
14109 * the keypress events for a certain number of milliseconds, and perform only if they stop
14110 * for that amount of time.
14111 * @constructor The parameters to this constructor serve as defaults and are not required.
14112 * @param {Function} fn (optional) The default function to timeout
14113 * @param {Object} scope (optional) The default scope of that timeout
14114 * @param {Array} args (optional) The default Array of arguments
14116 Roo.util.DelayedTask = function(fn, scope, args){
14117 var id = null, d, t;
14119 var call = function(){
14120 var now = new Date().getTime();
14124 fn.apply(scope, args || []);
14128 * Cancels any pending timeout and queues a new one
14129 * @param {Number} delay The milliseconds to delay
14130 * @param {Function} newFn (optional) Overrides function passed to constructor
14131 * @param {Object} newScope (optional) Overrides scope passed to constructor
14132 * @param {Array} newArgs (optional) Overrides args passed to constructor
14134 this.delay = function(delay, newFn, newScope, newArgs){
14135 if(id && delay != d){
14139 t = new Date().getTime();
14141 scope = newScope || scope;
14142 args = newArgs || args;
14144 id = setInterval(call, d);
14149 * Cancel the last queued timeout
14151 this.cancel = function(){
14159 * Ext JS Library 1.1.1
14160 * Copyright(c) 2006-2007, Ext JS, LLC.
14162 * Originally Released Under LGPL - original licence link has changed is not relivant.
14165 * <script type="text/javascript">
14168 * @class Roo.util.TaskRunner
14169 * Manage background tasks - not sure why this is better that setInterval?
14174 Roo.util.TaskRunner = function(interval){
14175 interval = interval || 10;
14176 var tasks = [], removeQueue = [];
14178 var running = false;
14180 var stopThread = function(){
14186 var startThread = function(){
14189 id = setInterval(runTasks, interval);
14193 var removeTask = function(task){
14194 removeQueue.push(task);
14200 var runTasks = function(){
14201 if(removeQueue.length > 0){
14202 for(var i = 0, len = removeQueue.length; i < len; i++){
14203 tasks.remove(removeQueue[i]);
14206 if(tasks.length < 1){
14211 var now = new Date().getTime();
14212 for(var i = 0, len = tasks.length; i < len; ++i){
14214 var itime = now - t.taskRunTime;
14215 if(t.interval <= itime){
14216 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14217 t.taskRunTime = now;
14218 if(rt === false || t.taskRunCount === t.repeat){
14223 if(t.duration && t.duration <= (now - t.taskStartTime)){
14230 * Queues a new task.
14231 * @param {Object} task
14233 * Task property : interval = how frequent to run.
14234 * Task object should implement
14236 * Task object may implement
14237 * function onStop()
14239 this.start = function(task){
14241 task.taskStartTime = new Date().getTime();
14242 task.taskRunTime = 0;
14243 task.taskRunCount = 0;
14249 * @param {Object} task
14251 this.stop = function(task){
14258 this.stopAll = function(){
14260 for(var i = 0, len = tasks.length; i < len; i++){
14261 if(tasks[i].onStop){
14270 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14272 * Ext JS Library 1.1.1
14273 * Copyright(c) 2006-2007, Ext JS, LLC.
14275 * Originally Released Under LGPL - original licence link has changed is not relivant.
14278 * <script type="text/javascript">
14283 * @class Roo.util.MixedCollection
14284 * @extends Roo.util.Observable
14285 * A Collection class that maintains both numeric indexes and keys and exposes events.
14287 * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14288 * collection (defaults to false)
14289 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14290 * and return the key value for that item. This is used when available to look up the key on items that
14291 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
14292 * equivalent to providing an implementation for the {@link #getKey} method.
14294 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14302 * Fires when the collection is cleared.
14307 * Fires when an item is added to the collection.
14308 * @param {Number} index The index at which the item was added.
14309 * @param {Object} o The item added.
14310 * @param {String} key The key associated with the added item.
14315 * Fires when an item is replaced in the collection.
14316 * @param {String} key he key associated with the new added.
14317 * @param {Object} old The item being replaced.
14318 * @param {Object} new The new item.
14323 * Fires when an item is removed from the collection.
14324 * @param {Object} o The item being removed.
14325 * @param {String} key (optional) The key associated with the removed item.
14330 this.allowFunctions = allowFunctions === true;
14332 this.getKey = keyFn;
14334 Roo.util.MixedCollection.superclass.constructor.call(this);
14337 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14338 allowFunctions : false,
14341 * Adds an item to the collection.
14342 * @param {String} key The key to associate with the item
14343 * @param {Object} o The item to add.
14344 * @return {Object} The item added.
14346 add : function(key, o){
14347 if(arguments.length == 1){
14349 key = this.getKey(o);
14351 if(typeof key == "undefined" || key === null){
14353 this.items.push(o);
14354 this.keys.push(null);
14356 var old = this.map[key];
14358 return this.replace(key, o);
14361 this.items.push(o);
14363 this.keys.push(key);
14365 this.fireEvent("add", this.length-1, o, key);
14370 * MixedCollection has a generic way to fetch keys if you implement getKey.
14373 var mc = new Roo.util.MixedCollection();
14374 mc.add(someEl.dom.id, someEl);
14375 mc.add(otherEl.dom.id, otherEl);
14379 var mc = new Roo.util.MixedCollection();
14380 mc.getKey = function(el){
14386 // or via the constructor
14387 var mc = new Roo.util.MixedCollection(false, function(el){
14393 * @param o {Object} The item for which to find the key.
14394 * @return {Object} The key for the passed item.
14396 getKey : function(o){
14401 * Replaces an item in the collection.
14402 * @param {String} key The key associated with the item to replace, or the item to replace.
14403 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14404 * @return {Object} The new item.
14406 replace : function(key, o){
14407 if(arguments.length == 1){
14409 key = this.getKey(o);
14411 var old = this.item(key);
14412 if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14413 return this.add(key, o);
14415 var index = this.indexOfKey(key);
14416 this.items[index] = o;
14418 this.fireEvent("replace", key, old, o);
14423 * Adds all elements of an Array or an Object to the collection.
14424 * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14425 * an Array of values, each of which are added to the collection.
14427 addAll : function(objs){
14428 if(arguments.length > 1 || objs instanceof Array){
14429 var args = arguments.length > 1 ? arguments : objs;
14430 for(var i = 0, len = args.length; i < len; i++){
14434 for(var key in objs){
14435 if(this.allowFunctions || typeof objs[key] != "function"){
14436 this.add(key, objs[key]);
14443 * Executes the specified function once for every item in the collection, passing each
14444 * item as the first and only parameter. returning false from the function will stop the iteration.
14445 * @param {Function} fn The function to execute for each item.
14446 * @param {Object} scope (optional) The scope in which to execute the function.
14448 each : function(fn, scope){
14449 var items = [].concat(this.items); // each safe for removal
14450 for(var i = 0, len = items.length; i < len; i++){
14451 if(fn.call(scope || items[i], items[i], i, len) === false){
14458 * Executes the specified function once for every key in the collection, passing each
14459 * key, and its associated item as the first two parameters.
14460 * @param {Function} fn The function to execute for each item.
14461 * @param {Object} scope (optional) The scope in which to execute the function.
14463 eachKey : function(fn, scope){
14464 for(var i = 0, len = this.keys.length; i < len; i++){
14465 fn.call(scope || window, this.keys[i], this.items[i], i, len);
14470 * Returns the first item in the collection which elicits a true return value from the
14471 * passed selection function.
14472 * @param {Function} fn The selection function to execute for each item.
14473 * @param {Object} scope (optional) The scope in which to execute the function.
14474 * @return {Object} The first item in the collection which returned true from the selection function.
14476 find : function(fn, scope){
14477 for(var i = 0, len = this.items.length; i < len; i++){
14478 if(fn.call(scope || window, this.items[i], this.keys[i])){
14479 return this.items[i];
14486 * Inserts an item at the specified index in the collection.
14487 * @param {Number} index The index to insert the item at.
14488 * @param {String} key The key to associate with the new item, or the item itself.
14489 * @param {Object} o (optional) If the second parameter was a key, the new item.
14490 * @return {Object} The item inserted.
14492 insert : function(index, key, o){
14493 if(arguments.length == 2){
14495 key = this.getKey(o);
14497 if(index >= this.length){
14498 return this.add(key, o);
14501 this.items.splice(index, 0, o);
14502 if(typeof key != "undefined" && key != null){
14505 this.keys.splice(index, 0, key);
14506 this.fireEvent("add", index, o, key);
14511 * Removed an item from the collection.
14512 * @param {Object} o The item to remove.
14513 * @return {Object} The item removed.
14515 remove : function(o){
14516 return this.removeAt(this.indexOf(o));
14520 * Remove an item from a specified index in the collection.
14521 * @param {Number} index The index within the collection of the item to remove.
14523 removeAt : function(index){
14524 if(index < this.length && index >= 0){
14526 var o = this.items[index];
14527 this.items.splice(index, 1);
14528 var key = this.keys[index];
14529 if(typeof key != "undefined"){
14530 delete this.map[key];
14532 this.keys.splice(index, 1);
14533 this.fireEvent("remove", o, key);
14538 * Removed an item associated with the passed key fom the collection.
14539 * @param {String} key The key of the item to remove.
14541 removeKey : function(key){
14542 return this.removeAt(this.indexOfKey(key));
14546 * Returns the number of items in the collection.
14547 * @return {Number} the number of items in the collection.
14549 getCount : function(){
14550 return this.length;
14554 * Returns index within the collection of the passed Object.
14555 * @param {Object} o The item to find the index of.
14556 * @return {Number} index of the item.
14558 indexOf : function(o){
14559 if(!this.items.indexOf){
14560 for(var i = 0, len = this.items.length; i < len; i++){
14561 if(this.items[i] == o) {
14567 return this.items.indexOf(o);
14572 * Returns index within the collection of the passed key.
14573 * @param {String} key The key to find the index of.
14574 * @return {Number} index of the key.
14576 indexOfKey : function(key){
14577 if(!this.keys.indexOf){
14578 for(var i = 0, len = this.keys.length; i < len; i++){
14579 if(this.keys[i] == key) {
14585 return this.keys.indexOf(key);
14590 * Returns the item associated with the passed key OR index. Key has priority over index.
14591 * @param {String/Number} key The key or index of the item.
14592 * @return {Object} The item associated with the passed key.
14594 item : function(key){
14595 if (key === 'length') {
14598 var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14599 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14603 * Returns the item at the specified index.
14604 * @param {Number} index The index of the item.
14607 itemAt : function(index){
14608 return this.items[index];
14612 * Returns the item associated with the passed key.
14613 * @param {String/Number} key The key of the item.
14614 * @return {Object} The item associated with the passed key.
14616 key : function(key){
14617 return this.map[key];
14621 * Returns true if the collection contains the passed Object as an item.
14622 * @param {Object} o The Object to look for in the collection.
14623 * @return {Boolean} True if the collection contains the Object as an item.
14625 contains : function(o){
14626 return this.indexOf(o) != -1;
14630 * Returns true if the collection contains the passed Object as a key.
14631 * @param {String} key The key to look for in the collection.
14632 * @return {Boolean} True if the collection contains the Object as a key.
14634 containsKey : function(key){
14635 return typeof this.map[key] != "undefined";
14639 * Removes all items from the collection.
14641 clear : function(){
14646 this.fireEvent("clear");
14650 * Returns the first item in the collection.
14651 * @return {Object} the first item in the collection..
14653 first : function(){
14654 return this.items[0];
14658 * Returns the last item in the collection.
14659 * @return {Object} the last item in the collection..
14662 return this.items[this.length-1];
14665 _sort : function(property, dir, fn){
14666 var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14667 fn = fn || function(a, b){
14670 var c = [], k = this.keys, items = this.items;
14671 for(var i = 0, len = items.length; i < len; i++){
14672 c[c.length] = {key: k[i], value: items[i], index: i};
14674 c.sort(function(a, b){
14675 var v = fn(a[property], b[property]) * dsc;
14677 v = (a.index < b.index ? -1 : 1);
14681 for(var i = 0, len = c.length; i < len; i++){
14682 items[i] = c[i].value;
14685 this.fireEvent("sort", this);
14689 * Sorts this collection with the passed comparison function
14690 * @param {String} direction (optional) "ASC" or "DESC"
14691 * @param {Function} fn (optional) comparison function
14693 sort : function(dir, fn){
14694 this._sort("value", dir, fn);
14698 * Sorts this collection by keys
14699 * @param {String} direction (optional) "ASC" or "DESC"
14700 * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14702 keySort : function(dir, fn){
14703 this._sort("key", dir, fn || function(a, b){
14704 return String(a).toUpperCase()-String(b).toUpperCase();
14709 * Returns a range of items in this collection
14710 * @param {Number} startIndex (optional) defaults to 0
14711 * @param {Number} endIndex (optional) default to the last item
14712 * @return {Array} An array of items
14714 getRange : function(start, end){
14715 var items = this.items;
14716 if(items.length < 1){
14719 start = start || 0;
14720 end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14723 for(var i = start; i <= end; i++) {
14724 r[r.length] = items[i];
14727 for(var i = start; i >= end; i--) {
14728 r[r.length] = items[i];
14735 * Filter the <i>objects</i> in this collection by a specific property.
14736 * Returns a new collection that has been filtered.
14737 * @param {String} property A property on your objects
14738 * @param {String/RegExp} value Either string that the property values
14739 * should start with or a RegExp to test against the property
14740 * @return {MixedCollection} The new filtered collection
14742 filter : function(property, value){
14743 if(!value.exec){ // not a regex
14744 value = String(value);
14745 if(value.length == 0){
14746 return this.clone();
14748 value = new RegExp("^" + Roo.escapeRe(value), "i");
14750 return this.filterBy(function(o){
14751 return o && value.test(o[property]);
14756 * Filter by a function. * Returns a new collection that has been filtered.
14757 * The passed function will be called with each
14758 * object in the collection. If the function returns true, the value is included
14759 * otherwise it is filtered.
14760 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14761 * @param {Object} scope (optional) The scope of the function (defaults to this)
14762 * @return {MixedCollection} The new filtered collection
14764 filterBy : function(fn, scope){
14765 var r = new Roo.util.MixedCollection();
14766 r.getKey = this.getKey;
14767 var k = this.keys, it = this.items;
14768 for(var i = 0, len = it.length; i < len; i++){
14769 if(fn.call(scope||this, it[i], k[i])){
14770 r.add(k[i], it[i]);
14777 * Creates a duplicate of this collection
14778 * @return {MixedCollection}
14780 clone : function(){
14781 var r = new Roo.util.MixedCollection();
14782 var k = this.keys, it = this.items;
14783 for(var i = 0, len = it.length; i < len; i++){
14784 r.add(k[i], it[i]);
14786 r.getKey = this.getKey;
14791 * Returns the item associated with the passed key or index.
14793 * @param {String/Number} key The key or index of the item.
14794 * @return {Object} The item associated with the passed key.
14796 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14798 * Ext JS Library 1.1.1
14799 * Copyright(c) 2006-2007, Ext JS, LLC.
14801 * Originally Released Under LGPL - original licence link has changed is not relivant.
14804 * <script type="text/javascript">
14807 * @class Roo.util.JSON
14808 * Modified version of Douglas Crockford"s json.js that doesn"t
14809 * mess with the Object prototype
14810 * http://www.json.org/js.html
14813 Roo.util.JSON = new (function(){
14814 var useHasOwn = {}.hasOwnProperty ? true : false;
14816 // crashes Safari in some instances
14817 //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14819 var pad = function(n) {
14820 return n < 10 ? "0" + n : n;
14833 var encodeString = function(s){
14834 if (/["\\\x00-\x1f]/.test(s)) {
14835 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14840 c = b.charCodeAt();
14842 Math.floor(c / 16).toString(16) +
14843 (c % 16).toString(16);
14846 return '"' + s + '"';
14849 var encodeArray = function(o){
14850 var a = ["["], b, i, l = o.length, v;
14851 for (i = 0; i < l; i += 1) {
14853 switch (typeof v) {
14862 a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14870 var encodeDate = function(o){
14871 return '"' + o.getFullYear() + "-" +
14872 pad(o.getMonth() + 1) + "-" +
14873 pad(o.getDate()) + "T" +
14874 pad(o.getHours()) + ":" +
14875 pad(o.getMinutes()) + ":" +
14876 pad(o.getSeconds()) + '"';
14880 * Encodes an Object, Array or other value
14881 * @param {Mixed} o The variable to encode
14882 * @return {String} The JSON string
14884 this.encode = function(o)
14886 // should this be extended to fully wrap stringify..
14888 if(typeof o == "undefined" || o === null){
14890 }else if(o instanceof Array){
14891 return encodeArray(o);
14892 }else if(o instanceof Date){
14893 return encodeDate(o);
14894 }else if(typeof o == "string"){
14895 return encodeString(o);
14896 }else if(typeof o == "number"){
14897 return isFinite(o) ? String(o) : "null";
14898 }else if(typeof o == "boolean"){
14901 var a = ["{"], b, i, v;
14903 if(!useHasOwn || o.hasOwnProperty(i)) {
14905 switch (typeof v) {
14914 a.push(this.encode(i), ":",
14915 v === null ? "null" : this.encode(v));
14926 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14927 * @param {String} json The JSON string
14928 * @return {Object} The resulting object
14930 this.decode = function(json){
14932 return /** eval:var:json */ eval("(" + json + ')');
14936 * Shorthand for {@link Roo.util.JSON#encode}
14937 * @member Roo encode
14939 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14941 * Shorthand for {@link Roo.util.JSON#decode}
14942 * @member Roo decode
14944 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14947 * Ext JS Library 1.1.1
14948 * Copyright(c) 2006-2007, Ext JS, LLC.
14950 * Originally Released Under LGPL - original licence link has changed is not relivant.
14953 * <script type="text/javascript">
14957 * @class Roo.util.Format
14958 * Reusable data formatting functions
14961 Roo.util.Format = function(){
14962 var trimRe = /^\s+|\s+$/g;
14965 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14966 * @param {String} value The string to truncate
14967 * @param {Number} length The maximum length to allow before truncating
14968 * @return {String} The converted text
14970 ellipsis : function(value, len){
14971 if(value && value.length > len){
14972 return value.substr(0, len-3)+"...";
14978 * Checks a reference and converts it to empty string if it is undefined
14979 * @param {Mixed} value Reference to check
14980 * @return {Mixed} Empty string if converted, otherwise the original value
14982 undef : function(value){
14983 return typeof value != "undefined" ? value : "";
14987 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14988 * @param {String} value The string to encode
14989 * @return {String} The encoded text
14991 htmlEncode : function(value){
14992 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
14996 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14997 * @param {String} value The string to decode
14998 * @return {String} The decoded text
15000 htmlDecode : function(value){
15001 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"');
15005 * Trims any whitespace from either side of a string
15006 * @param {String} value The text to trim
15007 * @return {String} The trimmed text
15009 trim : function(value){
15010 return String(value).replace(trimRe, "");
15014 * Returns a substring from within an original string
15015 * @param {String} value The original text
15016 * @param {Number} start The start index of the substring
15017 * @param {Number} length The length of the substring
15018 * @return {String} The substring
15020 substr : function(value, start, length){
15021 return String(value).substr(start, length);
15025 * Converts a string to all lower case letters
15026 * @param {String} value The text to convert
15027 * @return {String} The converted text
15029 lowercase : function(value){
15030 return String(value).toLowerCase();
15034 * Converts a string to all upper case letters
15035 * @param {String} value The text to convert
15036 * @return {String} The converted text
15038 uppercase : function(value){
15039 return String(value).toUpperCase();
15043 * Converts the first character only of a string to upper case
15044 * @param {String} value The text to convert
15045 * @return {String} The converted text
15047 capitalize : function(value){
15048 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15052 call : function(value, fn){
15053 if(arguments.length > 2){
15054 var args = Array.prototype.slice.call(arguments, 2);
15055 args.unshift(value);
15057 return /** eval:var:value */ eval(fn).apply(window, args);
15059 /** eval:var:value */
15060 return /** eval:var:value */ eval(fn).call(window, value);
15066 * safer version of Math.toFixed..??/
15067 * @param {Number/String} value The numeric value to format
15068 * @param {Number/String} value Decimal places
15069 * @return {String} The formatted currency string
15071 toFixed : function(v, n)
15073 // why not use to fixed - precision is buggered???
15075 return Math.round(v-0);
15077 var fact = Math.pow(10,n+1);
15078 v = (Math.round((v-0)*fact))/fact;
15079 var z = (''+fact).substring(2);
15080 if (v == Math.floor(v)) {
15081 return Math.floor(v) + '.' + z;
15084 // now just padd decimals..
15085 var ps = String(v).split('.');
15086 var fd = (ps[1] + z);
15087 var r = fd.substring(0,n);
15088 var rm = fd.substring(n);
15090 return ps[0] + '.' + r;
15092 r*=1; // turn it into a number;
15094 if (String(r).length != n) {
15097 r = String(r).substring(1); // chop the end off.
15100 return ps[0] + '.' + r;
15105 * Format a number as US currency
15106 * @param {Number/String} value The numeric value to format
15107 * @return {String} The formatted currency string
15109 usMoney : function(v){
15110 return '$' + Roo.util.Format.number(v);
15115 * eventually this should probably emulate php's number_format
15116 * @param {Number/String} value The numeric value to format
15117 * @param {Number} decimals number of decimal places
15118 * @param {String} delimiter for thousands (default comma)
15119 * @return {String} The formatted currency string
15121 number : function(v, decimals, thousandsDelimiter)
15123 // multiply and round.
15124 decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15125 thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15127 var mul = Math.pow(10, decimals);
15128 var zero = String(mul).substring(1);
15129 v = (Math.round((v-0)*mul))/mul;
15131 // if it's '0' number.. then
15133 //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15135 var ps = v.split('.');
15138 var r = /(\d+)(\d{3})/;
15141 if(thousandsDelimiter.length != 0) {
15142 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15147 (decimals ? ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15148 // does not have decimals
15149 (decimals ? ('.' + zero) : '');
15152 return whole + sub ;
15156 * Parse a value into a formatted date using the specified format pattern.
15157 * @param {Mixed} value The value to format
15158 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15159 * @return {String} The formatted date string
15161 date : function(v, format){
15165 if(!(v instanceof Date)){
15166 v = new Date(Date.parse(v));
15168 return v.dateFormat(format || Roo.util.Format.defaults.date);
15172 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15173 * @param {String} format Any valid date format string
15174 * @return {Function} The date formatting function
15176 dateRenderer : function(format){
15177 return function(v){
15178 return Roo.util.Format.date(v, format);
15183 stripTagsRE : /<\/?[^>]+>/gi,
15186 * Strips all HTML tags
15187 * @param {Mixed} value The text from which to strip tags
15188 * @return {String} The stripped text
15190 stripTags : function(v){
15191 return !v ? v : String(v).replace(this.stripTagsRE, "");
15195 * Size in Mb,Gb etc.
15196 * @param {Number} value The number to be formated
15197 * @param {number} decimals how many decimal places
15198 * @return {String} the formated string
15200 size : function(value, decimals)
15202 var sizes = ['b', 'k', 'M', 'G', 'T'];
15206 var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15207 return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals) + sizes[i];
15214 Roo.util.Format.defaults = {
15218 * Ext JS Library 1.1.1
15219 * Copyright(c) 2006-2007, Ext JS, LLC.
15221 * Originally Released Under LGPL - original licence link has changed is not relivant.
15224 * <script type="text/javascript">
15231 * @class Roo.MasterTemplate
15232 * @extends Roo.Template
15233 * Provides a template that can have child templates. The syntax is:
15235 var t = new Roo.MasterTemplate(
15236 '<select name="{name}">',
15237 '<tpl name="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
15240 t.add('options', {value: 'foo', text: 'bar'});
15241 // or you can add multiple child elements in one shot
15242 t.addAll('options', [
15243 {value: 'foo', text: 'bar'},
15244 {value: 'foo2', text: 'bar2'},
15245 {value: 'foo3', text: 'bar3'}
15247 // then append, applying the master template values
15248 t.append('my-form', {name: 'my-select'});
15250 * A name attribute for the child template is not required if you have only one child
15251 * template or you want to refer to them by index.
15253 Roo.MasterTemplate = function(){
15254 Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15255 this.originalHtml = this.html;
15257 var m, re = this.subTemplateRe;
15260 while(m = re.exec(this.html)){
15261 var name = m[1], content = m[2];
15266 tpl : new Roo.Template(content)
15269 st[name] = st[subIndex];
15271 st[subIndex].tpl.compile();
15272 st[subIndex].tpl.call = this.call.createDelegate(this);
15275 this.subCount = subIndex;
15278 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15280 * The regular expression used to match sub templates
15284 subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15287 * Applies the passed values to a child template.
15288 * @param {String/Number} name (optional) The name or index of the child template
15289 * @param {Array/Object} values The values to be applied to the template
15290 * @return {MasterTemplate} this
15292 add : function(name, values){
15293 if(arguments.length == 1){
15294 values = arguments[0];
15297 var s = this.subs[name];
15298 s.buffer[s.buffer.length] = s.tpl.apply(values);
15303 * Applies all the passed values to a child template.
15304 * @param {String/Number} name (optional) The name or index of the child template
15305 * @param {Array} values The values to be applied to the template, this should be an array of objects.
15306 * @param {Boolean} reset (optional) True to reset the template first
15307 * @return {MasterTemplate} this
15309 fill : function(name, values, reset){
15311 if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15319 for(var i = 0, len = values.length; i < len; i++){
15320 this.add(name, values[i]);
15326 * Resets the template for reuse
15327 * @return {MasterTemplate} this
15329 reset : function(){
15331 for(var i = 0; i < this.subCount; i++){
15337 applyTemplate : function(values){
15339 var replaceIndex = -1;
15340 this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15341 return s[++replaceIndex].buffer.join("");
15343 return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15346 apply : function(){
15347 return this.applyTemplate.apply(this, arguments);
15350 compile : function(){return this;}
15354 * Alias for fill().
15357 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15359 * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15360 * var tpl = Roo.MasterTemplate.from('element-id');
15361 * @param {String/HTMLElement} el
15362 * @param {Object} config
15365 Roo.MasterTemplate.from = function(el, config){
15366 el = Roo.getDom(el);
15367 return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15370 * Ext JS Library 1.1.1
15371 * Copyright(c) 2006-2007, Ext JS, LLC.
15373 * Originally Released Under LGPL - original licence link has changed is not relivant.
15376 * <script type="text/javascript">
15381 * @class Roo.util.CSS
15382 * Utility class for manipulating CSS rules
15386 Roo.util.CSS = function(){
15388 var doc = document;
15390 var camelRe = /(-[a-z])/gi;
15391 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15395 * Very simple dynamic creation of stylesheets from a text blob of rules. The text will wrapped in a style
15396 * tag and appended to the HEAD of the document.
15397 * @param {String|Object} cssText The text containing the css rules
15398 * @param {String} id An id to add to the stylesheet for later removal
15399 * @return {StyleSheet}
15401 createStyleSheet : function(cssText, id){
15403 var head = doc.getElementsByTagName("head")[0];
15404 var nrules = doc.createElement("style");
15405 nrules.setAttribute("type", "text/css");
15407 nrules.setAttribute("id", id);
15409 if (typeof(cssText) != 'string') {
15410 // support object maps..
15411 // not sure if this a good idea..
15412 // perhaps it should be merged with the general css handling
15413 // and handle js style props.
15414 var cssTextNew = [];
15415 for(var n in cssText) {
15417 for(var k in cssText[n]) {
15418 citems.push( k + ' : ' +cssText[n][k] + ';' );
15420 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15423 cssText = cssTextNew.join("\n");
15429 head.appendChild(nrules);
15430 ss = nrules.styleSheet;
15431 ss.cssText = cssText;
15434 nrules.appendChild(doc.createTextNode(cssText));
15436 nrules.cssText = cssText;
15438 head.appendChild(nrules);
15439 ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15441 this.cacheStyleSheet(ss);
15446 * Removes a style or link tag by id
15447 * @param {String} id The id of the tag
15449 removeStyleSheet : function(id){
15450 var existing = doc.getElementById(id);
15452 existing.parentNode.removeChild(existing);
15457 * Dynamically swaps an existing stylesheet reference for a new one
15458 * @param {String} id The id of an existing link tag to remove
15459 * @param {String} url The href of the new stylesheet to include
15461 swapStyleSheet : function(id, url){
15462 this.removeStyleSheet(id);
15463 var ss = doc.createElement("link");
15464 ss.setAttribute("rel", "stylesheet");
15465 ss.setAttribute("type", "text/css");
15466 ss.setAttribute("id", id);
15467 ss.setAttribute("href", url);
15468 doc.getElementsByTagName("head")[0].appendChild(ss);
15472 * Refresh the rule cache if you have dynamically added stylesheets
15473 * @return {Object} An object (hash) of rules indexed by selector
15475 refreshCache : function(){
15476 return this.getRules(true);
15480 cacheStyleSheet : function(stylesheet){
15484 try{// try catch for cross domain access issue
15485 var ssRules = stylesheet.cssRules || stylesheet.rules;
15486 for(var j = ssRules.length-1; j >= 0; --j){
15487 rules[ssRules[j].selectorText] = ssRules[j];
15493 * Gets all css rules for the document
15494 * @param {Boolean} refreshCache true to refresh the internal cache
15495 * @return {Object} An object (hash) of rules indexed by selector
15497 getRules : function(refreshCache){
15498 if(rules == null || refreshCache){
15500 var ds = doc.styleSheets;
15501 for(var i =0, len = ds.length; i < len; i++){
15503 this.cacheStyleSheet(ds[i]);
15511 * Gets an an individual CSS rule by selector(s)
15512 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15513 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15514 * @return {CSSRule} The CSS rule or null if one is not found
15516 getRule : function(selector, refreshCache){
15517 var rs = this.getRules(refreshCache);
15518 if(!(selector instanceof Array)){
15519 return rs[selector];
15521 for(var i = 0; i < selector.length; i++){
15522 if(rs[selector[i]]){
15523 return rs[selector[i]];
15531 * Updates a rule property
15532 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15533 * @param {String} property The css property
15534 * @param {String} value The new value for the property
15535 * @return {Boolean} true If a rule was found and updated
15537 updateRule : function(selector, property, value){
15538 if(!(selector instanceof Array)){
15539 var rule = this.getRule(selector);
15541 rule.style[property.replace(camelRe, camelFn)] = value;
15545 for(var i = 0; i < selector.length; i++){
15546 if(this.updateRule(selector[i], property, value)){
15556 * Ext JS Library 1.1.1
15557 * Copyright(c) 2006-2007, Ext JS, LLC.
15559 * Originally Released Under LGPL - original licence link has changed is not relivant.
15562 * <script type="text/javascript">
15568 * @class Roo.util.ClickRepeater
15569 * @extends Roo.util.Observable
15571 * A wrapper class which can be applied to any element. Fires a "click" event while the
15572 * mouse is pressed. The interval between firings may be specified in the config but
15573 * defaults to 10 milliseconds.
15575 * Optionally, a CSS class may be applied to the element during the time it is pressed.
15577 * @cfg {String/HTMLElement/Element} el The element to act as a button.
15578 * @cfg {Number} delay The initial delay before the repeating event begins firing.
15579 * Similar to an autorepeat key delay.
15580 * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15581 * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15582 * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15583 * "interval" and "delay" are ignored. "immediate" is honored.
15584 * @cfg {Boolean} preventDefault True to prevent the default click event
15585 * @cfg {Boolean} stopDefault True to stop the default click event
15588 * 2007-02-02 jvs Original code contributed by Nige "Animal" White
15589 * 2007-02-02 jvs Renamed to ClickRepeater
15590 * 2007-02-03 jvs Modifications for FF Mac and Safari
15593 * @param {String/HTMLElement/Element} el The element to listen on
15594 * @param {Object} config
15596 Roo.util.ClickRepeater = function(el, config)
15598 this.el = Roo.get(el);
15599 this.el.unselectable();
15601 Roo.apply(this, config);
15606 * Fires when the mouse button is depressed.
15607 * @param {Roo.util.ClickRepeater} this
15609 "mousedown" : true,
15612 * Fires on a specified interval during the time the element is pressed.
15613 * @param {Roo.util.ClickRepeater} this
15618 * Fires when the mouse key is released.
15619 * @param {Roo.util.ClickRepeater} this
15624 this.el.on("mousedown", this.handleMouseDown, this);
15625 if(this.preventDefault || this.stopDefault){
15626 this.el.on("click", function(e){
15627 if(this.preventDefault){
15628 e.preventDefault();
15630 if(this.stopDefault){
15636 // allow inline handler
15638 this.on("click", this.handler, this.scope || this);
15641 Roo.util.ClickRepeater.superclass.constructor.call(this);
15644 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15647 preventDefault : true,
15648 stopDefault : false,
15652 handleMouseDown : function(){
15653 clearTimeout(this.timer);
15655 if(this.pressClass){
15656 this.el.addClass(this.pressClass);
15658 this.mousedownTime = new Date();
15660 Roo.get(document).on("mouseup", this.handleMouseUp, this);
15661 this.el.on("mouseout", this.handleMouseOut, this);
15663 this.fireEvent("mousedown", this);
15664 this.fireEvent("click", this);
15666 this.timer = this.click.defer(this.delay || this.interval, this);
15670 click : function(){
15671 this.fireEvent("click", this);
15672 this.timer = this.click.defer(this.getInterval(), this);
15676 getInterval: function(){
15677 if(!this.accelerate){
15678 return this.interval;
15680 var pressTime = this.mousedownTime.getElapsed();
15681 if(pressTime < 500){
15683 }else if(pressTime < 1700){
15685 }else if(pressTime < 2600){
15687 }else if(pressTime < 3500){
15689 }else if(pressTime < 4400){
15691 }else if(pressTime < 5300){
15693 }else if(pressTime < 6200){
15701 handleMouseOut : function(){
15702 clearTimeout(this.timer);
15703 if(this.pressClass){
15704 this.el.removeClass(this.pressClass);
15706 this.el.on("mouseover", this.handleMouseReturn, this);
15710 handleMouseReturn : function(){
15711 this.el.un("mouseover", this.handleMouseReturn);
15712 if(this.pressClass){
15713 this.el.addClass(this.pressClass);
15719 handleMouseUp : function(){
15720 clearTimeout(this.timer);
15721 this.el.un("mouseover", this.handleMouseReturn);
15722 this.el.un("mouseout", this.handleMouseOut);
15723 Roo.get(document).un("mouseup", this.handleMouseUp);
15724 this.el.removeClass(this.pressClass);
15725 this.fireEvent("mouseup", this);
15728 * @class Roo.util.Clipboard
15734 Roo.util.Clipboard = {
15736 * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15737 * @param {String} text to copy to clipboard
15739 write : function(text) {
15740 // navigator clipboard api needs a secure context (https)
15741 if (navigator.clipboard && window.isSecureContext) {
15742 // navigator clipboard api method'
15743 navigator.clipboard.writeText(text);
15746 // text area method
15747 var ta = document.createElement("textarea");
15749 // make the textarea out of viewport
15750 ta.style.position = "fixed";
15751 ta.style.left = "-999999px";
15752 ta.style.top = "-999999px";
15753 document.body.appendChild(ta);
15756 document.execCommand('copy');
15766 * Ext JS Library 1.1.1
15767 * Copyright(c) 2006-2007, Ext JS, LLC.
15769 * Originally Released Under LGPL - original licence link has changed is not relivant.
15772 * <script type="text/javascript">
15777 * @class Roo.KeyNav
15778 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
15779 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15780 * way to implement custom navigation schemes for any UI component.</p>
15781 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15782 * pageUp, pageDown, del, home, end. Usage:</p>
15784 var nav = new Roo.KeyNav("my-element", {
15785 "left" : function(e){
15786 this.moveLeft(e.ctrlKey);
15788 "right" : function(e){
15789 this.moveRight(e.ctrlKey);
15791 "enter" : function(e){
15798 * @param {String/HTMLElement/Roo.Element} el The element to bind to
15799 * @param {Object} config The config
15801 Roo.KeyNav = function(el, config){
15802 this.el = Roo.get(el);
15803 Roo.apply(this, config);
15804 if(!this.disabled){
15805 this.disabled = true;
15810 Roo.KeyNav.prototype = {
15812 * @cfg {Boolean} disabled
15813 * True to disable this KeyNav instance (defaults to false)
15817 * @cfg {String} defaultEventAction
15818 * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key. Valid values are
15819 * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15820 * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15822 defaultEventAction: "stopEvent",
15824 * @cfg {Boolean} forceKeyDown
15825 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
15826 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15827 * handle keydown instead of keypress.
15829 forceKeyDown : false,
15832 prepareEvent : function(e){
15833 var k = e.getKey();
15834 var h = this.keyToHandler[k];
15835 //if(h && this[h]){
15836 // e.stopPropagation();
15838 if(Roo.isSafari && h && k >= 37 && k <= 40){
15844 relay : function(e){
15845 var k = e.getKey();
15846 var h = this.keyToHandler[k];
15848 if(this.doRelay(e, this[h], h) !== true){
15849 e[this.defaultEventAction]();
15855 doRelay : function(e, h, hname){
15856 return h.call(this.scope || this, e);
15859 // possible handlers
15873 // quick lookup hash
15890 * Enable this KeyNav
15892 enable: function(){
15894 // ie won't do special keys on keypress, no one else will repeat keys with keydown
15895 // the EventObject will normalize Safari automatically
15896 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15897 this.el.on("keydown", this.relay, this);
15899 this.el.on("keydown", this.prepareEvent, this);
15900 this.el.on("keypress", this.relay, this);
15902 this.disabled = false;
15907 * Disable this KeyNav
15909 disable: function(){
15910 if(!this.disabled){
15911 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15912 this.el.un("keydown", this.relay);
15914 this.el.un("keydown", this.prepareEvent);
15915 this.el.un("keypress", this.relay);
15917 this.disabled = true;
15922 * Ext JS Library 1.1.1
15923 * Copyright(c) 2006-2007, Ext JS, LLC.
15925 * Originally Released Under LGPL - original licence link has changed is not relivant.
15928 * <script type="text/javascript">
15933 * @class Roo.KeyMap
15934 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15935 * The constructor accepts the same config object as defined by {@link #addBinding}.
15936 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15937 * combination it will call the function with this signature (if the match is a multi-key
15938 * combination the callback will still be called only once): (String key, Roo.EventObject e)
15939 * A KeyMap can also handle a string representation of keys.<br />
15942 // map one key by key code
15943 var map = new Roo.KeyMap("my-element", {
15944 key: 13, // or Roo.EventObject.ENTER
15949 // map multiple keys to one action by string
15950 var map = new Roo.KeyMap("my-element", {
15956 // map multiple keys to multiple actions by strings and array of codes
15957 var map = new Roo.KeyMap("my-element", [
15960 fn: function(){ alert("Return was pressed"); }
15963 fn: function(){ alert('a, b or c was pressed'); }
15968 fn: function(){ alert('Control + shift + tab was pressed.'); }
15972 * <b>Note: A KeyMap starts enabled</b>
15974 * @param {String/HTMLElement/Roo.Element} el The element to bind to
15975 * @param {Object} config The config (see {@link #addBinding})
15976 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15978 Roo.KeyMap = function(el, config, eventName){
15979 this.el = Roo.get(el);
15980 this.eventName = eventName || "keydown";
15981 this.bindings = [];
15983 this.addBinding(config);
15988 Roo.KeyMap.prototype = {
15990 * True to stop the event from bubbling and prevent the default browser action if the
15991 * key was handled by the KeyMap (defaults to false)
15997 * Add a new binding to this KeyMap. The following config object properties are supported:
15999 Property Type Description
16000 ---------- --------------- ----------------------------------------------------------------------
16001 key String/Array A single keycode or an array of keycodes to handle
16002 shift Boolean True to handle key only when shift is pressed (defaults to false)
16003 ctrl Boolean True to handle key only when ctrl is pressed (defaults to false)
16004 alt Boolean True to handle key only when alt is pressed (defaults to false)
16005 fn Function The function to call when KeyMap finds the expected key combination
16006 scope Object The scope of the callback function
16012 var map = new Roo.KeyMap(document, {
16013 key: Roo.EventObject.ENTER,
16018 //Add a new binding to the existing KeyMap later
16026 * @param {Object/Array} config A single KeyMap config or an array of configs
16028 addBinding : function(config){
16029 if(config instanceof Array){
16030 for(var i = 0, len = config.length; i < len; i++){
16031 this.addBinding(config[i]);
16035 var keyCode = config.key,
16036 shift = config.shift,
16037 ctrl = config.ctrl,
16040 scope = config.scope;
16041 if(typeof keyCode == "string"){
16043 var keyString = keyCode.toUpperCase();
16044 for(var j = 0, len = keyString.length; j < len; j++){
16045 ks.push(keyString.charCodeAt(j));
16049 var keyArray = keyCode instanceof Array;
16050 var handler = function(e){
16051 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
16052 var k = e.getKey();
16054 for(var i = 0, len = keyCode.length; i < len; i++){
16055 if(keyCode[i] == k){
16056 if(this.stopEvent){
16059 fn.call(scope || window, k, e);
16065 if(this.stopEvent){
16068 fn.call(scope || window, k, e);
16073 this.bindings.push(handler);
16077 * Shorthand for adding a single key listener
16078 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16079 * following options:
16080 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16081 * @param {Function} fn The function to call
16082 * @param {Object} scope (optional) The scope of the function
16084 on : function(key, fn, scope){
16085 var keyCode, shift, ctrl, alt;
16086 if(typeof key == "object" && !(key instanceof Array)){
16105 handleKeyDown : function(e){
16106 if(this.enabled){ //just in case
16107 var b = this.bindings;
16108 for(var i = 0, len = b.length; i < len; i++){
16109 b[i].call(this, e);
16115 * Returns true if this KeyMap is enabled
16116 * @return {Boolean}
16118 isEnabled : function(){
16119 return this.enabled;
16123 * Enables this KeyMap
16125 enable: function(){
16127 this.el.on(this.eventName, this.handleKeyDown, this);
16128 this.enabled = true;
16133 * Disable this KeyMap
16135 disable: function(){
16137 this.el.removeListener(this.eventName, this.handleKeyDown, this);
16138 this.enabled = false;
16143 * Ext JS Library 1.1.1
16144 * Copyright(c) 2006-2007, Ext JS, LLC.
16146 * Originally Released Under LGPL - original licence link has changed is not relivant.
16149 * <script type="text/javascript">
16154 * @class Roo.util.TextMetrics
16155 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16156 * wide, in pixels, a given block of text will be.
16159 Roo.util.TextMetrics = function(){
16163 * Measures the size of the specified text
16164 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16165 * that can affect the size of the rendered text
16166 * @param {String} text The text to measure
16167 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16168 * in order to accurately measure the text height
16169 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16171 measure : function(el, text, fixedWidth){
16173 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16176 shared.setFixedWidth(fixedWidth || 'auto');
16177 return shared.getSize(text);
16181 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
16182 * the overhead of multiple calls to initialize the style properties on each measurement.
16183 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16184 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16185 * in order to accurately measure the text height
16186 * @return {Roo.util.TextMetrics.Instance} instance The new instance
16188 createInstance : function(el, fixedWidth){
16189 return Roo.util.TextMetrics.Instance(el, fixedWidth);
16195 * @class Roo.util.TextMetrics.Instance
16196 * Instance of TextMetrics Calcuation
16198 * Create a new TextMetrics Instance
16199 * @param {Object} bindto
16200 * @param {Boolean} fixedWidth
16203 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16205 var ml = new Roo.Element(document.createElement('div'));
16206 document.body.appendChild(ml.dom);
16207 ml.position('absolute');
16208 ml.setLeftTop(-1000, -1000);
16212 ml.setWidth(fixedWidth);
16217 * Returns the size of the specified text based on the internal element's style and width properties
16218 * @param {String} text The text to measure
16219 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16221 getSize : function(text){
16223 var s = ml.getSize();
16229 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16230 * that can affect the size of the rendered text
16231 * @param {String/HTMLElement} el The element, dom node or id
16233 bind : function(el){
16235 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16240 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
16241 * to set a fixed width in order to accurately measure the text height.
16242 * @param {Number} width The width to set on the element
16244 setFixedWidth : function(width){
16245 ml.setWidth(width);
16249 * Returns the measured width of the specified text
16250 * @param {String} text The text to measure
16251 * @return {Number} width The width in pixels
16253 getWidth : function(text){
16254 ml.dom.style.width = 'auto';
16255 return this.getSize(text).width;
16259 * Returns the measured height of the specified text. For multiline text, be sure to call
16260 * {@link #setFixedWidth} if necessary.
16261 * @param {String} text The text to measure
16262 * @return {Number} height The height in pixels
16264 getHeight : function(text){
16265 return this.getSize(text).height;
16269 instance.bind(bindTo);
16274 // backwards compat
16275 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16277 * Ext JS Library 1.1.1
16278 * Copyright(c) 2006-2007, Ext JS, LLC.
16280 * Originally Released Under LGPL - original licence link has changed is not relivant.
16283 * <script type="text/javascript">
16287 * @class Roo.state.Provider
16288 * Abstract base class for state provider implementations. This class provides methods
16289 * for encoding and decoding <b>typed</b> variables including dates and defines the
16290 * Provider interface.
16292 Roo.state.Provider = function(){
16294 * @event statechange
16295 * Fires when a state change occurs.
16296 * @param {Provider} this This state provider
16297 * @param {String} key The state key which was changed
16298 * @param {String} value The encoded value for the state
16301 "statechange": true
16304 Roo.state.Provider.superclass.constructor.call(this);
16306 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16308 * Returns the current value for a key
16309 * @param {String} name The key name
16310 * @param {Mixed} defaultValue A default value to return if the key's value is not found
16311 * @return {Mixed} The state data
16313 get : function(name, defaultValue){
16314 return typeof this.state[name] == "undefined" ?
16315 defaultValue : this.state[name];
16319 * Clears a value from the state
16320 * @param {String} name The key name
16322 clear : function(name){
16323 delete this.state[name];
16324 this.fireEvent("statechange", this, name, null);
16328 * Sets the value for a key
16329 * @param {String} name The key name
16330 * @param {Mixed} value The value to set
16332 set : function(name, value){
16333 this.state[name] = value;
16334 this.fireEvent("statechange", this, name, value);
16338 * Decodes a string previously encoded with {@link #encodeValue}.
16339 * @param {String} value The value to decode
16340 * @return {Mixed} The decoded value
16342 decodeValue : function(cookie){
16343 var re = /^(a|n|d|b|s|o)\:(.*)$/;
16344 var matches = re.exec(unescape(cookie));
16345 if(!matches || !matches[1]) {
16346 return; // non state cookie
16348 var type = matches[1];
16349 var v = matches[2];
16352 return parseFloat(v);
16354 return new Date(Date.parse(v));
16359 var values = v.split("^");
16360 for(var i = 0, len = values.length; i < len; i++){
16361 all.push(this.decodeValue(values[i]));
16366 var values = v.split("^");
16367 for(var i = 0, len = values.length; i < len; i++){
16368 var kv = values[i].split("=");
16369 all[kv[0]] = this.decodeValue(kv[1]);
16378 * Encodes a value including type information. Decode with {@link #decodeValue}.
16379 * @param {Mixed} value The value to encode
16380 * @return {String} The encoded value
16382 encodeValue : function(v){
16384 if(typeof v == "number"){
16386 }else if(typeof v == "boolean"){
16387 enc = "b:" + (v ? "1" : "0");
16388 }else if(v instanceof Date){
16389 enc = "d:" + v.toGMTString();
16390 }else if(v instanceof Array){
16392 for(var i = 0, len = v.length; i < len; i++){
16393 flat += this.encodeValue(v[i]);
16399 }else if(typeof v == "object"){
16402 if(typeof v[key] != "function"){
16403 flat += key + "=" + this.encodeValue(v[key]) + "^";
16406 enc = "o:" + flat.substring(0, flat.length-1);
16410 return escape(enc);
16416 * Ext JS Library 1.1.1
16417 * Copyright(c) 2006-2007, Ext JS, LLC.
16419 * Originally Released Under LGPL - original licence link has changed is not relivant.
16422 * <script type="text/javascript">
16425 * @class Roo.state.Manager
16426 * This is the global state manager. By default all components that are "state aware" check this class
16427 * for state information if you don't pass them a custom state provider. In order for this class
16428 * to be useful, it must be initialized with a provider when your application initializes.
16430 // in your initialization function
16432 Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16434 // supposed you have a {@link Roo.BorderLayout}
16435 var layout = new Roo.BorderLayout(...);
16436 layout.restoreState();
16437 // or a {Roo.BasicDialog}
16438 var dialog = new Roo.BasicDialog(...);
16439 dialog.restoreState();
16443 Roo.state.Manager = function(){
16444 var provider = new Roo.state.Provider();
16448 * Configures the default state provider for your application
16449 * @param {Provider} stateProvider The state provider to set
16451 setProvider : function(stateProvider){
16452 provider = stateProvider;
16456 * Returns the current value for a key
16457 * @param {String} name The key name
16458 * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16459 * @return {Mixed} The state data
16461 get : function(key, defaultValue){
16462 return provider.get(key, defaultValue);
16466 * Sets the value for a key
16467 * @param {String} name The key name
16468 * @param {Mixed} value The state data
16470 set : function(key, value){
16471 provider.set(key, value);
16475 * Clears a value from the state
16476 * @param {String} name The key name
16478 clear : function(key){
16479 provider.clear(key);
16483 * Gets the currently configured state provider
16484 * @return {Provider} The state provider
16486 getProvider : function(){
16493 * Ext JS Library 1.1.1
16494 * Copyright(c) 2006-2007, Ext JS, LLC.
16496 * Originally Released Under LGPL - original licence link has changed is not relivant.
16499 * <script type="text/javascript">
16502 * @class Roo.state.CookieProvider
16503 * @extends Roo.state.Provider
16504 * The default Provider implementation which saves state via cookies.
16507 var cp = new Roo.state.CookieProvider({
16509 expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16510 domain: "roojs.com"
16512 Roo.state.Manager.setProvider(cp);
16514 * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16515 * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16516 * @cfg {String} domain The domain to save the cookie for. Note that you cannot specify a different domain than
16517 * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16518 * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16519 * domain the page is running on including the 'www' like 'www.roojs.com')
16520 * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16522 * Create a new CookieProvider
16523 * @param {Object} config The configuration object
16525 Roo.state.CookieProvider = function(config){
16526 Roo.state.CookieProvider.superclass.constructor.call(this);
16528 this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16529 this.domain = null;
16530 this.secure = false;
16531 Roo.apply(this, config);
16532 this.state = this.readCookies();
16535 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16537 set : function(name, value){
16538 if(typeof value == "undefined" || value === null){
16542 this.setCookie(name, value);
16543 Roo.state.CookieProvider.superclass.set.call(this, name, value);
16547 clear : function(name){
16548 this.clearCookie(name);
16549 Roo.state.CookieProvider.superclass.clear.call(this, name);
16553 readCookies : function(){
16555 var c = document.cookie + ";";
16556 var re = /\s?(.*?)=(.*?);/g;
16558 while((matches = re.exec(c)) != null){
16559 var name = matches[1];
16560 var value = matches[2];
16561 if(name && name.substring(0,3) == "ys-"){
16562 cookies[name.substr(3)] = this.decodeValue(value);
16569 setCookie : function(name, value){
16570 document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16571 ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16572 ((this.path == null) ? "" : ("; path=" + this.path)) +
16573 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16574 ((this.secure == true) ? "; secure" : "");
16578 clearCookie : function(name){
16579 document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16580 ((this.path == null) ? "" : ("; path=" + this.path)) +
16581 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16582 ((this.secure == true) ? "; secure" : "");
16586 * Ext JS Library 1.1.1
16587 * Copyright(c) 2006-2007, Ext JS, LLC.
16589 * Originally Released Under LGPL - original licence link has changed is not relivant.
16592 * <script type="text/javascript">
16597 * @class Roo.ComponentMgr
16598 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16601 Roo.ComponentMgr = function(){
16602 var all = new Roo.util.MixedCollection();
16606 * Registers a component.
16607 * @param {Roo.Component} c The component
16609 register : function(c){
16614 * Unregisters a component.
16615 * @param {Roo.Component} c The component
16617 unregister : function(c){
16622 * Returns a component by id
16623 * @param {String} id The component id
16625 get : function(id){
16626 return all.get(id);
16630 * Registers a function that will be called when a specified component is added to ComponentMgr
16631 * @param {String} id The component id
16632 * @param {Funtction} fn The callback function
16633 * @param {Object} scope The scope of the callback
16635 onAvailable : function(id, fn, scope){
16636 all.on("add", function(index, o){
16638 fn.call(scope || o, o);
16639 all.un("add", fn, scope);
16646 * Ext JS Library 1.1.1
16647 * Copyright(c) 2006-2007, Ext JS, LLC.
16649 * Originally Released Under LGPL - original licence link has changed is not relivant.
16652 * <script type="text/javascript">
16656 * @class Roo.Component
16657 * @extends Roo.util.Observable
16658 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
16659 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
16660 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16661 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16662 * All visual components (widgets) that require rendering into a layout should subclass Component.
16664 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
16665 * 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
16666 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
16668 Roo.Component = function(config){
16669 config = config || {};
16670 if(config.tagName || config.dom || typeof config == "string"){ // element object
16671 config = {el: config, id: config.id || config};
16673 this.initialConfig = config;
16675 Roo.apply(this, config);
16679 * Fires after the component is disabled.
16680 * @param {Roo.Component} this
16685 * Fires after the component is enabled.
16686 * @param {Roo.Component} this
16690 * @event beforeshow
16691 * Fires before the component is shown. Return false to stop the show.
16692 * @param {Roo.Component} this
16697 * Fires after the component is shown.
16698 * @param {Roo.Component} this
16702 * @event beforehide
16703 * Fires before the component is hidden. Return false to stop the hide.
16704 * @param {Roo.Component} this
16709 * Fires after the component is hidden.
16710 * @param {Roo.Component} this
16714 * @event beforerender
16715 * Fires before the component is rendered. Return false to stop the render.
16716 * @param {Roo.Component} this
16718 beforerender : true,
16721 * Fires after the component is rendered.
16722 * @param {Roo.Component} this
16726 * @event beforedestroy
16727 * Fires before the component is destroyed. Return false to stop the destroy.
16728 * @param {Roo.Component} this
16730 beforedestroy : true,
16733 * Fires after the component is destroyed.
16734 * @param {Roo.Component} this
16739 this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16741 Roo.ComponentMgr.register(this);
16742 Roo.Component.superclass.constructor.call(this);
16743 this.initComponent();
16744 if(this.renderTo){ // not supported by all components yet. use at your own risk!
16745 this.render(this.renderTo);
16746 delete this.renderTo;
16751 Roo.Component.AUTO_ID = 1000;
16753 Roo.extend(Roo.Component, Roo.util.Observable, {
16755 * @scope Roo.Component.prototype
16757 * true if this component is hidden. Read-only.
16762 * true if this component is disabled. Read-only.
16767 * true if this component has been rendered. Read-only.
16771 /** @cfg {String} disableClass
16772 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16774 disabledClass : "x-item-disabled",
16775 /** @cfg {Boolean} allowDomMove
16776 * Whether the component can move the Dom node when rendering (defaults to true).
16778 allowDomMove : true,
16779 /** @cfg {String} hideMode (display|visibility)
16780 * How this component should hidden. Supported values are
16781 * "visibility" (css visibility), "offsets" (negative offset position) and
16782 * "display" (css display) - defaults to "display".
16784 hideMode: 'display',
16787 ctype : "Roo.Component",
16790 * @cfg {String} actionMode
16791 * which property holds the element that used for hide() / show() / disable() / enable()
16792 * default is 'el' for forms you probably want to set this to fieldEl
16797 getActionEl : function(){
16798 return this[this.actionMode];
16801 initComponent : Roo.emptyFn,
16803 * If this is a lazy rendering component, render it to its container element.
16804 * @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.
16806 render : function(container, position){
16812 if(this.fireEvent("beforerender", this) === false){
16816 if(!container && this.el){
16817 this.el = Roo.get(this.el);
16818 container = this.el.dom.parentNode;
16819 this.allowDomMove = false;
16821 this.container = Roo.get(container);
16822 this.rendered = true;
16823 if(position !== undefined){
16824 if(typeof position == 'number'){
16825 position = this.container.dom.childNodes[position];
16827 position = Roo.getDom(position);
16830 this.onRender(this.container, position || null);
16832 this.el.addClass(this.cls);
16836 this.el.applyStyles(this.style);
16839 this.fireEvent("render", this);
16840 this.afterRender(this.container);
16853 // default function is not really useful
16854 onRender : function(ct, position){
16856 this.el = Roo.get(this.el);
16857 if(this.allowDomMove !== false){
16858 ct.dom.insertBefore(this.el.dom, position);
16864 getAutoCreate : function(){
16865 var cfg = typeof this.autoCreate == "object" ?
16866 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16867 if(this.id && !cfg.id){
16874 afterRender : Roo.emptyFn,
16877 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16878 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16880 destroy : function(){
16881 if(this.fireEvent("beforedestroy", this) !== false){
16882 this.purgeListeners();
16883 this.beforeDestroy();
16885 this.el.removeAllListeners();
16887 if(this.actionMode == "container"){
16888 this.container.remove();
16892 Roo.ComponentMgr.unregister(this);
16893 this.fireEvent("destroy", this);
16898 beforeDestroy : function(){
16903 onDestroy : function(){
16908 * Returns the underlying {@link Roo.Element}.
16909 * @return {Roo.Element} The element
16911 getEl : function(){
16916 * Returns the id of this component.
16919 getId : function(){
16924 * Try to focus this component.
16925 * @param {Boolean} selectText True to also select the text in this component (if applicable)
16926 * @return {Roo.Component} this
16928 focus : function(selectText){
16931 if(selectText === true){
16932 this.el.dom.select();
16947 * Disable this component.
16948 * @return {Roo.Component} this
16950 disable : function(){
16954 this.disabled = true;
16955 this.fireEvent("disable", this);
16960 onDisable : function(){
16961 this.getActionEl().addClass(this.disabledClass);
16962 this.el.dom.disabled = true;
16966 * Enable this component.
16967 * @return {Roo.Component} this
16969 enable : function(){
16973 this.disabled = false;
16974 this.fireEvent("enable", this);
16979 onEnable : function(){
16980 this.getActionEl().removeClass(this.disabledClass);
16981 this.el.dom.disabled = false;
16985 * Convenience function for setting disabled/enabled by boolean.
16986 * @param {Boolean} disabled
16988 setDisabled : function(disabled){
16989 this[disabled ? "disable" : "enable"]();
16993 * Show this component.
16994 * @return {Roo.Component} this
16997 if(this.fireEvent("beforeshow", this) !== false){
16998 this.hidden = false;
17002 this.fireEvent("show", this);
17008 onShow : function(){
17009 var ae = this.getActionEl();
17010 if(this.hideMode == 'visibility'){
17011 ae.dom.style.visibility = "visible";
17012 }else if(this.hideMode == 'offsets'){
17013 ae.removeClass('x-hidden');
17015 ae.dom.style.display = "";
17020 * Hide this component.
17021 * @return {Roo.Component} this
17024 if(this.fireEvent("beforehide", this) !== false){
17025 this.hidden = true;
17029 this.fireEvent("hide", this);
17035 onHide : function(){
17036 var ae = this.getActionEl();
17037 if(this.hideMode == 'visibility'){
17038 ae.dom.style.visibility = "hidden";
17039 }else if(this.hideMode == 'offsets'){
17040 ae.addClass('x-hidden');
17042 ae.dom.style.display = "none";
17047 * Convenience function to hide or show this component by boolean.
17048 * @param {Boolean} visible True to show, false to hide
17049 * @return {Roo.Component} this
17051 setVisible: function(visible){
17061 * Returns true if this component is visible.
17063 isVisible : function(){
17064 return this.getActionEl().isVisible();
17067 cloneConfig : function(overrides){
17068 overrides = overrides || {};
17069 var id = overrides.id || Roo.id();
17070 var cfg = Roo.applyIf(overrides, this.initialConfig);
17071 cfg.id = id; // prevent dup id
17072 return new this.constructor(cfg);
17076 * Ext JS Library 1.1.1
17077 * Copyright(c) 2006-2007, Ext JS, LLC.
17079 * Originally Released Under LGPL - original licence link has changed is not relivant.
17082 * <script type="text/javascript">
17086 * @class Roo.BoxComponent
17087 * @extends Roo.Component
17088 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
17089 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
17090 * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17091 * layout containers.
17093 * @param {Roo.Element/String/Object} config The configuration options.
17095 Roo.BoxComponent = function(config){
17096 Roo.Component.call(this, config);
17100 * Fires after the component is resized.
17101 * @param {Roo.Component} this
17102 * @param {Number} adjWidth The box-adjusted width that was set
17103 * @param {Number} adjHeight The box-adjusted height that was set
17104 * @param {Number} rawWidth The width that was originally specified
17105 * @param {Number} rawHeight The height that was originally specified
17110 * Fires after the component is moved.
17111 * @param {Roo.Component} this
17112 * @param {Number} x The new x position
17113 * @param {Number} y The new y position
17119 Roo.extend(Roo.BoxComponent, Roo.Component, {
17120 // private, set in afterRender to signify that the component has been rendered
17122 // private, used to defer height settings to subclasses
17123 deferHeight: false,
17124 /** @cfg {Number} width
17125 * width (optional) size of component
17127 /** @cfg {Number} height
17128 * height (optional) size of component
17132 * Sets the width and height of the component. This method fires the resize event. This method can accept
17133 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17134 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17135 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17136 * @return {Roo.BoxComponent} this
17138 setSize : function(w, h){
17139 // support for standard size objects
17140 if(typeof w == 'object'){
17145 if(!this.boxReady){
17151 // prevent recalcs when not needed
17152 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17155 this.lastSize = {width: w, height: h};
17157 var adj = this.adjustSize(w, h);
17158 var aw = adj.width, ah = adj.height;
17159 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17160 var rz = this.getResizeEl();
17161 if(!this.deferHeight && aw !== undefined && ah !== undefined){
17162 rz.setSize(aw, ah);
17163 }else if(!this.deferHeight && ah !== undefined){
17165 }else if(aw !== undefined){
17168 this.onResize(aw, ah, w, h);
17169 this.fireEvent('resize', this, aw, ah, w, h);
17175 * Gets the current size of the component's underlying element.
17176 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17178 getSize : function(){
17179 return this.el.getSize();
17183 * Gets the current XY position of the component's underlying element.
17184 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17185 * @return {Array} The XY position of the element (e.g., [100, 200])
17187 getPosition : function(local){
17188 if(local === true){
17189 return [this.el.getLeft(true), this.el.getTop(true)];
17191 return this.xy || this.el.getXY();
17195 * Gets the current box measurements of the component's underlying element.
17196 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17197 * @returns {Object} box An object in the format {x, y, width, height}
17199 getBox : function(local){
17200 var s = this.el.getSize();
17202 s.x = this.el.getLeft(true);
17203 s.y = this.el.getTop(true);
17205 var xy = this.xy || this.el.getXY();
17213 * Sets the current box measurements of the component's underlying element.
17214 * @param {Object} box An object in the format {x, y, width, height}
17215 * @returns {Roo.BoxComponent} this
17217 updateBox : function(box){
17218 this.setSize(box.width, box.height);
17219 this.setPagePosition(box.x, box.y);
17224 getResizeEl : function(){
17225 return this.resizeEl || this.el;
17229 getPositionEl : function(){
17230 return this.positionEl || this.el;
17234 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
17235 * This method fires the move event.
17236 * @param {Number} left The new left
17237 * @param {Number} top The new top
17238 * @returns {Roo.BoxComponent} this
17240 setPosition : function(x, y){
17243 if(!this.boxReady){
17246 var adj = this.adjustPosition(x, y);
17247 var ax = adj.x, ay = adj.y;
17249 var el = this.getPositionEl();
17250 if(ax !== undefined || ay !== undefined){
17251 if(ax !== undefined && ay !== undefined){
17252 el.setLeftTop(ax, ay);
17253 }else if(ax !== undefined){
17255 }else if(ay !== undefined){
17258 this.onPosition(ax, ay);
17259 this.fireEvent('move', this, ax, ay);
17265 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
17266 * This method fires the move event.
17267 * @param {Number} x The new x position
17268 * @param {Number} y The new y position
17269 * @returns {Roo.BoxComponent} this
17271 setPagePosition : function(x, y){
17274 if(!this.boxReady){
17277 if(x === undefined || y === undefined){ // cannot translate undefined points
17280 var p = this.el.translatePoints(x, y);
17281 this.setPosition(p.left, p.top);
17286 onRender : function(ct, position){
17287 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17289 this.resizeEl = Roo.get(this.resizeEl);
17291 if(this.positionEl){
17292 this.positionEl = Roo.get(this.positionEl);
17297 afterRender : function(){
17298 Roo.BoxComponent.superclass.afterRender.call(this);
17299 this.boxReady = true;
17300 this.setSize(this.width, this.height);
17301 if(this.x || this.y){
17302 this.setPosition(this.x, this.y);
17304 if(this.pageX || this.pageY){
17305 this.setPagePosition(this.pageX, this.pageY);
17310 * Force the component's size to recalculate based on the underlying element's current height and width.
17311 * @returns {Roo.BoxComponent} this
17313 syncSize : function(){
17314 delete this.lastSize;
17315 this.setSize(this.el.getWidth(), this.el.getHeight());
17320 * Called after the component is resized, this method is empty by default but can be implemented by any
17321 * subclass that needs to perform custom logic after a resize occurs.
17322 * @param {Number} adjWidth The box-adjusted width that was set
17323 * @param {Number} adjHeight The box-adjusted height that was set
17324 * @param {Number} rawWidth The width that was originally specified
17325 * @param {Number} rawHeight The height that was originally specified
17327 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17332 * Called after the component is moved, this method is empty by default but can be implemented by any
17333 * subclass that needs to perform custom logic after a move occurs.
17334 * @param {Number} x The new x position
17335 * @param {Number} y The new y position
17337 onPosition : function(x, y){
17342 adjustSize : function(w, h){
17343 if(this.autoWidth){
17346 if(this.autoHeight){
17349 return {width : w, height: h};
17353 adjustPosition : function(x, y){
17354 return {x : x, y: y};
17358 * Ext JS Library 1.1.1
17359 * Copyright(c) 2006-2007, Ext JS, LLC.
17361 * Originally Released Under LGPL - original licence link has changed is not relivant.
17364 * <script type="text/javascript">
17369 * @extends Roo.Element
17370 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17371 * automatic maintaining of shadow/shim positions.
17372 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17373 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17374 * you can pass a string with a CSS class name. False turns off the shadow.
17375 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17376 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17377 * @cfg {String} cls CSS class to add to the element
17378 * @cfg {Number} zindex Starting z-index (defaults to 11000)
17379 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17381 * @param {Object} config An object with config options.
17382 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17385 Roo.Layer = function(config, existingEl){
17386 config = config || {};
17387 var dh = Roo.DomHelper;
17388 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17390 this.dom = Roo.getDom(existingEl);
17393 var o = config.dh || {tag: "div", cls: "x-layer"};
17394 this.dom = dh.append(pel, o);
17397 this.addClass(config.cls);
17399 this.constrain = config.constrain !== false;
17400 this.visibilityMode = Roo.Element.VISIBILITY;
17402 this.id = this.dom.id = config.id;
17404 this.id = Roo.id(this.dom);
17406 this.zindex = config.zindex || this.getZIndex();
17407 this.position("absolute", this.zindex);
17409 this.shadowOffset = config.shadowOffset || 4;
17410 this.shadow = new Roo.Shadow({
17411 offset : this.shadowOffset,
17412 mode : config.shadow
17415 this.shadowOffset = 0;
17417 this.useShim = config.shim !== false && Roo.useShims;
17418 this.useDisplay = config.useDisplay;
17422 var supr = Roo.Element.prototype;
17424 // shims are shared among layer to keep from having 100 iframes
17427 Roo.extend(Roo.Layer, Roo.Element, {
17429 getZIndex : function(){
17430 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17433 getShim : function(){
17440 var shim = shims.shift();
17442 shim = this.createShim();
17443 shim.enableDisplayMode('block');
17444 shim.dom.style.display = 'none';
17445 shim.dom.style.visibility = 'visible';
17447 var pn = this.dom.parentNode;
17448 if(shim.dom.parentNode != pn){
17449 pn.insertBefore(shim.dom, this.dom);
17451 shim.setStyle('z-index', this.getZIndex()-2);
17456 hideShim : function(){
17458 this.shim.setDisplayed(false);
17459 shims.push(this.shim);
17464 disableShadow : function(){
17466 this.shadowDisabled = true;
17467 this.shadow.hide();
17468 this.lastShadowOffset = this.shadowOffset;
17469 this.shadowOffset = 0;
17473 enableShadow : function(show){
17475 this.shadowDisabled = false;
17476 this.shadowOffset = this.lastShadowOffset;
17477 delete this.lastShadowOffset;
17485 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17486 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17487 sync : function(doShow){
17488 var sw = this.shadow;
17489 if(!this.updating && this.isVisible() && (sw || this.useShim)){
17490 var sh = this.getShim();
17492 var w = this.getWidth(),
17493 h = this.getHeight();
17495 var l = this.getLeft(true),
17496 t = this.getTop(true);
17498 if(sw && !this.shadowDisabled){
17499 if(doShow && !sw.isVisible()){
17502 sw.realign(l, t, w, h);
17508 // fit the shim behind the shadow, so it is shimmed too
17509 var a = sw.adjusts, s = sh.dom.style;
17510 s.left = (Math.min(l, l+a.l))+"px";
17511 s.top = (Math.min(t, t+a.t))+"px";
17512 s.width = (w+a.w)+"px";
17513 s.height = (h+a.h)+"px";
17520 sh.setLeftTop(l, t);
17527 destroy : function(){
17530 this.shadow.hide();
17532 this.removeAllListeners();
17533 var pn = this.dom.parentNode;
17535 pn.removeChild(this.dom);
17537 Roo.Element.uncache(this.id);
17540 remove : function(){
17545 beginUpdate : function(){
17546 this.updating = true;
17550 endUpdate : function(){
17551 this.updating = false;
17556 hideUnders : function(negOffset){
17558 this.shadow.hide();
17564 constrainXY : function(){
17565 if(this.constrain){
17566 var vw = Roo.lib.Dom.getViewWidth(),
17567 vh = Roo.lib.Dom.getViewHeight();
17568 var s = Roo.get(document).getScroll();
17570 var xy = this.getXY();
17571 var x = xy[0], y = xy[1];
17572 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17573 // only move it if it needs it
17575 // first validate right/bottom
17576 if((x + w) > vw+s.left){
17577 x = vw - w - this.shadowOffset;
17580 if((y + h) > vh+s.top){
17581 y = vh - h - this.shadowOffset;
17584 // then make sure top/left isn't negative
17595 var ay = this.avoidY;
17596 if(y <= ay && (y+h) >= ay){
17602 supr.setXY.call(this, xy);
17608 isVisible : function(){
17609 return this.visible;
17613 showAction : function(){
17614 this.visible = true; // track visibility to prevent getStyle calls
17615 if(this.useDisplay === true){
17616 this.setDisplayed("");
17617 }else if(this.lastXY){
17618 supr.setXY.call(this, this.lastXY);
17619 }else if(this.lastLT){
17620 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17625 hideAction : function(){
17626 this.visible = false;
17627 if(this.useDisplay === true){
17628 this.setDisplayed(false);
17630 this.setLeftTop(-10000,-10000);
17634 // overridden Element method
17635 setVisible : function(v, a, d, c, e){
17640 var cb = function(){
17645 }.createDelegate(this);
17646 supr.setVisible.call(this, true, true, d, cb, e);
17649 this.hideUnders(true);
17658 }.createDelegate(this);
17660 supr.setVisible.call(this, v, a, d, cb, e);
17669 storeXY : function(xy){
17670 delete this.lastLT;
17674 storeLeftTop : function(left, top){
17675 delete this.lastXY;
17676 this.lastLT = [left, top];
17680 beforeFx : function(){
17681 this.beforeAction();
17682 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17686 afterFx : function(){
17687 Roo.Layer.superclass.afterFx.apply(this, arguments);
17688 this.sync(this.isVisible());
17692 beforeAction : function(){
17693 if(!this.updating && this.shadow){
17694 this.shadow.hide();
17698 // overridden Element method
17699 setLeft : function(left){
17700 this.storeLeftTop(left, this.getTop(true));
17701 supr.setLeft.apply(this, arguments);
17705 setTop : function(top){
17706 this.storeLeftTop(this.getLeft(true), top);
17707 supr.setTop.apply(this, arguments);
17711 setLeftTop : function(left, top){
17712 this.storeLeftTop(left, top);
17713 supr.setLeftTop.apply(this, arguments);
17717 setXY : function(xy, a, d, c, e){
17719 this.beforeAction();
17721 var cb = this.createCB(c);
17722 supr.setXY.call(this, xy, a, d, cb, e);
17729 createCB : function(c){
17740 // overridden Element method
17741 setX : function(x, a, d, c, e){
17742 this.setXY([x, this.getY()], a, d, c, e);
17745 // overridden Element method
17746 setY : function(y, a, d, c, e){
17747 this.setXY([this.getX(), y], a, d, c, e);
17750 // overridden Element method
17751 setSize : function(w, h, a, d, c, e){
17752 this.beforeAction();
17753 var cb = this.createCB(c);
17754 supr.setSize.call(this, w, h, a, d, cb, e);
17760 // overridden Element method
17761 setWidth : function(w, a, d, c, e){
17762 this.beforeAction();
17763 var cb = this.createCB(c);
17764 supr.setWidth.call(this, w, a, d, cb, e);
17770 // overridden Element method
17771 setHeight : function(h, a, d, c, e){
17772 this.beforeAction();
17773 var cb = this.createCB(c);
17774 supr.setHeight.call(this, h, a, d, cb, e);
17780 // overridden Element method
17781 setBounds : function(x, y, w, h, a, d, c, e){
17782 this.beforeAction();
17783 var cb = this.createCB(c);
17785 this.storeXY([x, y]);
17786 supr.setXY.call(this, [x, y]);
17787 supr.setSize.call(this, w, h, a, d, cb, e);
17790 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17796 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17797 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17798 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17799 * @param {Number} zindex The new z-index to set
17800 * @return {this} The Layer
17802 setZIndex : function(zindex){
17803 this.zindex = zindex;
17804 this.setStyle("z-index", zindex + 2);
17806 this.shadow.setZIndex(zindex + 1);
17809 this.shim.setStyle("z-index", zindex);
17814 * Original code for Roojs - LGPL
17815 * <script type="text/javascript">
17819 * @class Roo.XComponent
17820 * A delayed Element creator...
17821 * Or a way to group chunks of interface together.
17822 * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17823 * used in conjunction with XComponent.build() it will create an instance of each element,
17824 * then call addxtype() to build the User interface.
17826 * Mypart.xyx = new Roo.XComponent({
17828 parent : 'Mypart.xyz', // empty == document.element.!!
17832 disabled : function() {}
17834 tree : function() { // return an tree of xtype declared components
17838 xtype : 'NestedLayoutPanel',
17845 * It can be used to build a big heiracy, with parent etc.
17846 * or you can just use this to render a single compoent to a dom element
17847 * MYPART.render(Roo.Element | String(id) | dom_element )
17854 * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17855 * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17857 * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17859 * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17860 * - if mulitple topModules exist, the last one is defined as the top module.
17864 * When the top level or multiple modules are to embedded into a existing HTML page,
17865 * the parent element can container '#id' of the element where the module will be drawn.
17869 * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17870 * it relies more on a include mechanism, where sub modules are included into an outer page.
17871 * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17873 * Bootstrap Roo Included elements
17875 * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17876 * hence confusing the component builder as it thinks there are multiple top level elements.
17878 * String Over-ride & Translations
17880 * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17881 * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17882 * are needed. @see Roo.XComponent.overlayString
17886 * @extends Roo.util.Observable
17888 * @param cfg {Object} configuration of component
17891 Roo.XComponent = function(cfg) {
17892 Roo.apply(this, cfg);
17896 * Fires when this the componnt is built
17897 * @param {Roo.XComponent} c the component
17902 this.region = this.region || 'center'; // default..
17903 Roo.XComponent.register(this);
17904 this.modules = false;
17905 this.el = false; // where the layout goes..
17909 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17912 * The created element (with Roo.factory())
17913 * @type {Roo.Layout}
17919 * for BC - use el in new code
17920 * @type {Roo.Layout}
17926 * for BC - use el in new code
17927 * @type {Roo.Layout}
17932 * @cfg {Function|boolean} disabled
17933 * If this module is disabled by some rule, return true from the funtion
17938 * @cfg {String} parent
17939 * Name of parent element which it get xtype added to..
17944 * @cfg {String} order
17945 * Used to set the order in which elements are created (usefull for multiple tabs)
17950 * @cfg {String} name
17951 * String to display while loading.
17955 * @cfg {String} region
17956 * Region to render component to (defaults to center)
17961 * @cfg {Array} items
17962 * A single item array - the first element is the root of the tree..
17963 * It's done this way to stay compatible with the Xtype system...
17969 * The method that retuns the tree of parts that make up this compoennt
17976 * render element to dom or tree
17977 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17980 render : function(el)
17984 var hp = this.parent ? 1 : 0;
17985 Roo.debug && Roo.log(this);
17987 var tree = this._tree ? this._tree() : this.tree();
17990 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17991 // if parent is a '#.....' string, then let's use that..
17992 var ename = this.parent.substr(1);
17993 this.parent = false;
17994 Roo.debug && Roo.log(ename);
17996 case 'bootstrap-body':
17997 if (typeof(tree.el) != 'undefined' && tree.el == document.body) {
17998 // this is the BorderLayout standard?
17999 this.parent = { el : true };
18002 if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype) > -1) {
18003 // need to insert stuff...
18005 el : new Roo.bootstrap.layout.Border({
18006 el : document.body,
18012 tabPosition: 'top',
18013 //resizeTabs: true,
18014 alwaysShowTabs: true,
18024 if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18025 this.parent = { el : new Roo.bootstrap.Body() };
18026 Roo.debug && Roo.log("setting el to doc body");
18029 throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18033 this.parent = { el : true};
18036 el = Roo.get(ename);
18037 if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18038 this.parent = { el : true};
18045 if (!el && !this.parent) {
18046 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18051 Roo.debug && Roo.log("EL:");
18052 Roo.debug && Roo.log(el);
18053 Roo.debug && Roo.log("this.parent.el:");
18054 Roo.debug && Roo.log(this.parent.el);
18057 // altertive root elements ??? - we need a better way to indicate these.
18058 var is_alt = Roo.XComponent.is_alt ||
18059 (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18060 (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18061 (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18065 if (!this.parent && is_alt) {
18066 //el = Roo.get(document.body);
18067 this.parent = { el : true };
18072 if (!this.parent) {
18074 Roo.debug && Roo.log("no parent - creating one");
18076 el = el ? Roo.get(el) : false;
18078 if (typeof(Roo.BorderLayout) == 'undefined' ) {
18081 el : new Roo.bootstrap.layout.Border({
18082 el: el || document.body,
18088 tabPosition: 'top',
18089 //resizeTabs: true,
18090 alwaysShowTabs: false,
18093 overflow: 'visible'
18099 // it's a top level one..
18101 el : new Roo.BorderLayout(el || document.body, {
18106 tabPosition: 'top',
18107 //resizeTabs: true,
18108 alwaysShowTabs: el && hp? false : true,
18109 hideTabs: el || !hp ? true : false,
18117 if (!this.parent.el) {
18118 // probably an old style ctor, which has been disabled.
18122 // The 'tree' method is '_tree now'
18124 tree.region = tree.region || this.region;
18125 var is_body = false;
18126 if (this.parent.el === true) {
18127 // bootstrap... - body..
18131 this.parent.el = Roo.factory(tree);
18135 this.el = this.parent.el.addxtype(tree, undefined, is_body);
18136 this.fireEvent('built', this);
18138 this.panel = this.el;
18139 this.layout = this.panel.layout;
18140 this.parentLayout = this.parent.layout || false;
18146 Roo.apply(Roo.XComponent, {
18148 * @property hideProgress
18149 * true to disable the building progress bar.. usefull on single page renders.
18152 hideProgress : false,
18154 * @property buildCompleted
18155 * True when the builder has completed building the interface.
18158 buildCompleted : false,
18161 * @property topModule
18162 * the upper most module - uses document.element as it's constructor.
18169 * @property modules
18170 * array of modules to be created by registration system.
18171 * @type {Array} of Roo.XComponent
18176 * @property elmodules
18177 * array of modules to be created by which use #ID
18178 * @type {Array} of Roo.XComponent
18185 * Is an alternative Root - normally used by bootstrap or other systems,
18186 * where the top element in the tree can wrap 'body'
18187 * @type {boolean} (default false)
18192 * @property build_from_html
18193 * Build elements from html - used by bootstrap HTML stuff
18194 * - this is cleared after build is completed
18195 * @type {boolean} (default false)
18198 build_from_html : false,
18200 * Register components to be built later.
18202 * This solves the following issues
18203 * - Building is not done on page load, but after an authentication process has occured.
18204 * - Interface elements are registered on page load
18205 * - Parent Interface elements may not be loaded before child, so this handles that..
18212 module : 'Pman.Tab.projectMgr',
18214 parent : 'Pman.layout',
18215 disabled : false, // or use a function..
18218 * * @param {Object} details about module
18220 register : function(obj) {
18222 Roo.XComponent.event.fireEvent('register', obj);
18223 switch(typeof(obj.disabled) ) {
18229 if ( obj.disabled() ) {
18235 if (obj.disabled || obj.region == '#disabled') {
18241 this.modules.push(obj);
18245 * convert a string to an object..
18246 * eg. 'AAA.BBB' -> finds AAA.BBB
18250 toObject : function(str)
18252 if (!str || typeof(str) == 'object') {
18255 if (str.substring(0,1) == '#') {
18259 var ar = str.split('.');
18264 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18266 throw "Module not found : " + str;
18270 throw "Module not found : " + str;
18272 Roo.each(ar, function(e) {
18273 if (typeof(o[e]) == 'undefined') {
18274 throw "Module not found : " + str;
18285 * move modules into their correct place in the tree..
18288 preBuild : function ()
18291 Roo.each(this.modules , function (obj)
18293 Roo.XComponent.event.fireEvent('beforebuild', obj);
18295 var opar = obj.parent;
18297 obj.parent = this.toObject(opar);
18299 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18304 Roo.debug && Roo.log("GOT top level module");
18305 Roo.debug && Roo.log(obj);
18306 obj.modules = new Roo.util.MixedCollection(false,
18307 function(o) { return o.order + '' }
18309 this.topModule = obj;
18312 // parent is a string (usually a dom element name..)
18313 if (typeof(obj.parent) == 'string') {
18314 this.elmodules.push(obj);
18317 if (obj.parent.constructor != Roo.XComponent) {
18318 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18320 if (!obj.parent.modules) {
18321 obj.parent.modules = new Roo.util.MixedCollection(false,
18322 function(o) { return o.order + '' }
18325 if (obj.parent.disabled) {
18326 obj.disabled = true;
18328 obj.parent.modules.add(obj);
18333 * make a list of modules to build.
18334 * @return {Array} list of modules.
18337 buildOrder : function()
18340 var cmp = function(a,b) {
18341 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18343 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18344 throw "No top level modules to build";
18347 // make a flat list in order of modules to build.
18348 var mods = this.topModule ? [ this.topModule ] : [];
18351 // elmodules (is a list of DOM based modules )
18352 Roo.each(this.elmodules, function(e) {
18354 if (!this.topModule &&
18355 typeof(e.parent) == 'string' &&
18356 e.parent.substring(0,1) == '#' &&
18357 Roo.get(e.parent.substr(1))
18360 _this.topModule = e;
18366 // add modules to their parents..
18367 var addMod = function(m) {
18368 Roo.debug && Roo.log("build Order: add: " + m.name);
18371 if (m.modules && !m.disabled) {
18372 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18373 m.modules.keySort('ASC', cmp );
18374 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18376 m.modules.each(addMod);
18378 Roo.debug && Roo.log("build Order: no child modules");
18380 // not sure if this is used any more..
18382 m.finalize.name = m.name + " (clean up) ";
18383 mods.push(m.finalize);
18387 if (this.topModule && this.topModule.modules) {
18388 this.topModule.modules.keySort('ASC', cmp );
18389 this.topModule.modules.each(addMod);
18395 * Build the registered modules.
18396 * @param {Object} parent element.
18397 * @param {Function} optional method to call after module has been added.
18401 build : function(opts)
18404 if (typeof(opts) != 'undefined') {
18405 Roo.apply(this,opts);
18409 var mods = this.buildOrder();
18411 //this.allmods = mods;
18412 //Roo.debug && Roo.log(mods);
18414 if (!mods.length) { // should not happen
18415 throw "NO modules!!!";
18419 var msg = "Building Interface...";
18420 // flash it up as modal - so we store the mask!?
18421 if (!this.hideProgress && Roo.MessageBox) {
18422 Roo.MessageBox.show({ title: 'loading' });
18423 Roo.MessageBox.show({
18424 title: "Please wait...",
18434 var total = mods.length;
18437 var progressRun = function() {
18438 if (!mods.length) {
18439 Roo.debug && Roo.log('hide?');
18440 if (!this.hideProgress && Roo.MessageBox) {
18441 Roo.MessageBox.hide();
18443 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18445 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18451 var m = mods.shift();
18454 Roo.debug && Roo.log(m);
18455 // not sure if this is supported any more.. - modules that are are just function
18456 if (typeof(m) == 'function') {
18458 return progressRun.defer(10, _this);
18462 msg = "Building Interface " + (total - mods.length) +
18464 (m.name ? (' - ' + m.name) : '');
18465 Roo.debug && Roo.log(msg);
18466 if (!_this.hideProgress && Roo.MessageBox) {
18467 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
18471 // is the module disabled?
18472 var disabled = (typeof(m.disabled) == 'function') ?
18473 m.disabled.call(m.module.disabled) : m.disabled;
18477 return progressRun(); // we do not update the display!
18485 // it's 10 on top level, and 1 on others??? why...
18486 return progressRun.defer(10, _this);
18489 progressRun.defer(1, _this);
18495 * Overlay a set of modified strings onto a component
18496 * This is dependant on our builder exporting the strings and 'named strings' elements.
18498 * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18499 * @param {Object} associative array of 'named' string and it's new value.
18502 overlayStrings : function( component, strings )
18504 if (typeof(component['_named_strings']) == 'undefined') {
18505 throw "ERROR: component does not have _named_strings";
18507 for ( var k in strings ) {
18508 var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18509 if (md !== false) {
18510 component['_strings'][md] = strings[k];
18512 Roo.log('could not find named string: ' + k + ' in');
18513 Roo.log(component);
18528 * wrapper for event.on - aliased later..
18529 * Typically use to register a event handler for register:
18531 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18540 Roo.XComponent.event = new Roo.util.Observable({
18544 * Fires when an Component is registered,
18545 * set the disable property on the Component to stop registration.
18546 * @param {Roo.XComponent} c the component being registerd.
18551 * @event beforebuild
18552 * Fires before each Component is built
18553 * can be used to apply permissions.
18554 * @param {Roo.XComponent} c the component being registerd.
18557 'beforebuild' : true,
18559 * @event buildcomplete
18560 * Fires on the top level element when all elements have been built
18561 * @param {Roo.XComponent} the top level component.
18563 'buildcomplete' : true
18568 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
18571 * marked - a markdown parser
18572 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18573 * https://github.com/chjj/marked
18579 * Roo.Markdown - is a very crude wrapper around marked..
18583 * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18585 * Note: move the sample code to the bottom of this
18586 * file before uncommenting it.
18591 Roo.Markdown.toHtml = function(text) {
18593 var c = new Roo.Markdown.marked.setOptions({
18594 renderer: new Roo.Markdown.marked.Renderer(),
18605 text = text.replace(/\\\n/g,' ');
18606 return Roo.Markdown.marked(text);
18611 // Wraps all "globals" so that the only thing
18612 // exposed is makeHtml().
18618 * eval:var:unescape
18626 var escape = function (html, encode) {
18628 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18629 .replace(/</g, '<')
18630 .replace(/>/g, '>')
18631 .replace(/"/g, '"')
18632 .replace(/'/g, ''');
18635 var unescape = function (html) {
18636 // explicitly match decimal, hex, and named HTML entities
18637 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18638 n = n.toLowerCase();
18639 if (n === 'colon') { return ':'; }
18640 if (n.charAt(0) === '#') {
18641 return n.charAt(1) === 'x'
18642 ? String.fromCharCode(parseInt(n.substring(2), 16))
18643 : String.fromCharCode(+n.substring(1));
18649 var replace = function (regex, opt) {
18650 regex = regex.source;
18652 return function self(name, val) {
18653 if (!name) { return new RegExp(regex, opt); }
18654 val = val.source || val;
18655 val = val.replace(/(^|[^\[])\^/g, '$1');
18656 regex = regex.replace(name, val);
18665 var noop = function () {}
18671 var merge = function (obj) {
18676 for (; i < arguments.length; i++) {
18677 target = arguments[i];
18678 for (key in target) {
18679 if (Object.prototype.hasOwnProperty.call(target, key)) {
18680 obj[key] = target[key];
18690 * Block-Level Grammar
18698 code: /^( {4}[^\n]+\n*)+/,
18700 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18701 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18703 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18704 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18705 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18706 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18707 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18709 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18713 block.bullet = /(?:[*+-]|\d+\.)/;
18714 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18715 block.item = replace(block.item, 'gm')
18716 (/bull/g, block.bullet)
18719 block.list = replace(block.list)
18720 (/bull/g, block.bullet)
18721 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18722 ('def', '\\n+(?=' + block.def.source + ')')
18725 block.blockquote = replace(block.blockquote)
18729 block._tag = '(?!(?:'
18730 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18731 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18732 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18734 block.html = replace(block.html)
18735 ('comment', /<!--[\s\S]*?-->/)
18736 ('closed', /<(tag)[\s\S]+?<\/\1>/)
18737 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18738 (/tag/g, block._tag)
18741 block.paragraph = replace(block.paragraph)
18743 ('heading', block.heading)
18744 ('lheading', block.lheading)
18745 ('blockquote', block.blockquote)
18746 ('tag', '<' + block._tag)
18751 * Normal Block Grammar
18754 block.normal = merge({}, block);
18757 * GFM Block Grammar
18760 block.gfm = merge({}, block.normal, {
18761 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18763 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18766 block.gfm.paragraph = replace(block.paragraph)
18768 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18769 + block.list.source.replace('\\1', '\\3') + '|')
18773 * GFM + Tables Block Grammar
18776 block.tables = merge({}, block.gfm, {
18777 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18778 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18785 var Lexer = function (options) {
18787 this.tokens.links = {};
18788 this.options = options || marked.defaults;
18789 this.rules = block.normal;
18791 if (this.options.gfm) {
18792 if (this.options.tables) {
18793 this.rules = block.tables;
18795 this.rules = block.gfm;
18801 * Expose Block Rules
18804 Lexer.rules = block;
18807 * Static Lex Method
18810 Lexer.lex = function(src, options) {
18811 var lexer = new Lexer(options);
18812 return lexer.lex(src);
18819 Lexer.prototype.lex = function(src) {
18821 .replace(/\r\n|\r/g, '\n')
18822 .replace(/\t/g, ' ')
18823 .replace(/\u00a0/g, ' ')
18824 .replace(/\u2424/g, '\n');
18826 return this.token(src, true);
18833 Lexer.prototype.token = function(src, top, bq) {
18834 var src = src.replace(/^ +$/gm, '')
18847 if (cap = this.rules.newline.exec(src)) {
18848 src = src.substring(cap[0].length);
18849 if (cap[0].length > 1) {
18857 if (cap = this.rules.code.exec(src)) {
18858 src = src.substring(cap[0].length);
18859 cap = cap[0].replace(/^ {4}/gm, '');
18862 text: !this.options.pedantic
18863 ? cap.replace(/\n+$/, '')
18870 if (cap = this.rules.fences.exec(src)) {
18871 src = src.substring(cap[0].length);
18881 if (cap = this.rules.heading.exec(src)) {
18882 src = src.substring(cap[0].length);
18885 depth: cap[1].length,
18891 // table no leading pipe (gfm)
18892 if (top && (cap = this.rules.nptable.exec(src))) {
18893 src = src.substring(cap[0].length);
18897 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18898 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18899 cells: cap[3].replace(/\n$/, '').split('\n')
18902 for (i = 0; i < item.align.length; i++) {
18903 if (/^ *-+: *$/.test(item.align[i])) {
18904 item.align[i] = 'right';
18905 } else if (/^ *:-+: *$/.test(item.align[i])) {
18906 item.align[i] = 'center';
18907 } else if (/^ *:-+ *$/.test(item.align[i])) {
18908 item.align[i] = 'left';
18910 item.align[i] = null;
18914 for (i = 0; i < item.cells.length; i++) {
18915 item.cells[i] = item.cells[i].split(/ *\| */);
18918 this.tokens.push(item);
18924 if (cap = this.rules.lheading.exec(src)) {
18925 src = src.substring(cap[0].length);
18928 depth: cap[2] === '=' ? 1 : 2,
18935 if (cap = this.rules.hr.exec(src)) {
18936 src = src.substring(cap[0].length);
18944 if (cap = this.rules.blockquote.exec(src)) {
18945 src = src.substring(cap[0].length);
18948 type: 'blockquote_start'
18951 cap = cap[0].replace(/^ *> ?/gm, '');
18953 // Pass `top` to keep the current
18954 // "toplevel" state. This is exactly
18955 // how markdown.pl works.
18956 this.token(cap, top, true);
18959 type: 'blockquote_end'
18966 if (cap = this.rules.list.exec(src)) {
18967 src = src.substring(cap[0].length);
18971 type: 'list_start',
18972 ordered: bull.length > 1
18975 // Get each top-level item.
18976 cap = cap[0].match(this.rules.item);
18982 for (; i < l; i++) {
18985 // Remove the list item's bullet
18986 // so it is seen as the next token.
18987 space = item.length;
18988 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18990 // Outdent whatever the
18991 // list item contains. Hacky.
18992 if (~item.indexOf('\n ')) {
18993 space -= item.length;
18994 item = !this.options.pedantic
18995 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18996 : item.replace(/^ {1,4}/gm, '');
18999 // Determine whether the next list item belongs here.
19000 // Backpedal if it does not belong in this list.
19001 if (this.options.smartLists && i !== l - 1) {
19002 b = block.bullet.exec(cap[i + 1])[0];
19003 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19004 src = cap.slice(i + 1).join('\n') + src;
19009 // Determine whether item is loose or not.
19010 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19011 // for discount behavior.
19012 loose = next || /\n\n(?!\s*$)/.test(item);
19014 next = item.charAt(item.length - 1) === '\n';
19015 if (!loose) { loose = next; }
19020 ? 'loose_item_start'
19021 : 'list_item_start'
19025 this.token(item, false, bq);
19028 type: 'list_item_end'
19040 if (cap = this.rules.html.exec(src)) {
19041 src = src.substring(cap[0].length);
19043 type: this.options.sanitize
19046 pre: !this.options.sanitizer
19047 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19054 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19055 src = src.substring(cap[0].length);
19056 this.tokens.links[cap[1].toLowerCase()] = {
19064 if (top && (cap = this.rules.table.exec(src))) {
19065 src = src.substring(cap[0].length);
19069 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19070 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19071 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19074 for (i = 0; i < item.align.length; i++) {
19075 if (/^ *-+: *$/.test(item.align[i])) {
19076 item.align[i] = 'right';
19077 } else if (/^ *:-+: *$/.test(item.align[i])) {
19078 item.align[i] = 'center';
19079 } else if (/^ *:-+ *$/.test(item.align[i])) {
19080 item.align[i] = 'left';
19082 item.align[i] = null;
19086 for (i = 0; i < item.cells.length; i++) {
19087 item.cells[i] = item.cells[i]
19088 .replace(/^ *\| *| *\| *$/g, '')
19092 this.tokens.push(item);
19097 // top-level paragraph
19098 if (top && (cap = this.rules.paragraph.exec(src))) {
19099 src = src.substring(cap[0].length);
19102 text: cap[1].charAt(cap[1].length - 1) === '\n'
19103 ? cap[1].slice(0, -1)
19110 if (cap = this.rules.text.exec(src)) {
19111 // Top-level should never reach here.
19112 src = src.substring(cap[0].length);
19122 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19126 return this.tokens;
19130 * Inline-Level Grammar
19134 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19135 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19137 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19138 link: /^!?\[(inside)\]\(href\)/,
19139 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19140 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19141 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19142 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19143 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19144 br: /^ {2,}\n(?!\s*$)/,
19146 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19149 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19150 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19152 inline.link = replace(inline.link)
19153 ('inside', inline._inside)
19154 ('href', inline._href)
19157 inline.reflink = replace(inline.reflink)
19158 ('inside', inline._inside)
19162 * Normal Inline Grammar
19165 inline.normal = merge({}, inline);
19168 * Pedantic Inline Grammar
19171 inline.pedantic = merge({}, inline.normal, {
19172 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19173 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19177 * GFM Inline Grammar
19180 inline.gfm = merge({}, inline.normal, {
19181 escape: replace(inline.escape)('])', '~|])')(),
19182 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19183 del: /^~~(?=\S)([\s\S]*?\S)~~/,
19184 text: replace(inline.text)
19186 ('|', '|https?://|')
19191 * GFM + Line Breaks Inline Grammar
19194 inline.breaks = merge({}, inline.gfm, {
19195 br: replace(inline.br)('{2,}', '*')(),
19196 text: replace(inline.gfm.text)('{2,}', '*')()
19200 * Inline Lexer & Compiler
19203 var InlineLexer = function (links, options) {
19204 this.options = options || marked.defaults;
19205 this.links = links;
19206 this.rules = inline.normal;
19207 this.renderer = this.options.renderer || new Renderer;
19208 this.renderer.options = this.options;
19212 Error('Tokens array requires a `links` property.');
19215 if (this.options.gfm) {
19216 if (this.options.breaks) {
19217 this.rules = inline.breaks;
19219 this.rules = inline.gfm;
19221 } else if (this.options.pedantic) {
19222 this.rules = inline.pedantic;
19227 * Expose Inline Rules
19230 InlineLexer.rules = inline;
19233 * Static Lexing/Compiling Method
19236 InlineLexer.output = function(src, links, options) {
19237 var inline = new InlineLexer(links, options);
19238 return inline.output(src);
19245 InlineLexer.prototype.output = function(src) {
19254 if (cap = this.rules.escape.exec(src)) {
19255 src = src.substring(cap[0].length);
19261 if (cap = this.rules.autolink.exec(src)) {
19262 src = src.substring(cap[0].length);
19263 if (cap[2] === '@') {
19264 text = cap[1].charAt(6) === ':'
19265 ? this.mangle(cap[1].substring(7))
19266 : this.mangle(cap[1]);
19267 href = this.mangle('mailto:') + text;
19269 text = escape(cap[1]);
19272 out += this.renderer.link(href, null, text);
19277 if (!this.inLink && (cap = this.rules.url.exec(src))) {
19278 src = src.substring(cap[0].length);
19279 text = escape(cap[1]);
19281 out += this.renderer.link(href, null, text);
19286 if (cap = this.rules.tag.exec(src)) {
19287 if (!this.inLink && /^<a /i.test(cap[0])) {
19288 this.inLink = true;
19289 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19290 this.inLink = false;
19292 src = src.substring(cap[0].length);
19293 out += this.options.sanitize
19294 ? this.options.sanitizer
19295 ? this.options.sanitizer(cap[0])
19302 if (cap = this.rules.link.exec(src)) {
19303 src = src.substring(cap[0].length);
19304 this.inLink = true;
19305 out += this.outputLink(cap, {
19309 this.inLink = false;
19314 if ((cap = this.rules.reflink.exec(src))
19315 || (cap = this.rules.nolink.exec(src))) {
19316 src = src.substring(cap[0].length);
19317 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19318 link = this.links[link.toLowerCase()];
19319 if (!link || !link.href) {
19320 out += cap[0].charAt(0);
19321 src = cap[0].substring(1) + src;
19324 this.inLink = true;
19325 out += this.outputLink(cap, link);
19326 this.inLink = false;
19331 if (cap = this.rules.strong.exec(src)) {
19332 src = src.substring(cap[0].length);
19333 out += this.renderer.strong(this.output(cap[2] || cap[1]));
19338 if (cap = this.rules.em.exec(src)) {
19339 src = src.substring(cap[0].length);
19340 out += this.renderer.em(this.output(cap[2] || cap[1]));
19345 if (cap = this.rules.code.exec(src)) {
19346 src = src.substring(cap[0].length);
19347 out += this.renderer.codespan(escape(cap[2], true));
19352 if (cap = this.rules.br.exec(src)) {
19353 src = src.substring(cap[0].length);
19354 out += this.renderer.br();
19359 if (cap = this.rules.del.exec(src)) {
19360 src = src.substring(cap[0].length);
19361 out += this.renderer.del(this.output(cap[1]));
19366 if (cap = this.rules.text.exec(src)) {
19367 src = src.substring(cap[0].length);
19368 out += this.renderer.text(escape(this.smartypants(cap[0])));
19374 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19385 InlineLexer.prototype.outputLink = function(cap, link) {
19386 var href = escape(link.href)
19387 , title = link.title ? escape(link.title) : null;
19389 return cap[0].charAt(0) !== '!'
19390 ? this.renderer.link(href, title, this.output(cap[1]))
19391 : this.renderer.image(href, title, escape(cap[1]));
19395 * Smartypants Transformations
19398 InlineLexer.prototype.smartypants = function(text) {
19399 if (!this.options.smartypants) { return text; }
19402 .replace(/---/g, '\u2014')
19404 .replace(/--/g, '\u2013')
19406 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19407 // closing singles & apostrophes
19408 .replace(/'/g, '\u2019')
19410 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19412 .replace(/"/g, '\u201d')
19414 .replace(/\.{3}/g, '\u2026');
19421 InlineLexer.prototype.mangle = function(text) {
19422 if (!this.options.mangle) { return text; }
19428 for (; i < l; i++) {
19429 ch = text.charCodeAt(i);
19430 if (Math.random() > 0.5) {
19431 ch = 'x' + ch.toString(16);
19433 out += '&#' + ch + ';';
19444 * eval:var:Renderer
19447 var Renderer = function (options) {
19448 this.options = options || {};
19451 Renderer.prototype.code = function(code, lang, escaped) {
19452 if (this.options.highlight) {
19453 var out = this.options.highlight(code, lang);
19454 if (out != null && out !== code) {
19459 // hack!!! - it's already escapeD?
19464 return '<pre><code>'
19465 + (escaped ? code : escape(code, true))
19466 + '\n</code></pre>';
19469 return '<pre><code class="'
19470 + this.options.langPrefix
19471 + escape(lang, true)
19473 + (escaped ? code : escape(code, true))
19474 + '\n</code></pre>\n';
19477 Renderer.prototype.blockquote = function(quote) {
19478 return '<blockquote>\n' + quote + '</blockquote>\n';
19481 Renderer.prototype.html = function(html) {
19485 Renderer.prototype.heading = function(text, level, raw) {
19489 + this.options.headerPrefix
19490 + raw.toLowerCase().replace(/[^\w]+/g, '-')
19498 Renderer.prototype.hr = function() {
19499 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19502 Renderer.prototype.list = function(body, ordered) {
19503 var type = ordered ? 'ol' : 'ul';
19504 return '<' + type + '>\n' + body + '</' + type + '>\n';
19507 Renderer.prototype.listitem = function(text) {
19508 return '<li>' + text + '</li>\n';
19511 Renderer.prototype.paragraph = function(text) {
19512 return '<p>' + text + '</p>\n';
19515 Renderer.prototype.table = function(header, body) {
19516 return '<table class="table table-striped">\n'
19526 Renderer.prototype.tablerow = function(content) {
19527 return '<tr>\n' + content + '</tr>\n';
19530 Renderer.prototype.tablecell = function(content, flags) {
19531 var type = flags.header ? 'th' : 'td';
19532 var tag = flags.align
19533 ? '<' + type + ' style="text-align:' + flags.align + '">'
19534 : '<' + type + '>';
19535 return tag + content + '</' + type + '>\n';
19538 // span level renderer
19539 Renderer.prototype.strong = function(text) {
19540 return '<strong>' + text + '</strong>';
19543 Renderer.prototype.em = function(text) {
19544 return '<em>' + text + '</em>';
19547 Renderer.prototype.codespan = function(text) {
19548 return '<code>' + text + '</code>';
19551 Renderer.prototype.br = function() {
19552 return this.options.xhtml ? '<br/>' : '<br>';
19555 Renderer.prototype.del = function(text) {
19556 return '<del>' + text + '</del>';
19559 Renderer.prototype.link = function(href, title, text) {
19560 if (this.options.sanitize) {
19562 var prot = decodeURIComponent(unescape(href))
19563 .replace(/[^\w:]/g, '')
19568 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19572 var out = '<a href="' + href + '"';
19574 out += ' title="' + title + '"';
19576 out += '>' + text + '</a>';
19580 Renderer.prototype.image = function(href, title, text) {
19581 var out = '<img src="' + href + '" alt="' + text + '"';
19583 out += ' title="' + title + '"';
19585 out += this.options.xhtml ? '/>' : '>';
19589 Renderer.prototype.text = function(text) {
19594 * Parsing & Compiling
19600 var Parser= function (options) {
19603 this.options = options || marked.defaults;
19604 this.options.renderer = this.options.renderer || new Renderer;
19605 this.renderer = this.options.renderer;
19606 this.renderer.options = this.options;
19610 * Static Parse Method
19613 Parser.parse = function(src, options, renderer) {
19614 var parser = new Parser(options, renderer);
19615 return parser.parse(src);
19622 Parser.prototype.parse = function(src) {
19623 this.inline = new InlineLexer(src.links, this.options, this.renderer);
19624 this.tokens = src.reverse();
19627 while (this.next()) {
19638 Parser.prototype.next = function() {
19639 return this.token = this.tokens.pop();
19643 * Preview Next Token
19646 Parser.prototype.peek = function() {
19647 return this.tokens[this.tokens.length - 1] || 0;
19651 * Parse Text Tokens
19654 Parser.prototype.parseText = function() {
19655 var body = this.token.text;
19657 while (this.peek().type === 'text') {
19658 body += '\n' + this.next().text;
19661 return this.inline.output(body);
19665 * Parse Current Token
19668 Parser.prototype.tok = function() {
19669 switch (this.token.type) {
19674 return this.renderer.hr();
19677 return this.renderer.heading(
19678 this.inline.output(this.token.text),
19683 return this.renderer.code(this.token.text,
19685 this.token.escaped);
19698 for (i = 0; i < this.token.header.length; i++) {
19699 flags = { header: true, align: this.token.align[i] };
19700 cell += this.renderer.tablecell(
19701 this.inline.output(this.token.header[i]),
19702 { header: true, align: this.token.align[i] }
19705 header += this.renderer.tablerow(cell);
19707 for (i = 0; i < this.token.cells.length; i++) {
19708 row = this.token.cells[i];
19711 for (j = 0; j < row.length; j++) {
19712 cell += this.renderer.tablecell(
19713 this.inline.output(row[j]),
19714 { header: false, align: this.token.align[j] }
19718 body += this.renderer.tablerow(cell);
19720 return this.renderer.table(header, body);
19722 case 'blockquote_start': {
19725 while (this.next().type !== 'blockquote_end') {
19726 body += this.tok();
19729 return this.renderer.blockquote(body);
19731 case 'list_start': {
19733 , ordered = this.token.ordered;
19735 while (this.next().type !== 'list_end') {
19736 body += this.tok();
19739 return this.renderer.list(body, ordered);
19741 case 'list_item_start': {
19744 while (this.next().type !== 'list_item_end') {
19745 body += this.token.type === 'text'
19750 return this.renderer.listitem(body);
19752 case 'loose_item_start': {
19755 while (this.next().type !== 'list_item_end') {
19756 body += this.tok();
19759 return this.renderer.listitem(body);
19762 var html = !this.token.pre && !this.options.pedantic
19763 ? this.inline.output(this.token.text)
19765 return this.renderer.html(html);
19767 case 'paragraph': {
19768 return this.renderer.paragraph(this.inline.output(this.token.text));
19771 return this.renderer.paragraph(this.parseText());
19783 var marked = function (src, opt, callback) {
19784 if (callback || typeof opt === 'function') {
19790 opt = merge({}, marked.defaults, opt || {});
19792 var highlight = opt.highlight
19798 tokens = Lexer.lex(src, opt)
19800 return callback(e);
19803 pending = tokens.length;
19807 var done = function(err) {
19809 opt.highlight = highlight;
19810 return callback(err);
19816 out = Parser.parse(tokens, opt);
19821 opt.highlight = highlight;
19825 : callback(null, out);
19828 if (!highlight || highlight.length < 3) {
19832 delete opt.highlight;
19834 if (!pending) { return done(); }
19836 for (; i < tokens.length; i++) {
19838 if (token.type !== 'code') {
19839 return --pending || done();
19841 return highlight(token.text, token.lang, function(err, code) {
19842 if (err) { return done(err); }
19843 if (code == null || code === token.text) {
19844 return --pending || done();
19847 token.escaped = true;
19848 --pending || done();
19856 if (opt) { opt = merge({}, marked.defaults, opt); }
19857 return Parser.parse(Lexer.lex(src, opt), opt);
19859 e.message += '\nPlease report this to https://github.com/chjj/marked.';
19860 if ((opt || marked.defaults).silent) {
19861 return '<p>An error occured:</p><pre>'
19862 + escape(e.message + '', true)
19874 marked.setOptions = function(opt) {
19875 merge(marked.defaults, opt);
19879 marked.defaults = {
19890 langPrefix: 'lang-',
19891 smartypants: false,
19893 renderer: new Renderer,
19901 marked.Parser = Parser;
19902 marked.parser = Parser.parse;
19904 marked.Renderer = Renderer;
19906 marked.Lexer = Lexer;
19907 marked.lexer = Lexer.lex;
19909 marked.InlineLexer = InlineLexer;
19910 marked.inlineLexer = InlineLexer.output;
19912 marked.parse = marked;
19914 Roo.Markdown.marked = marked;
19918 * Ext JS Library 1.1.1
19919 * Copyright(c) 2006-2007, Ext JS, LLC.
19921 * Originally Released Under LGPL - original licence link has changed is not relivant.
19924 * <script type="text/javascript">
19930 * These classes are derivatives of the similarly named classes in the YUI Library.
19931 * The original license:
19932 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19933 * Code licensed under the BSD License:
19934 * http://developer.yahoo.net/yui/license.txt
19939 var Event=Roo.EventManager;
19940 var Dom=Roo.lib.Dom;
19943 * @class Roo.dd.DragDrop
19944 * @extends Roo.util.Observable
19945 * Defines the interface and base operation of items that that can be
19946 * dragged or can be drop targets. It was designed to be extended, overriding
19947 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19948 * Up to three html elements can be associated with a DragDrop instance:
19950 * <li>linked element: the element that is passed into the constructor.
19951 * This is the element which defines the boundaries for interaction with
19952 * other DragDrop objects.</li>
19953 * <li>handle element(s): The drag operation only occurs if the element that
19954 * was clicked matches a handle element. By default this is the linked
19955 * element, but there are times that you will want only a portion of the
19956 * linked element to initiate the drag operation, and the setHandleElId()
19957 * method provides a way to define this.</li>
19958 * <li>drag element: this represents the element that would be moved along
19959 * with the cursor during a drag operation. By default, this is the linked
19960 * element itself as in {@link Roo.dd.DD}. setDragElId() lets you define
19961 * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19964 * This class should not be instantiated until the onload event to ensure that
19965 * the associated elements are available.
19966 * The following would define a DragDrop obj that would interact with any
19967 * other DragDrop obj in the "group1" group:
19969 * dd = new Roo.dd.DragDrop("div1", "group1");
19971 * Since none of the event handlers have been implemented, nothing would
19972 * actually happen if you were to run the code above. Normally you would
19973 * override this class or one of the default implementations, but you can
19974 * also override the methods you want on an instance of the class...
19976 * dd.onDragDrop = function(e, id) {
19977 * alert("dd was dropped on " + id);
19981 * @param {String} id of the element that is linked to this instance
19982 * @param {String} sGroup the group of related DragDrop objects
19983 * @param {object} config an object containing configurable attributes
19984 * Valid properties for DragDrop:
19985 * padding, isTarget, maintainOffset, primaryButtonOnly
19987 Roo.dd.DragDrop = function(id, sGroup, config) {
19989 this.init(id, sGroup, config);
19994 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19997 * The id of the element associated with this object. This is what we
19998 * refer to as the "linked element" because the size and position of
19999 * this element is used to determine when the drag and drop objects have
20007 * Configuration attributes passed into the constructor
20014 * The id of the element that will be dragged. By default this is same
20015 * as the linked element , but could be changed to another element. Ex:
20017 * @property dragElId
20024 * the id of the element that initiates the drag operation. By default
20025 * this is the linked element, but could be changed to be a child of this
20026 * element. This lets us do things like only starting the drag when the
20027 * header element within the linked html element is clicked.
20028 * @property handleElId
20035 * An associative array of HTML tags that will be ignored if clicked.
20036 * @property invalidHandleTypes
20037 * @type {string: string}
20039 invalidHandleTypes: null,
20042 * An associative array of ids for elements that will be ignored if clicked
20043 * @property invalidHandleIds
20044 * @type {string: string}
20046 invalidHandleIds: null,
20049 * An indexted array of css class names for elements that will be ignored
20051 * @property invalidHandleClasses
20054 invalidHandleClasses: null,
20057 * The linked element's absolute X position at the time the drag was
20059 * @property startPageX
20066 * The linked element's absolute X position at the time the drag was
20068 * @property startPageY
20075 * The group defines a logical collection of DragDrop objects that are
20076 * related. Instances only get events when interacting with other
20077 * DragDrop object in the same group. This lets us define multiple
20078 * groups using a single DragDrop subclass if we want.
20080 * @type {string: string}
20085 * Individual drag/drop instances can be locked. This will prevent
20086 * onmousedown start drag.
20094 * Lock this instance
20097 lock: function() { this.locked = true; },
20100 * Unlock this instace
20103 unlock: function() { this.locked = false; },
20106 * By default, all insances can be a drop target. This can be disabled by
20107 * setting isTarget to false.
20114 * The padding configured for this drag and drop object for calculating
20115 * the drop zone intersection with this object.
20122 * Cached reference to the linked element
20123 * @property _domRef
20129 * Internal typeof flag
20130 * @property __ygDragDrop
20133 __ygDragDrop: true,
20136 * Set to true when horizontal contraints are applied
20137 * @property constrainX
20144 * Set to true when vertical contraints are applied
20145 * @property constrainY
20152 * The left constraint
20160 * The right constraint
20168 * The up constraint
20177 * The down constraint
20185 * Maintain offsets when we resetconstraints. Set to true when you want
20186 * the position of the element relative to its parent to stay the same
20187 * when the page changes
20189 * @property maintainOffset
20192 maintainOffset: false,
20195 * Array of pixel locations the element will snap to if we specified a
20196 * horizontal graduation/interval. This array is generated automatically
20197 * when you define a tick interval.
20204 * Array of pixel locations the element will snap to if we specified a
20205 * vertical graduation/interval. This array is generated automatically
20206 * when you define a tick interval.
20213 * By default the drag and drop instance will only respond to the primary
20214 * button click (left button for a right-handed mouse). Set to true to
20215 * allow drag and drop to start with any mouse click that is propogated
20217 * @property primaryButtonOnly
20220 primaryButtonOnly: true,
20223 * The availabe property is false until the linked dom element is accessible.
20224 * @property available
20230 * By default, drags can only be initiated if the mousedown occurs in the
20231 * region the linked element is. This is done in part to work around a
20232 * bug in some browsers that mis-report the mousedown if the previous
20233 * mouseup happened outside of the window. This property is set to true
20234 * if outer handles are defined.
20236 * @property hasOuterHandles
20240 hasOuterHandles: false,
20243 * Code that executes immediately before the startDrag event
20244 * @method b4StartDrag
20247 b4StartDrag: function(x, y) { },
20250 * Abstract method called after a drag/drop object is clicked
20251 * and the drag or mousedown time thresholds have beeen met.
20252 * @method startDrag
20253 * @param {int} X click location
20254 * @param {int} Y click location
20256 startDrag: function(x, y) { /* override this */ },
20259 * Code that executes immediately before the onDrag event
20263 b4Drag: function(e) { },
20266 * Abstract method called during the onMouseMove event while dragging an
20269 * @param {Event} e the mousemove event
20271 onDrag: function(e) { /* override this */ },
20274 * Abstract method called when this element fist begins hovering over
20275 * another DragDrop obj
20276 * @method onDragEnter
20277 * @param {Event} e the mousemove event
20278 * @param {String|DragDrop[]} id In POINT mode, the element
20279 * id this is hovering over. In INTERSECT mode, an array of one or more
20280 * dragdrop items being hovered over.
20282 onDragEnter: function(e, id) { /* override this */ },
20285 * Code that executes immediately before the onDragOver event
20286 * @method b4DragOver
20289 b4DragOver: function(e) { },
20292 * Abstract method called when this element is hovering over another
20294 * @method onDragOver
20295 * @param {Event} e the mousemove event
20296 * @param {String|DragDrop[]} id In POINT mode, the element
20297 * id this is hovering over. In INTERSECT mode, an array of dd items
20298 * being hovered over.
20300 onDragOver: function(e, id) { /* override this */ },
20303 * Code that executes immediately before the onDragOut event
20304 * @method b4DragOut
20307 b4DragOut: function(e) { },
20310 * Abstract method called when we are no longer hovering over an element
20311 * @method onDragOut
20312 * @param {Event} e the mousemove event
20313 * @param {String|DragDrop[]} id In POINT mode, the element
20314 * id this was hovering over. In INTERSECT mode, an array of dd items
20315 * that the mouse is no longer over.
20317 onDragOut: function(e, id) { /* override this */ },
20320 * Code that executes immediately before the onDragDrop event
20321 * @method b4DragDrop
20324 b4DragDrop: function(e) { },
20327 * Abstract method called when this item is dropped on another DragDrop
20329 * @method onDragDrop
20330 * @param {Event} e the mouseup event
20331 * @param {String|DragDrop[]} id In POINT mode, the element
20332 * id this was dropped on. In INTERSECT mode, an array of dd items this
20335 onDragDrop: function(e, id) { /* override this */ },
20338 * Abstract method called when this item is dropped on an area with no
20340 * @method onInvalidDrop
20341 * @param {Event} e the mouseup event
20343 onInvalidDrop: function(e) { /* override this */ },
20346 * Code that executes immediately before the endDrag event
20347 * @method b4EndDrag
20350 b4EndDrag: function(e) { },
20353 * Fired when we are done dragging the object
20355 * @param {Event} e the mouseup event
20357 endDrag: function(e) { /* override this */ },
20360 * Code executed immediately before the onMouseDown event
20361 * @method b4MouseDown
20362 * @param {Event} e the mousedown event
20365 b4MouseDown: function(e) { },
20368 * Event handler that fires when a drag/drop obj gets a mousedown
20369 * @method onMouseDown
20370 * @param {Event} e the mousedown event
20372 onMouseDown: function(e) { /* override this */ },
20375 * Event handler that fires when a drag/drop obj gets a mouseup
20376 * @method onMouseUp
20377 * @param {Event} e the mouseup event
20379 onMouseUp: function(e) { /* override this */ },
20382 * Override the onAvailable method to do what is needed after the initial
20383 * position was determined.
20384 * @method onAvailable
20386 onAvailable: function () {
20390 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20393 defaultPadding : {left:0, right:0, top:0, bottom:0},
20396 * Initializes the drag drop object's constraints to restrict movement to a certain element.
20400 var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20401 { dragElId: "existingProxyDiv" });
20402 dd.startDrag = function(){
20403 this.constrainTo("parent-id");
20406 * Or you can initalize it using the {@link Roo.Element} object:
20408 Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20409 startDrag : function(){
20410 this.constrainTo("parent-id");
20414 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20415 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20416 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20417 * an object containing the sides to pad. For example: {right:10, bottom:10}
20418 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20420 constrainTo : function(constrainTo, pad, inContent){
20421 if(typeof pad == "number"){
20422 pad = {left: pad, right:pad, top:pad, bottom:pad};
20424 pad = pad || this.defaultPadding;
20425 var b = Roo.get(this.getEl()).getBox();
20426 var ce = Roo.get(constrainTo);
20427 var s = ce.getScroll();
20428 var c, cd = ce.dom;
20429 if(cd == document.body){
20430 c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20433 c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20437 var topSpace = b.y - c.y;
20438 var leftSpace = b.x - c.x;
20440 this.resetConstraints();
20441 this.setXConstraint(leftSpace - (pad.left||0), // left
20442 c.width - leftSpace - b.width - (pad.right||0) //right
20444 this.setYConstraint(topSpace - (pad.top||0), //top
20445 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20450 * Returns a reference to the linked element
20452 * @return {HTMLElement} the html element
20454 getEl: function() {
20455 if (!this._domRef) {
20456 this._domRef = Roo.getDom(this.id);
20459 return this._domRef;
20463 * Returns a reference to the actual element to drag. By default this is
20464 * the same as the html element, but it can be assigned to another
20465 * element. An example of this can be found in Roo.dd.DDProxy
20466 * @method getDragEl
20467 * @return {HTMLElement} the html element
20469 getDragEl: function() {
20470 return Roo.getDom(this.dragElId);
20474 * Sets up the DragDrop object. Must be called in the constructor of any
20475 * Roo.dd.DragDrop subclass
20477 * @param id the id of the linked element
20478 * @param {String} sGroup the group of related items
20479 * @param {object} config configuration attributes
20481 init: function(id, sGroup, config) {
20482 this.initTarget(id, sGroup, config);
20483 if (!Roo.isTouch) {
20484 Event.on(this.id, "mousedown", this.handleMouseDown, this);
20486 Event.on(this.id, "touchstart", this.handleMouseDown, this);
20487 // Event.on(this.id, "selectstart", Event.preventDefault);
20491 * Initializes Targeting functionality only... the object does not
20492 * get a mousedown handler.
20493 * @method initTarget
20494 * @param id the id of the linked element
20495 * @param {String} sGroup the group of related items
20496 * @param {object} config configuration attributes
20498 initTarget: function(id, sGroup, config) {
20500 // configuration attributes
20501 this.config = config || {};
20503 // create a local reference to the drag and drop manager
20504 this.DDM = Roo.dd.DDM;
20505 // initialize the groups array
20508 // assume that we have an element reference instead of an id if the
20509 // parameter is not a string
20510 if (typeof id !== "string") {
20517 // add to an interaction group
20518 this.addToGroup((sGroup) ? sGroup : "default");
20520 // We don't want to register this as the handle with the manager
20521 // so we just set the id rather than calling the setter.
20522 this.handleElId = id;
20524 // the linked element is the element that gets dragged by default
20525 this.setDragElId(id);
20527 // by default, clicked anchors will not start drag operations.
20528 this.invalidHandleTypes = { A: "A" };
20529 this.invalidHandleIds = {};
20530 this.invalidHandleClasses = [];
20532 this.applyConfig();
20534 this.handleOnAvailable();
20538 * Applies the configuration parameters that were passed into the constructor.
20539 * This is supposed to happen at each level through the inheritance chain. So
20540 * a DDProxy implentation will execute apply config on DDProxy, DD, and
20541 * DragDrop in order to get all of the parameters that are available in
20543 * @method applyConfig
20545 applyConfig: function() {
20547 // configurable properties:
20548 // padding, isTarget, maintainOffset, primaryButtonOnly
20549 this.padding = this.config.padding || [0, 0, 0, 0];
20550 this.isTarget = (this.config.isTarget !== false);
20551 this.maintainOffset = (this.config.maintainOffset);
20552 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20557 * Executed when the linked element is available
20558 * @method handleOnAvailable
20561 handleOnAvailable: function() {
20562 this.available = true;
20563 this.resetConstraints();
20564 this.onAvailable();
20568 * Configures the padding for the target zone in px. Effectively expands
20569 * (or reduces) the virtual object size for targeting calculations.
20570 * Supports css-style shorthand; if only one parameter is passed, all sides
20571 * will have that padding, and if only two are passed, the top and bottom
20572 * will have the first param, the left and right the second.
20573 * @method setPadding
20574 * @param {int} iTop Top pad
20575 * @param {int} iRight Right pad
20576 * @param {int} iBot Bot pad
20577 * @param {int} iLeft Left pad
20579 setPadding: function(iTop, iRight, iBot, iLeft) {
20580 // this.padding = [iLeft, iRight, iTop, iBot];
20581 if (!iRight && 0 !== iRight) {
20582 this.padding = [iTop, iTop, iTop, iTop];
20583 } else if (!iBot && 0 !== iBot) {
20584 this.padding = [iTop, iRight, iTop, iRight];
20586 this.padding = [iTop, iRight, iBot, iLeft];
20591 * Stores the initial placement of the linked element.
20592 * @method setInitialPosition
20593 * @param {int} diffX the X offset, default 0
20594 * @param {int} diffY the Y offset, default 0
20596 setInitPosition: function(diffX, diffY) {
20597 var el = this.getEl();
20599 if (!this.DDM.verifyEl(el)) {
20603 var dx = diffX || 0;
20604 var dy = diffY || 0;
20606 var p = Dom.getXY( el );
20608 this.initPageX = p[0] - dx;
20609 this.initPageY = p[1] - dy;
20611 this.lastPageX = p[0];
20612 this.lastPageY = p[1];
20615 this.setStartPosition(p);
20619 * Sets the start position of the element. This is set when the obj
20620 * is initialized, the reset when a drag is started.
20621 * @method setStartPosition
20622 * @param pos current position (from previous lookup)
20625 setStartPosition: function(pos) {
20626 var p = pos || Dom.getXY( this.getEl() );
20627 this.deltaSetXY = null;
20629 this.startPageX = p[0];
20630 this.startPageY = p[1];
20634 * Add this instance to a group of related drag/drop objects. All
20635 * instances belong to at least one group, and can belong to as many
20636 * groups as needed.
20637 * @method addToGroup
20638 * @param sGroup {string} the name of the group
20640 addToGroup: function(sGroup) {
20641 this.groups[sGroup] = true;
20642 this.DDM.regDragDrop(this, sGroup);
20646 * Remove's this instance from the supplied interaction group
20647 * @method removeFromGroup
20648 * @param {string} sGroup The group to drop
20650 removeFromGroup: function(sGroup) {
20651 if (this.groups[sGroup]) {
20652 delete this.groups[sGroup];
20655 this.DDM.removeDDFromGroup(this, sGroup);
20659 * Allows you to specify that an element other than the linked element
20660 * will be moved with the cursor during a drag
20661 * @method setDragElId
20662 * @param id {string} the id of the element that will be used to initiate the drag
20664 setDragElId: function(id) {
20665 this.dragElId = id;
20669 * Allows you to specify a child of the linked element that should be
20670 * used to initiate the drag operation. An example of this would be if
20671 * you have a content div with text and links. Clicking anywhere in the
20672 * content area would normally start the drag operation. Use this method
20673 * to specify that an element inside of the content div is the element
20674 * that starts the drag operation.
20675 * @method setHandleElId
20676 * @param id {string} the id of the element that will be used to
20677 * initiate the drag.
20679 setHandleElId: function(id) {
20680 if (typeof id !== "string") {
20683 this.handleElId = id;
20684 this.DDM.regHandle(this.id, id);
20688 * Allows you to set an element outside of the linked element as a drag
20690 * @method setOuterHandleElId
20691 * @param id the id of the element that will be used to initiate the drag
20693 setOuterHandleElId: function(id) {
20694 if (typeof id !== "string") {
20697 Event.on(id, "mousedown",
20698 this.handleMouseDown, this);
20699 this.setHandleElId(id);
20701 this.hasOuterHandles = true;
20705 * Remove all drag and drop hooks for this element
20708 unreg: function() {
20709 Event.un(this.id, "mousedown",
20710 this.handleMouseDown);
20711 Event.un(this.id, "touchstart",
20712 this.handleMouseDown);
20713 this._domRef = null;
20714 this.DDM._remove(this);
20717 destroy : function(){
20722 * Returns true if this instance is locked, or the drag drop mgr is locked
20723 * (meaning that all drag/drop is disabled on the page.)
20725 * @return {boolean} true if this obj or all drag/drop is locked, else
20728 isLocked: function() {
20729 return (this.DDM.isLocked() || this.locked);
20733 * Fired when this object is clicked
20734 * @method handleMouseDown
20736 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20739 handleMouseDown: function(e, oDD){
20741 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20742 //Roo.log('not touch/ button !=0');
20745 if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20746 return; // double touch..
20750 if (this.isLocked()) {
20751 //Roo.log('locked');
20755 this.DDM.refreshCache(this.groups);
20756 // Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20757 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20758 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
20759 //Roo.log('no outer handes or not over target');
20762 // Roo.log('check validator');
20763 if (this.clickValidator(e)) {
20764 // Roo.log('validate success');
20765 // set the initial element position
20766 this.setStartPosition();
20769 this.b4MouseDown(e);
20770 this.onMouseDown(e);
20772 this.DDM.handleMouseDown(e, this);
20774 this.DDM.stopEvent(e);
20782 clickValidator: function(e) {
20783 var target = e.getTarget();
20784 return ( this.isValidHandleChild(target) &&
20785 (this.id == this.handleElId ||
20786 this.DDM.handleWasClicked(target, this.id)) );
20790 * Allows you to specify a tag name that should not start a drag operation
20791 * when clicked. This is designed to facilitate embedding links within a
20792 * drag handle that do something other than start the drag.
20793 * @method addInvalidHandleType
20794 * @param {string} tagName the type of element to exclude
20796 addInvalidHandleType: function(tagName) {
20797 var type = tagName.toUpperCase();
20798 this.invalidHandleTypes[type] = type;
20802 * Lets you to specify an element id for a child of a drag handle
20803 * that should not initiate a drag
20804 * @method addInvalidHandleId
20805 * @param {string} id the element id of the element you wish to ignore
20807 addInvalidHandleId: function(id) {
20808 if (typeof id !== "string") {
20811 this.invalidHandleIds[id] = id;
20815 * Lets you specify a css class of elements that will not initiate a drag
20816 * @method addInvalidHandleClass
20817 * @param {string} cssClass the class of the elements you wish to ignore
20819 addInvalidHandleClass: function(cssClass) {
20820 this.invalidHandleClasses.push(cssClass);
20824 * Unsets an excluded tag name set by addInvalidHandleType
20825 * @method removeInvalidHandleType
20826 * @param {string} tagName the type of element to unexclude
20828 removeInvalidHandleType: function(tagName) {
20829 var type = tagName.toUpperCase();
20830 // this.invalidHandleTypes[type] = null;
20831 delete this.invalidHandleTypes[type];
20835 * Unsets an invalid handle id
20836 * @method removeInvalidHandleId
20837 * @param {string} id the id of the element to re-enable
20839 removeInvalidHandleId: function(id) {
20840 if (typeof id !== "string") {
20843 delete this.invalidHandleIds[id];
20847 * Unsets an invalid css class
20848 * @method removeInvalidHandleClass
20849 * @param {string} cssClass the class of the element(s) you wish to
20852 removeInvalidHandleClass: function(cssClass) {
20853 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20854 if (this.invalidHandleClasses[i] == cssClass) {
20855 delete this.invalidHandleClasses[i];
20861 * Checks the tag exclusion list to see if this click should be ignored
20862 * @method isValidHandleChild
20863 * @param {HTMLElement} node the HTMLElement to evaluate
20864 * @return {boolean} true if this is a valid tag type, false if not
20866 isValidHandleChild: function(node) {
20869 // var n = (node.nodeName == "#text") ? node.parentNode : node;
20872 nodeName = node.nodeName.toUpperCase();
20874 nodeName = node.nodeName;
20876 valid = valid && !this.invalidHandleTypes[nodeName];
20877 valid = valid && !this.invalidHandleIds[node.id];
20879 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20880 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20889 * Create the array of horizontal tick marks if an interval was specified
20890 * in setXConstraint().
20891 * @method setXTicks
20894 setXTicks: function(iStartX, iTickSize) {
20896 this.xTickSize = iTickSize;
20900 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20902 this.xTicks[this.xTicks.length] = i;
20907 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20909 this.xTicks[this.xTicks.length] = i;
20914 this.xTicks.sort(this.DDM.numericSort) ;
20918 * Create the array of vertical tick marks if an interval was specified in
20919 * setYConstraint().
20920 * @method setYTicks
20923 setYTicks: function(iStartY, iTickSize) {
20925 this.yTickSize = iTickSize;
20929 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20931 this.yTicks[this.yTicks.length] = i;
20936 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20938 this.yTicks[this.yTicks.length] = i;
20943 this.yTicks.sort(this.DDM.numericSort) ;
20947 * By default, the element can be dragged any place on the screen. Use
20948 * this method to limit the horizontal travel of the element. Pass in
20949 * 0,0 for the parameters if you want to lock the drag to the y axis.
20950 * @method setXConstraint
20951 * @param {int} iLeft the number of pixels the element can move to the left
20952 * @param {int} iRight the number of pixels the element can move to the
20954 * @param {int} iTickSize optional parameter for specifying that the
20956 * should move iTickSize pixels at a time.
20958 setXConstraint: function(iLeft, iRight, iTickSize) {
20959 this.leftConstraint = iLeft;
20960 this.rightConstraint = iRight;
20962 this.minX = this.initPageX - iLeft;
20963 this.maxX = this.initPageX + iRight;
20964 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20966 this.constrainX = true;
20970 * Clears any constraints applied to this instance. Also clears ticks
20971 * since they can't exist independent of a constraint at this time.
20972 * @method clearConstraints
20974 clearConstraints: function() {
20975 this.constrainX = false;
20976 this.constrainY = false;
20981 * Clears any tick interval defined for this instance
20982 * @method clearTicks
20984 clearTicks: function() {
20985 this.xTicks = null;
20986 this.yTicks = null;
20987 this.xTickSize = 0;
20988 this.yTickSize = 0;
20992 * By default, the element can be dragged any place on the screen. Set
20993 * this to limit the vertical travel of the element. Pass in 0,0 for the
20994 * parameters if you want to lock the drag to the x axis.
20995 * @method setYConstraint
20996 * @param {int} iUp the number of pixels the element can move up
20997 * @param {int} iDown the number of pixels the element can move down
20998 * @param {int} iTickSize optional parameter for specifying that the
20999 * element should move iTickSize pixels at a time.
21001 setYConstraint: function(iUp, iDown, iTickSize) {
21002 this.topConstraint = iUp;
21003 this.bottomConstraint = iDown;
21005 this.minY = this.initPageY - iUp;
21006 this.maxY = this.initPageY + iDown;
21007 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21009 this.constrainY = true;
21014 * resetConstraints must be called if you manually reposition a dd element.
21015 * @method resetConstraints
21016 * @param {boolean} maintainOffset
21018 resetConstraints: function() {
21021 // Maintain offsets if necessary
21022 if (this.initPageX || this.initPageX === 0) {
21023 // figure out how much this thing has moved
21024 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21025 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21027 this.setInitPosition(dx, dy);
21029 // This is the first time we have detected the element's position
21031 this.setInitPosition();
21034 if (this.constrainX) {
21035 this.setXConstraint( this.leftConstraint,
21036 this.rightConstraint,
21040 if (this.constrainY) {
21041 this.setYConstraint( this.topConstraint,
21042 this.bottomConstraint,
21048 * Normally the drag element is moved pixel by pixel, but we can specify
21049 * that it move a number of pixels at a time. This method resolves the
21050 * location when we have it set up like this.
21052 * @param {int} val where we want to place the object
21053 * @param {int[]} tickArray sorted array of valid points
21054 * @return {int} the closest tick
21057 getTick: function(val, tickArray) {
21060 // If tick interval is not defined, it is effectively 1 pixel,
21061 // so we return the value passed to us.
21063 } else if (tickArray[0] >= val) {
21064 // The value is lower than the first tick, so we return the first
21066 return tickArray[0];
21068 for (var i=0, len=tickArray.length; i<len; ++i) {
21070 if (tickArray[next] && tickArray[next] >= val) {
21071 var diff1 = val - tickArray[i];
21072 var diff2 = tickArray[next] - val;
21073 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21077 // The value is larger than the last tick, so we return the last
21079 return tickArray[tickArray.length - 1];
21086 * @return {string} string representation of the dd obj
21088 toString: function() {
21089 return ("DragDrop " + this.id);
21097 * Ext JS Library 1.1.1
21098 * Copyright(c) 2006-2007, Ext JS, LLC.
21100 * Originally Released Under LGPL - original licence link has changed is not relivant.
21103 * <script type="text/javascript">
21108 * The drag and drop utility provides a framework for building drag and drop
21109 * applications. In addition to enabling drag and drop for specific elements,
21110 * the drag and drop elements are tracked by the manager class, and the
21111 * interactions between the various elements are tracked during the drag and
21112 * the implementing code is notified about these important moments.
21115 // Only load the library once. Rewriting the manager class would orphan
21116 // existing drag and drop instances.
21117 if (!Roo.dd.DragDropMgr) {
21120 * @class Roo.dd.DragDropMgr
21121 * DragDropMgr is a singleton that tracks the element interaction for
21122 * all DragDrop items in the window. Generally, you will not call
21123 * this class directly, but it does have helper methods that could
21124 * be useful in your DragDrop implementations.
21127 Roo.dd.DragDropMgr = function() {
21129 var Event = Roo.EventManager;
21134 * Two dimensional Array of registered DragDrop objects. The first
21135 * dimension is the DragDrop item group, the second the DragDrop
21138 * @type {string: string}
21145 * Array of element ids defined as drag handles. Used to determine
21146 * if the element that generated the mousedown event is actually the
21147 * handle and not the html element itself.
21148 * @property handleIds
21149 * @type {string: string}
21156 * the DragDrop object that is currently being dragged
21157 * @property dragCurrent
21165 * the DragDrop object(s) that are being hovered over
21166 * @property dragOvers
21174 * the X distance between the cursor and the object being dragged
21183 * the Y distance between the cursor and the object being dragged
21192 * Flag to determine if we should prevent the default behavior of the
21193 * events we define. By default this is true, but this can be set to
21194 * false if you need the default behavior (not recommended)
21195 * @property preventDefault
21199 preventDefault: true,
21202 * Flag to determine if we should stop the propagation of the events
21203 * we generate. This is true by default but you may want to set it to
21204 * false if the html element contains other features that require the
21206 * @property stopPropagation
21210 stopPropagation: true,
21213 * Internal flag that is set to true when drag and drop has been
21215 * @property initialized
21222 * All drag and drop can be disabled.
21230 * Called the first time an element is registered.
21236 this.initialized = true;
21240 * In point mode, drag and drop interaction is defined by the
21241 * location of the cursor during the drag/drop
21249 * In intersect mode, drag and drop interactio nis defined by the
21250 * overlap of two or more drag and drop objects.
21251 * @property INTERSECT
21258 * The current drag and drop mode. Default: POINT
21266 * Runs method on all drag and drop objects
21267 * @method _execOnAll
21271 _execOnAll: function(sMethod, args) {
21272 for (var i in this.ids) {
21273 for (var j in this.ids[i]) {
21274 var oDD = this.ids[i][j];
21275 if (! this.isTypeOfDD(oDD)) {
21278 oDD[sMethod].apply(oDD, args);
21284 * Drag and drop initialization. Sets up the global event handlers
21289 _onLoad: function() {
21293 if (!Roo.isTouch) {
21294 Event.on(document, "mouseup", this.handleMouseUp, this, true);
21295 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21297 Event.on(document, "touchend", this.handleMouseUp, this, true);
21298 Event.on(document, "touchmove", this.handleMouseMove, this, true);
21300 Event.on(window, "unload", this._onUnload, this, true);
21301 Event.on(window, "resize", this._onResize, this, true);
21302 // Event.on(window, "mouseout", this._test);
21307 * Reset constraints on all drag and drop objs
21308 * @method _onResize
21312 _onResize: function(e) {
21313 this._execOnAll("resetConstraints", []);
21317 * Lock all drag and drop functionality
21321 lock: function() { this.locked = true; },
21324 * Unlock all drag and drop functionality
21328 unlock: function() { this.locked = false; },
21331 * Is drag and drop locked?
21333 * @return {boolean} True if drag and drop is locked, false otherwise.
21336 isLocked: function() { return this.locked; },
21339 * Location cache that is set for all drag drop objects when a drag is
21340 * initiated, cleared when the drag is finished.
21341 * @property locationCache
21348 * Set useCache to false if you want to force object the lookup of each
21349 * drag and drop linked element constantly during a drag.
21350 * @property useCache
21357 * The number of pixels that the mouse needs to move after the
21358 * mousedown before the drag is initiated. Default=3;
21359 * @property clickPixelThresh
21363 clickPixelThresh: 3,
21366 * The number of milliseconds after the mousedown event to initiate the
21367 * drag if we don't get a mouseup event. Default=1000
21368 * @property clickTimeThresh
21372 clickTimeThresh: 350,
21375 * Flag that indicates that either the drag pixel threshold or the
21376 * mousdown time threshold has been met
21377 * @property dragThreshMet
21382 dragThreshMet: false,
21385 * Timeout used for the click time threshold
21386 * @property clickTimeout
21391 clickTimeout: null,
21394 * The X position of the mousedown event stored for later use when a
21395 * drag threshold is met.
21404 * The Y position of the mousedown event stored for later use when a
21405 * drag threshold is met.
21414 * Each DragDrop instance must be registered with the DragDropMgr.
21415 * This is executed in DragDrop.init()
21416 * @method regDragDrop
21417 * @param {DragDrop} oDD the DragDrop object to register
21418 * @param {String} sGroup the name of the group this element belongs to
21421 regDragDrop: function(oDD, sGroup) {
21422 if (!this.initialized) { this.init(); }
21424 if (!this.ids[sGroup]) {
21425 this.ids[sGroup] = {};
21427 this.ids[sGroup][oDD.id] = oDD;
21431 * Removes the supplied dd instance from the supplied group. Executed
21432 * by DragDrop.removeFromGroup, so don't call this function directly.
21433 * @method removeDDFromGroup
21437 removeDDFromGroup: function(oDD, sGroup) {
21438 if (!this.ids[sGroup]) {
21439 this.ids[sGroup] = {};
21442 var obj = this.ids[sGroup];
21443 if (obj && obj[oDD.id]) {
21444 delete obj[oDD.id];
21449 * Unregisters a drag and drop item. This is executed in
21450 * DragDrop.unreg, use that method instead of calling this directly.
21455 _remove: function(oDD) {
21456 for (var g in oDD.groups) {
21457 if (g && this.ids[g][oDD.id]) {
21458 delete this.ids[g][oDD.id];
21461 delete this.handleIds[oDD.id];
21465 * Each DragDrop handle element must be registered. This is done
21466 * automatically when executing DragDrop.setHandleElId()
21467 * @method regHandle
21468 * @param {String} sDDId the DragDrop id this element is a handle for
21469 * @param {String} sHandleId the id of the element that is the drag
21473 regHandle: function(sDDId, sHandleId) {
21474 if (!this.handleIds[sDDId]) {
21475 this.handleIds[sDDId] = {};
21477 this.handleIds[sDDId][sHandleId] = sHandleId;
21481 * Utility function to determine if a given element has been
21482 * registered as a drag drop item.
21483 * @method isDragDrop
21484 * @param {String} id the element id to check
21485 * @return {boolean} true if this element is a DragDrop item,
21489 isDragDrop: function(id) {
21490 return ( this.getDDById(id) ) ? true : false;
21494 * Returns the drag and drop instances that are in all groups the
21495 * passed in instance belongs to.
21496 * @method getRelated
21497 * @param {DragDrop} p_oDD the obj to get related data for
21498 * @param {boolean} bTargetsOnly if true, only return targetable objs
21499 * @return {DragDrop[]} the related instances
21502 getRelated: function(p_oDD, bTargetsOnly) {
21504 for (var i in p_oDD.groups) {
21505 for (j in this.ids[i]) {
21506 var dd = this.ids[i][j];
21507 if (! this.isTypeOfDD(dd)) {
21510 if (!bTargetsOnly || dd.isTarget) {
21511 oDDs[oDDs.length] = dd;
21520 * Returns true if the specified dd target is a legal target for
21521 * the specifice drag obj
21522 * @method isLegalTarget
21523 * @param {DragDrop} the drag obj
21524 * @param {DragDrop} the target
21525 * @return {boolean} true if the target is a legal target for the
21529 isLegalTarget: function (oDD, oTargetDD) {
21530 var targets = this.getRelated(oDD, true);
21531 for (var i=0, len=targets.length;i<len;++i) {
21532 if (targets[i].id == oTargetDD.id) {
21541 * My goal is to be able to transparently determine if an object is
21542 * typeof DragDrop, and the exact subclass of DragDrop. typeof
21543 * returns "object", oDD.constructor.toString() always returns
21544 * "DragDrop" and not the name of the subclass. So for now it just
21545 * evaluates a well-known variable in DragDrop.
21546 * @method isTypeOfDD
21547 * @param {Object} the object to evaluate
21548 * @return {boolean} true if typeof oDD = DragDrop
21551 isTypeOfDD: function (oDD) {
21552 return (oDD && oDD.__ygDragDrop);
21556 * Utility function to determine if a given element has been
21557 * registered as a drag drop handle for the given Drag Drop object.
21559 * @param {String} id the element id to check
21560 * @return {boolean} true if this element is a DragDrop handle, false
21564 isHandle: function(sDDId, sHandleId) {
21565 return ( this.handleIds[sDDId] &&
21566 this.handleIds[sDDId][sHandleId] );
21570 * Returns the DragDrop instance for a given id
21571 * @method getDDById
21572 * @param {String} id the id of the DragDrop object
21573 * @return {DragDrop} the drag drop object, null if it is not found
21576 getDDById: function(id) {
21577 for (var i in this.ids) {
21578 if (this.ids[i][id]) {
21579 return this.ids[i][id];
21586 * Fired after a registered DragDrop object gets the mousedown event.
21587 * Sets up the events required to track the object being dragged
21588 * @method handleMouseDown
21589 * @param {Event} e the event
21590 * @param oDD the DragDrop object being dragged
21594 handleMouseDown: function(e, oDD) {
21596 Roo.QuickTips.disable();
21598 this.currentTarget = e.getTarget();
21600 this.dragCurrent = oDD;
21602 var el = oDD.getEl();
21604 // track start position
21605 this.startX = e.getPageX();
21606 this.startY = e.getPageY();
21608 this.deltaX = this.startX - el.offsetLeft;
21609 this.deltaY = this.startY - el.offsetTop;
21611 this.dragThreshMet = false;
21613 this.clickTimeout = setTimeout(
21615 var DDM = Roo.dd.DDM;
21616 DDM.startDrag(DDM.startX, DDM.startY);
21618 this.clickTimeThresh );
21622 * Fired when either the drag pixel threshol or the mousedown hold
21623 * time threshold has been met.
21624 * @method startDrag
21625 * @param x {int} the X position of the original mousedown
21626 * @param y {int} the Y position of the original mousedown
21629 startDrag: function(x, y) {
21630 clearTimeout(this.clickTimeout);
21631 if (this.dragCurrent) {
21632 this.dragCurrent.b4StartDrag(x, y);
21633 this.dragCurrent.startDrag(x, y);
21635 this.dragThreshMet = true;
21639 * Internal function to handle the mouseup event. Will be invoked
21640 * from the context of the document.
21641 * @method handleMouseUp
21642 * @param {Event} e the event
21646 handleMouseUp: function(e) {
21649 Roo.QuickTips.enable();
21651 if (! this.dragCurrent) {
21655 clearTimeout(this.clickTimeout);
21657 if (this.dragThreshMet) {
21658 this.fireEvents(e, true);
21668 * Utility to stop event propagation and event default, if these
21669 * features are turned on.
21670 * @method stopEvent
21671 * @param {Event} e the event as returned by this.getEvent()
21674 stopEvent: function(e){
21675 if(this.stopPropagation) {
21676 e.stopPropagation();
21679 if (this.preventDefault) {
21680 e.preventDefault();
21685 * Internal function to clean up event handlers after the drag
21686 * operation is complete
21688 * @param {Event} e the event
21692 stopDrag: function(e) {
21693 // Fire the drag end event for the item that was dragged
21694 if (this.dragCurrent) {
21695 if (this.dragThreshMet) {
21696 this.dragCurrent.b4EndDrag(e);
21697 this.dragCurrent.endDrag(e);
21700 this.dragCurrent.onMouseUp(e);
21703 this.dragCurrent = null;
21704 this.dragOvers = {};
21708 * Internal function to handle the mousemove event. Will be invoked
21709 * from the context of the html element.
21711 * @TODO figure out what we can do about mouse events lost when the
21712 * user drags objects beyond the window boundary. Currently we can
21713 * detect this in internet explorer by verifying that the mouse is
21714 * down during the mousemove event. Firefox doesn't give us the
21715 * button state on the mousemove event.
21716 * @method handleMouseMove
21717 * @param {Event} e the event
21721 handleMouseMove: function(e) {
21722 if (! this.dragCurrent) {
21726 // var button = e.which || e.button;
21728 // check for IE mouseup outside of page boundary
21729 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21731 return this.handleMouseUp(e);
21734 if (!this.dragThreshMet) {
21735 var diffX = Math.abs(this.startX - e.getPageX());
21736 var diffY = Math.abs(this.startY - e.getPageY());
21737 if (diffX > this.clickPixelThresh ||
21738 diffY > this.clickPixelThresh) {
21739 this.startDrag(this.startX, this.startY);
21743 if (this.dragThreshMet) {
21744 this.dragCurrent.b4Drag(e);
21745 this.dragCurrent.onDrag(e);
21746 if(!this.dragCurrent.moveOnly){
21747 this.fireEvents(e, false);
21757 * Iterates over all of the DragDrop elements to find ones we are
21758 * hovering over or dropping on
21759 * @method fireEvents
21760 * @param {Event} e the event
21761 * @param {boolean} isDrop is this a drop op or a mouseover op?
21765 fireEvents: function(e, isDrop) {
21766 var dc = this.dragCurrent;
21768 // If the user did the mouse up outside of the window, we could
21769 // get here even though we have ended the drag.
21770 if (!dc || dc.isLocked()) {
21774 var pt = e.getPoint();
21776 // cache the previous dragOver array
21782 var enterEvts = [];
21784 // Check to see if the object(s) we were hovering over is no longer
21785 // being hovered over so we can fire the onDragOut event
21786 for (var i in this.dragOvers) {
21788 var ddo = this.dragOvers[i];
21790 if (! this.isTypeOfDD(ddo)) {
21794 if (! this.isOverTarget(pt, ddo, this.mode)) {
21795 outEvts.push( ddo );
21798 oldOvers[i] = true;
21799 delete this.dragOvers[i];
21802 for (var sGroup in dc.groups) {
21804 if ("string" != typeof sGroup) {
21808 for (i in this.ids[sGroup]) {
21809 var oDD = this.ids[sGroup][i];
21810 if (! this.isTypeOfDD(oDD)) {
21814 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21815 if (this.isOverTarget(pt, oDD, this.mode)) {
21816 // look for drop interactions
21818 dropEvts.push( oDD );
21819 // look for drag enter and drag over interactions
21822 // initial drag over: dragEnter fires
21823 if (!oldOvers[oDD.id]) {
21824 enterEvts.push( oDD );
21825 // subsequent drag overs: dragOver fires
21827 overEvts.push( oDD );
21830 this.dragOvers[oDD.id] = oDD;
21838 if (outEvts.length) {
21839 dc.b4DragOut(e, outEvts);
21840 dc.onDragOut(e, outEvts);
21843 if (enterEvts.length) {
21844 dc.onDragEnter(e, enterEvts);
21847 if (overEvts.length) {
21848 dc.b4DragOver(e, overEvts);
21849 dc.onDragOver(e, overEvts);
21852 if (dropEvts.length) {
21853 dc.b4DragDrop(e, dropEvts);
21854 dc.onDragDrop(e, dropEvts);
21858 // fire dragout events
21860 for (i=0, len=outEvts.length; i<len; ++i) {
21861 dc.b4DragOut(e, outEvts[i].id);
21862 dc.onDragOut(e, outEvts[i].id);
21865 // fire enter events
21866 for (i=0,len=enterEvts.length; i<len; ++i) {
21867 // dc.b4DragEnter(e, oDD.id);
21868 dc.onDragEnter(e, enterEvts[i].id);
21871 // fire over events
21872 for (i=0,len=overEvts.length; i<len; ++i) {
21873 dc.b4DragOver(e, overEvts[i].id);
21874 dc.onDragOver(e, overEvts[i].id);
21877 // fire drop events
21878 for (i=0, len=dropEvts.length; i<len; ++i) {
21879 dc.b4DragDrop(e, dropEvts[i].id);
21880 dc.onDragDrop(e, dropEvts[i].id);
21885 // notify about a drop that did not find a target
21886 if (isDrop && !dropEvts.length) {
21887 dc.onInvalidDrop(e);
21893 * Helper function for getting the best match from the list of drag
21894 * and drop objects returned by the drag and drop events when we are
21895 * in INTERSECT mode. It returns either the first object that the
21896 * cursor is over, or the object that has the greatest overlap with
21897 * the dragged element.
21898 * @method getBestMatch
21899 * @param {DragDrop[]} dds The array of drag and drop objects
21901 * @return {DragDrop} The best single match
21904 getBestMatch: function(dds) {
21906 // Return null if the input is not what we expect
21907 //if (!dds || !dds.length || dds.length == 0) {
21909 // If there is only one item, it wins
21910 //} else if (dds.length == 1) {
21912 var len = dds.length;
21917 // Loop through the targeted items
21918 for (var i=0; i<len; ++i) {
21920 // If the cursor is over the object, it wins. If the
21921 // cursor is over multiple matches, the first one we come
21923 if (dd.cursorIsOver) {
21926 // Otherwise the object with the most overlap wins
21929 winner.overlap.getArea() < dd.overlap.getArea()) {
21940 * Refreshes the cache of the top-left and bottom-right points of the
21941 * drag and drop objects in the specified group(s). This is in the
21942 * format that is stored in the drag and drop instance, so typical
21945 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21949 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21951 * @TODO this really should be an indexed array. Alternatively this
21952 * method could accept both.
21953 * @method refreshCache
21954 * @param {Object} groups an associative array of groups to refresh
21957 refreshCache: function(groups) {
21958 for (var sGroup in groups) {
21959 if ("string" != typeof sGroup) {
21962 for (var i in this.ids[sGroup]) {
21963 var oDD = this.ids[sGroup][i];
21965 if (this.isTypeOfDD(oDD)) {
21966 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21967 var loc = this.getLocation(oDD);
21969 this.locationCache[oDD.id] = loc;
21971 delete this.locationCache[oDD.id];
21972 // this will unregister the drag and drop object if
21973 // the element is not in a usable state
21982 * This checks to make sure an element exists and is in the DOM. The
21983 * main purpose is to handle cases where innerHTML is used to remove
21984 * drag and drop objects from the DOM. IE provides an 'unspecified
21985 * error' when trying to access the offsetParent of such an element
21987 * @param {HTMLElement} el the element to check
21988 * @return {boolean} true if the element looks usable
21991 verifyEl: function(el) {
21996 parent = el.offsetParent;
21999 parent = el.offsetParent;
22010 * Returns a Region object containing the drag and drop element's position
22011 * and size, including the padding configured for it
22012 * @method getLocation
22013 * @param {DragDrop} oDD the drag and drop object to get the
22015 * @return {Roo.lib.Region} a Region object representing the total area
22016 * the element occupies, including any padding
22017 * the instance is configured for.
22020 getLocation: function(oDD) {
22021 if (! this.isTypeOfDD(oDD)) {
22025 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22028 pos= Roo.lib.Dom.getXY(el);
22036 x2 = x1 + el.offsetWidth;
22038 y2 = y1 + el.offsetHeight;
22040 t = y1 - oDD.padding[0];
22041 r = x2 + oDD.padding[1];
22042 b = y2 + oDD.padding[2];
22043 l = x1 - oDD.padding[3];
22045 return new Roo.lib.Region( t, r, b, l );
22049 * Checks the cursor location to see if it over the target
22050 * @method isOverTarget
22051 * @param {Roo.lib.Point} pt The point to evaluate
22052 * @param {DragDrop} oTarget the DragDrop object we are inspecting
22053 * @return {boolean} true if the mouse is over the target
22057 isOverTarget: function(pt, oTarget, intersect) {
22058 // use cache if available
22059 var loc = this.locationCache[oTarget.id];
22060 if (!loc || !this.useCache) {
22061 loc = this.getLocation(oTarget);
22062 this.locationCache[oTarget.id] = loc;
22070 oTarget.cursorIsOver = loc.contains( pt );
22072 // DragDrop is using this as a sanity check for the initial mousedown
22073 // in this case we are done. In POINT mode, if the drag obj has no
22074 // contraints, we are also done. Otherwise we need to evaluate the
22075 // location of the target as related to the actual location of the
22076 // dragged element.
22077 var dc = this.dragCurrent;
22078 if (!dc || !dc.getTargetCoord ||
22079 (!intersect && !dc.constrainX && !dc.constrainY)) {
22080 return oTarget.cursorIsOver;
22083 oTarget.overlap = null;
22085 // Get the current location of the drag element, this is the
22086 // location of the mouse event less the delta that represents
22087 // where the original mousedown happened on the element. We
22088 // need to consider constraints and ticks as well.
22089 var pos = dc.getTargetCoord(pt.x, pt.y);
22091 var el = dc.getDragEl();
22092 var curRegion = new Roo.lib.Region( pos.y,
22093 pos.x + el.offsetWidth,
22094 pos.y + el.offsetHeight,
22097 var overlap = curRegion.intersect(loc);
22100 oTarget.overlap = overlap;
22101 return (intersect) ? true : oTarget.cursorIsOver;
22108 * unload event handler
22109 * @method _onUnload
22113 _onUnload: function(e, me) {
22114 Roo.dd.DragDropMgr.unregAll();
22118 * Cleans up the drag and drop events and objects.
22123 unregAll: function() {
22125 if (this.dragCurrent) {
22127 this.dragCurrent = null;
22130 this._execOnAll("unreg", []);
22132 for (i in this.elementCache) {
22133 delete this.elementCache[i];
22136 this.elementCache = {};
22141 * A cache of DOM elements
22142 * @property elementCache
22149 * Get the wrapper for the DOM element specified
22150 * @method getElWrapper
22151 * @param {String} id the id of the element to get
22152 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22154 * @deprecated This wrapper isn't that useful
22157 getElWrapper: function(id) {
22158 var oWrapper = this.elementCache[id];
22159 if (!oWrapper || !oWrapper.el) {
22160 oWrapper = this.elementCache[id] =
22161 new this.ElementWrapper(Roo.getDom(id));
22167 * Returns the actual DOM element
22168 * @method getElement
22169 * @param {String} id the id of the elment to get
22170 * @return {Object} The element
22171 * @deprecated use Roo.getDom instead
22174 getElement: function(id) {
22175 return Roo.getDom(id);
22179 * Returns the style property for the DOM element (i.e.,
22180 * document.getElById(id).style)
22182 * @param {String} id the id of the elment to get
22183 * @return {Object} The style property of the element
22184 * @deprecated use Roo.getDom instead
22187 getCss: function(id) {
22188 var el = Roo.getDom(id);
22189 return (el) ? el.style : null;
22193 * Inner class for cached elements
22194 * @class DragDropMgr.ElementWrapper
22199 ElementWrapper: function(el) {
22204 this.el = el || null;
22209 this.id = this.el && el.id;
22211 * A reference to the style property
22214 this.css = this.el && el.style;
22218 * Returns the X position of an html element
22220 * @param el the element for which to get the position
22221 * @return {int} the X coordinate
22223 * @deprecated use Roo.lib.Dom.getX instead
22226 getPosX: function(el) {
22227 return Roo.lib.Dom.getX(el);
22231 * Returns the Y position of an html element
22233 * @param el the element for which to get the position
22234 * @return {int} the Y coordinate
22235 * @deprecated use Roo.lib.Dom.getY instead
22238 getPosY: function(el) {
22239 return Roo.lib.Dom.getY(el);
22243 * Swap two nodes. In IE, we use the native method, for others we
22244 * emulate the IE behavior
22246 * @param n1 the first node to swap
22247 * @param n2 the other node to swap
22250 swapNode: function(n1, n2) {
22254 var p = n2.parentNode;
22255 var s = n2.nextSibling;
22258 p.insertBefore(n1, n2);
22259 } else if (n2 == n1.nextSibling) {
22260 p.insertBefore(n2, n1);
22262 n1.parentNode.replaceChild(n2, n1);
22263 p.insertBefore(n1, s);
22269 * Returns the current scroll position
22270 * @method getScroll
22274 getScroll: function () {
22275 var t, l, dde=document.documentElement, db=document.body;
22276 if (dde && (dde.scrollTop || dde.scrollLeft)) {
22278 l = dde.scrollLeft;
22285 return { top: t, left: l };
22289 * Returns the specified element style property
22291 * @param {HTMLElement} el the element
22292 * @param {string} styleProp the style property
22293 * @return {string} The value of the style property
22294 * @deprecated use Roo.lib.Dom.getStyle
22297 getStyle: function(el, styleProp) {
22298 return Roo.fly(el).getStyle(styleProp);
22302 * Gets the scrollTop
22303 * @method getScrollTop
22304 * @return {int} the document's scrollTop
22307 getScrollTop: function () { return this.getScroll().top; },
22310 * Gets the scrollLeft
22311 * @method getScrollLeft
22312 * @return {int} the document's scrollTop
22315 getScrollLeft: function () { return this.getScroll().left; },
22318 * Sets the x/y position of an element to the location of the
22321 * @param {HTMLElement} moveEl The element to move
22322 * @param {HTMLElement} targetEl The position reference element
22325 moveToEl: function (moveEl, targetEl) {
22326 var aCoord = Roo.lib.Dom.getXY(targetEl);
22327 Roo.lib.Dom.setXY(moveEl, aCoord);
22331 * Numeric array sort function
22332 * @method numericSort
22335 numericSort: function(a, b) { return (a - b); },
22339 * @property _timeoutCount
22346 * Trying to make the load order less important. Without this we get
22347 * an error if this file is loaded before the Event Utility.
22348 * @method _addListeners
22352 _addListeners: function() {
22353 var DDM = Roo.dd.DDM;
22354 if ( Roo.lib.Event && document ) {
22357 if (DDM._timeoutCount > 2000) {
22359 setTimeout(DDM._addListeners, 10);
22360 if (document && document.body) {
22361 DDM._timeoutCount += 1;
22368 * Recursively searches the immediate parent and all child nodes for
22369 * the handle element in order to determine wheter or not it was
22371 * @method handleWasClicked
22372 * @param node the html element to inspect
22375 handleWasClicked: function(node, id) {
22376 if (this.isHandle(id, node.id)) {
22379 // check to see if this is a text node child of the one we want
22380 var p = node.parentNode;
22383 if (this.isHandle(id, p.id)) {
22398 // shorter alias, save a few bytes
22399 Roo.dd.DDM = Roo.dd.DragDropMgr;
22400 Roo.dd.DDM._addListeners();
22404 * Ext JS Library 1.1.1
22405 * Copyright(c) 2006-2007, Ext JS, LLC.
22407 * Originally Released Under LGPL - original licence link has changed is not relivant.
22410 * <script type="text/javascript">
22415 * A DragDrop implementation where the linked element follows the
22416 * mouse cursor during a drag.
22417 * @extends Roo.dd.DragDrop
22419 * @param {String} id the id of the linked element
22420 * @param {String} sGroup the group of related DragDrop items
22421 * @param {object} config an object containing configurable attributes
22422 * Valid properties for DD:
22425 Roo.dd.DD = function(id, sGroup, config) {
22427 this.init(id, sGroup, config);
22431 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22434 * When set to true, the utility automatically tries to scroll the browser
22435 * window wehn a drag and drop element is dragged near the viewport boundary.
22436 * Defaults to true.
22443 * Sets the pointer offset to the distance between the linked element's top
22444 * left corner and the location the element was clicked
22445 * @method autoOffset
22446 * @param {int} iPageX the X coordinate of the click
22447 * @param {int} iPageY the Y coordinate of the click
22449 autoOffset: function(iPageX, iPageY) {
22450 var x = iPageX - this.startPageX;
22451 var y = iPageY - this.startPageY;
22452 this.setDelta(x, y);
22456 * Sets the pointer offset. You can call this directly to force the
22457 * offset to be in a particular location (e.g., pass in 0,0 to set it
22458 * to the center of the object)
22460 * @param {int} iDeltaX the distance from the left
22461 * @param {int} iDeltaY the distance from the top
22463 setDelta: function(iDeltaX, iDeltaY) {
22464 this.deltaX = iDeltaX;
22465 this.deltaY = iDeltaY;
22469 * Sets the drag element to the location of the mousedown or click event,
22470 * maintaining the cursor location relative to the location on the element
22471 * that was clicked. Override this if you want to place the element in a
22472 * location other than where the cursor is.
22473 * @method setDragElPos
22474 * @param {int} iPageX the X coordinate of the mousedown or drag event
22475 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22477 setDragElPos: function(iPageX, iPageY) {
22478 // the first time we do this, we are going to check to make sure
22479 // the element has css positioning
22481 var el = this.getDragEl();
22482 this.alignElWithMouse(el, iPageX, iPageY);
22486 * Sets the element to the location of the mousedown or click event,
22487 * maintaining the cursor location relative to the location on the element
22488 * that was clicked. Override this if you want to place the element in a
22489 * location other than where the cursor is.
22490 * @method alignElWithMouse
22491 * @param {HTMLElement} el the element to move
22492 * @param {int} iPageX the X coordinate of the mousedown or drag event
22493 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22495 alignElWithMouse: function(el, iPageX, iPageY) {
22496 var oCoord = this.getTargetCoord(iPageX, iPageY);
22497 var fly = el.dom ? el : Roo.fly(el);
22498 if (!this.deltaSetXY) {
22499 var aCoord = [oCoord.x, oCoord.y];
22501 var newLeft = fly.getLeft(true);
22502 var newTop = fly.getTop(true);
22503 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22505 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22508 this.cachePosition(oCoord.x, oCoord.y);
22509 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22514 * Saves the most recent position so that we can reset the constraints and
22515 * tick marks on-demand. We need to know this so that we can calculate the
22516 * number of pixels the element is offset from its original position.
22517 * @method cachePosition
22518 * @param iPageX the current x position (optional, this just makes it so we
22519 * don't have to look it up again)
22520 * @param iPageY the current y position (optional, this just makes it so we
22521 * don't have to look it up again)
22523 cachePosition: function(iPageX, iPageY) {
22525 this.lastPageX = iPageX;
22526 this.lastPageY = iPageY;
22528 var aCoord = Roo.lib.Dom.getXY(this.getEl());
22529 this.lastPageX = aCoord[0];
22530 this.lastPageY = aCoord[1];
22535 * Auto-scroll the window if the dragged object has been moved beyond the
22536 * visible window boundary.
22537 * @method autoScroll
22538 * @param {int} x the drag element's x position
22539 * @param {int} y the drag element's y position
22540 * @param {int} h the height of the drag element
22541 * @param {int} w the width of the drag element
22544 autoScroll: function(x, y, h, w) {
22547 // The client height
22548 var clientH = Roo.lib.Dom.getViewWidth();
22550 // The client width
22551 var clientW = Roo.lib.Dom.getViewHeight();
22553 // The amt scrolled down
22554 var st = this.DDM.getScrollTop();
22556 // The amt scrolled right
22557 var sl = this.DDM.getScrollLeft();
22559 // Location of the bottom of the element
22562 // Location of the right of the element
22565 // The distance from the cursor to the bottom of the visible area,
22566 // adjusted so that we don't scroll if the cursor is beyond the
22567 // element drag constraints
22568 var toBot = (clientH + st - y - this.deltaY);
22570 // The distance from the cursor to the right of the visible area
22571 var toRight = (clientW + sl - x - this.deltaX);
22574 // How close to the edge the cursor must be before we scroll
22575 // var thresh = (document.all) ? 100 : 40;
22578 // How many pixels to scroll per autoscroll op. This helps to reduce
22579 // clunky scrolling. IE is more sensitive about this ... it needs this
22580 // value to be higher.
22581 var scrAmt = (document.all) ? 80 : 30;
22583 // Scroll down if we are near the bottom of the visible page and the
22584 // obj extends below the crease
22585 if ( bot > clientH && toBot < thresh ) {
22586 window.scrollTo(sl, st + scrAmt);
22589 // Scroll up if the window is scrolled down and the top of the object
22590 // goes above the top border
22591 if ( y < st && st > 0 && y - st < thresh ) {
22592 window.scrollTo(sl, st - scrAmt);
22595 // Scroll right if the obj is beyond the right border and the cursor is
22596 // near the border.
22597 if ( right > clientW && toRight < thresh ) {
22598 window.scrollTo(sl + scrAmt, st);
22601 // Scroll left if the window has been scrolled to the right and the obj
22602 // extends past the left border
22603 if ( x < sl && sl > 0 && x - sl < thresh ) {
22604 window.scrollTo(sl - scrAmt, st);
22610 * Finds the location the element should be placed if we want to move
22611 * it to where the mouse location less the click offset would place us.
22612 * @method getTargetCoord
22613 * @param {int} iPageX the X coordinate of the click
22614 * @param {int} iPageY the Y coordinate of the click
22615 * @return an object that contains the coordinates (Object.x and Object.y)
22618 getTargetCoord: function(iPageX, iPageY) {
22621 var x = iPageX - this.deltaX;
22622 var y = iPageY - this.deltaY;
22624 if (this.constrainX) {
22625 if (x < this.minX) { x = this.minX; }
22626 if (x > this.maxX) { x = this.maxX; }
22629 if (this.constrainY) {
22630 if (y < this.minY) { y = this.minY; }
22631 if (y > this.maxY) { y = this.maxY; }
22634 x = this.getTick(x, this.xTicks);
22635 y = this.getTick(y, this.yTicks);
22642 * Sets up config options specific to this class. Overrides
22643 * Roo.dd.DragDrop, but all versions of this method through the
22644 * inheritance chain are called
22646 applyConfig: function() {
22647 Roo.dd.DD.superclass.applyConfig.call(this);
22648 this.scroll = (this.config.scroll !== false);
22652 * Event that fires prior to the onMouseDown event. Overrides
22655 b4MouseDown: function(e) {
22656 // this.resetConstraints();
22657 this.autoOffset(e.getPageX(),
22662 * Event that fires prior to the onDrag event. Overrides
22665 b4Drag: function(e) {
22666 this.setDragElPos(e.getPageX(),
22670 toString: function() {
22671 return ("DD " + this.id);
22674 //////////////////////////////////////////////////////////////////////////
22675 // Debugging ygDragDrop events that can be overridden
22676 //////////////////////////////////////////////////////////////////////////
22678 startDrag: function(x, y) {
22681 onDrag: function(e) {
22684 onDragEnter: function(e, id) {
22687 onDragOver: function(e, id) {
22690 onDragOut: function(e, id) {
22693 onDragDrop: function(e, id) {
22696 endDrag: function(e) {
22703 * Ext JS Library 1.1.1
22704 * Copyright(c) 2006-2007, Ext JS, LLC.
22706 * Originally Released Under LGPL - original licence link has changed is not relivant.
22709 * <script type="text/javascript">
22713 * @class Roo.dd.DDProxy
22714 * A DragDrop implementation that inserts an empty, bordered div into
22715 * the document that follows the cursor during drag operations. At the time of
22716 * the click, the frame div is resized to the dimensions of the linked html
22717 * element, and moved to the exact location of the linked element.
22719 * References to the "frame" element refer to the single proxy element that
22720 * was created to be dragged in place of all DDProxy elements on the
22723 * @extends Roo.dd.DD
22725 * @param {String} id the id of the linked html element
22726 * @param {String} sGroup the group of related DragDrop objects
22727 * @param {object} config an object containing configurable attributes
22728 * Valid properties for DDProxy in addition to those in DragDrop:
22729 * resizeFrame, centerFrame, dragElId
22731 Roo.dd.DDProxy = function(id, sGroup, config) {
22733 this.init(id, sGroup, config);
22739 * The default drag frame div id
22740 * @property Roo.dd.DDProxy.dragElId
22744 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22746 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22749 * By default we resize the drag frame to be the same size as the element
22750 * we want to drag (this is to get the frame effect). We can turn it off
22751 * if we want a different behavior.
22752 * @property resizeFrame
22758 * By default the frame is positioned exactly where the drag element is, so
22759 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
22760 * you do not have constraints on the obj is to have the drag frame centered
22761 * around the cursor. Set centerFrame to true for this effect.
22762 * @property centerFrame
22765 centerFrame: false,
22768 * Creates the proxy element if it does not yet exist
22769 * @method createFrame
22771 createFrame: function() {
22773 var body = document.body;
22775 if (!body || !body.firstChild) {
22776 setTimeout( function() { self.createFrame(); }, 50 );
22780 var div = this.getDragEl();
22783 div = document.createElement("div");
22784 div.id = this.dragElId;
22787 s.position = "absolute";
22788 s.visibility = "hidden";
22790 s.border = "2px solid #aaa";
22793 // appendChild can blow up IE if invoked prior to the window load event
22794 // while rendering a table. It is possible there are other scenarios
22795 // that would cause this to happen as well.
22796 body.insertBefore(div, body.firstChild);
22801 * Initialization for the drag frame element. Must be called in the
22802 * constructor of all subclasses
22803 * @method initFrame
22805 initFrame: function() {
22806 this.createFrame();
22809 applyConfig: function() {
22810 Roo.dd.DDProxy.superclass.applyConfig.call(this);
22812 this.resizeFrame = (this.config.resizeFrame !== false);
22813 this.centerFrame = (this.config.centerFrame);
22814 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22818 * Resizes the drag frame to the dimensions of the clicked object, positions
22819 * it over the object, and finally displays it
22820 * @method showFrame
22821 * @param {int} iPageX X click position
22822 * @param {int} iPageY Y click position
22825 showFrame: function(iPageX, iPageY) {
22826 var el = this.getEl();
22827 var dragEl = this.getDragEl();
22828 var s = dragEl.style;
22830 this._resizeProxy();
22832 if (this.centerFrame) {
22833 this.setDelta( Math.round(parseInt(s.width, 10)/2),
22834 Math.round(parseInt(s.height, 10)/2) );
22837 this.setDragElPos(iPageX, iPageY);
22839 Roo.fly(dragEl).show();
22843 * The proxy is automatically resized to the dimensions of the linked
22844 * element when a drag is initiated, unless resizeFrame is set to false
22845 * @method _resizeProxy
22848 _resizeProxy: function() {
22849 if (this.resizeFrame) {
22850 var el = this.getEl();
22851 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22855 // overrides Roo.dd.DragDrop
22856 b4MouseDown: function(e) {
22857 var x = e.getPageX();
22858 var y = e.getPageY();
22859 this.autoOffset(x, y);
22860 this.setDragElPos(x, y);
22863 // overrides Roo.dd.DragDrop
22864 b4StartDrag: function(x, y) {
22865 // show the drag frame
22866 this.showFrame(x, y);
22869 // overrides Roo.dd.DragDrop
22870 b4EndDrag: function(e) {
22871 Roo.fly(this.getDragEl()).hide();
22874 // overrides Roo.dd.DragDrop
22875 // By default we try to move the element to the last location of the frame.
22876 // This is so that the default behavior mirrors that of Roo.dd.DD.
22877 endDrag: function(e) {
22879 var lel = this.getEl();
22880 var del = this.getDragEl();
22882 // Show the drag frame briefly so we can get its position
22883 del.style.visibility = "";
22886 // Hide the linked element before the move to get around a Safari
22888 lel.style.visibility = "hidden";
22889 Roo.dd.DDM.moveToEl(lel, del);
22890 del.style.visibility = "hidden";
22891 lel.style.visibility = "";
22896 beforeMove : function(){
22900 afterDrag : function(){
22904 toString: function() {
22905 return ("DDProxy " + this.id);
22911 * Ext JS Library 1.1.1
22912 * Copyright(c) 2006-2007, Ext JS, LLC.
22914 * Originally Released Under LGPL - original licence link has changed is not relivant.
22917 * <script type="text/javascript">
22921 * @class Roo.dd.DDTarget
22922 * A DragDrop implementation that does not move, but can be a drop
22923 * target. You would get the same result by simply omitting implementation
22924 * for the event callbacks, but this way we reduce the processing cost of the
22925 * event listener and the callbacks.
22926 * @extends Roo.dd.DragDrop
22928 * @param {String} id the id of the element that is a drop target
22929 * @param {String} sGroup the group of related DragDrop objects
22930 * @param {object} config an object containing configurable attributes
22931 * Valid properties for DDTarget in addition to those in
22935 Roo.dd.DDTarget = function(id, sGroup, config) {
22937 this.initTarget(id, sGroup, config);
22939 if (config && (config.listeners || config.events)) {
22940 Roo.dd.DragDrop.superclass.constructor.call(this, {
22941 listeners : config.listeners || {},
22942 events : config.events || {}
22947 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22948 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22949 toString: function() {
22950 return ("DDTarget " + this.id);
22955 * Ext JS Library 1.1.1
22956 * Copyright(c) 2006-2007, Ext JS, LLC.
22958 * Originally Released Under LGPL - original licence link has changed is not relivant.
22961 * <script type="text/javascript">
22966 * @class Roo.dd.ScrollManager
22967 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22968 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22971 Roo.dd.ScrollManager = function(){
22972 var ddm = Roo.dd.DragDropMgr;
22979 var onStop = function(e){
22984 var triggerRefresh = function(){
22985 if(ddm.dragCurrent){
22986 ddm.refreshCache(ddm.dragCurrent.groups);
22990 var doScroll = function(){
22991 if(ddm.dragCurrent){
22992 var dds = Roo.dd.ScrollManager;
22994 if(proc.el.scroll(proc.dir, dds.increment)){
22998 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23003 var clearProc = function(){
23005 clearInterval(proc.id);
23012 var startProc = function(el, dir){
23013 Roo.log('scroll startproc');
23017 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23020 var onFire = function(e, isDrop){
23022 if(isDrop || !ddm.dragCurrent){ return; }
23023 var dds = Roo.dd.ScrollManager;
23024 if(!dragEl || dragEl != ddm.dragCurrent){
23025 dragEl = ddm.dragCurrent;
23026 // refresh regions on drag start
23027 dds.refreshCache();
23030 var xy = Roo.lib.Event.getXY(e);
23031 var pt = new Roo.lib.Point(xy[0], xy[1]);
23032 for(var id in els){
23033 var el = els[id], r = el._region;
23034 if(r && r.contains(pt) && el.isScrollable()){
23035 if(r.bottom - pt.y <= dds.thresh){
23037 startProc(el, "down");
23040 }else if(r.right - pt.x <= dds.thresh){
23042 startProc(el, "left");
23045 }else if(pt.y - r.top <= dds.thresh){
23047 startProc(el, "up");
23050 }else if(pt.x - r.left <= dds.thresh){
23052 startProc(el, "right");
23061 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23062 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23066 * Registers new overflow element(s) to auto scroll
23067 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23069 register : function(el){
23070 if(el instanceof Array){
23071 for(var i = 0, len = el.length; i < len; i++) {
23072 this.register(el[i]);
23078 Roo.dd.ScrollManager.els = els;
23082 * Unregisters overflow element(s) so they are no longer scrolled
23083 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23085 unregister : function(el){
23086 if(el instanceof Array){
23087 for(var i = 0, len = el.length; i < len; i++) {
23088 this.unregister(el[i]);
23097 * The number of pixels from the edge of a container the pointer needs to be to
23098 * trigger scrolling (defaults to 25)
23104 * The number of pixels to scroll in each scroll increment (defaults to 50)
23110 * The frequency of scrolls in milliseconds (defaults to 500)
23116 * True to animate the scroll (defaults to true)
23122 * The animation duration in seconds -
23123 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23129 * Manually trigger a cache refresh.
23131 refreshCache : function(){
23132 for(var id in els){
23133 if(typeof els[id] == 'object'){ // for people extending the object prototype
23134 els[id]._region = els[id].getRegion();
23141 * Ext JS Library 1.1.1
23142 * Copyright(c) 2006-2007, Ext JS, LLC.
23144 * Originally Released Under LGPL - original licence link has changed is not relivant.
23147 * <script type="text/javascript">
23152 * @class Roo.dd.Registry
23153 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
23154 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23157 Roo.dd.Registry = function(){
23160 var autoIdSeed = 0;
23162 var getId = function(el, autogen){
23163 if(typeof el == "string"){
23167 if(!id && autogen !== false){
23168 id = "roodd-" + (++autoIdSeed);
23176 * Register a drag drop element
23177 * @param {String|HTMLElement} element The id or DOM node to register
23178 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23179 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
23180 * knows how to interpret, plus there are some specific properties known to the Registry that should be
23181 * populated in the data object (if applicable):
23183 Value Description<br />
23184 --------- ------------------------------------------<br />
23185 handles Array of DOM nodes that trigger dragging<br />
23186 for the element being registered<br />
23187 isHandle True if the element passed in triggers<br />
23188 dragging itself, else false
23191 register : function(el, data){
23193 if(typeof el == "string"){
23194 el = document.getElementById(el);
23197 elements[getId(el)] = data;
23198 if(data.isHandle !== false){
23199 handles[data.ddel.id] = data;
23202 var hs = data.handles;
23203 for(var i = 0, len = hs.length; i < len; i++){
23204 handles[getId(hs[i])] = data;
23210 * Unregister a drag drop element
23211 * @param {String|HTMLElement} element The id or DOM node to unregister
23213 unregister : function(el){
23214 var id = getId(el, false);
23215 var data = elements[id];
23217 delete elements[id];
23219 var hs = data.handles;
23220 for(var i = 0, len = hs.length; i < len; i++){
23221 delete handles[getId(hs[i], false)];
23228 * Returns the handle registered for a DOM Node by id
23229 * @param {String|HTMLElement} id The DOM node or id to look up
23230 * @return {Object} handle The custom handle data
23232 getHandle : function(id){
23233 if(typeof id != "string"){ // must be element?
23236 return handles[id];
23240 * Returns the handle that is registered for the DOM node that is the target of the event
23241 * @param {Event} e The event
23242 * @return {Object} handle The custom handle data
23244 getHandleFromEvent : function(e){
23245 var t = Roo.lib.Event.getTarget(e);
23246 return t ? handles[t.id] : null;
23250 * Returns a custom data object that is registered for a DOM node by id
23251 * @param {String|HTMLElement} id The DOM node or id to look up
23252 * @return {Object} data The custom data
23254 getTarget : function(id){
23255 if(typeof id != "string"){ // must be element?
23258 return elements[id];
23262 * Returns a custom data object that is registered for the DOM node that is the target of the event
23263 * @param {Event} e The event
23264 * @return {Object} data The custom data
23266 getTargetFromEvent : function(e){
23267 var t = Roo.lib.Event.getTarget(e);
23268 return t ? elements[t.id] || handles[t.id] : null;
23273 * Ext JS Library 1.1.1
23274 * Copyright(c) 2006-2007, Ext JS, LLC.
23276 * Originally Released Under LGPL - original licence link has changed is not relivant.
23279 * <script type="text/javascript">
23284 * @class Roo.dd.StatusProxy
23285 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
23286 * default drag proxy used by all Roo.dd components.
23288 * @param {Object} config
23290 Roo.dd.StatusProxy = function(config){
23291 Roo.apply(this, config);
23292 this.id = this.id || Roo.id();
23293 this.el = new Roo.Layer({
23295 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23296 {tag: "div", cls: "x-dd-drop-icon"},
23297 {tag: "div", cls: "x-dd-drag-ghost"}
23300 shadow: !config || config.shadow !== false
23302 this.ghost = Roo.get(this.el.dom.childNodes[1]);
23303 this.dropStatus = this.dropNotAllowed;
23306 Roo.dd.StatusProxy.prototype = {
23308 * @cfg {String} dropAllowed
23309 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23311 dropAllowed : "x-dd-drop-ok",
23313 * @cfg {String} dropNotAllowed
23314 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23316 dropNotAllowed : "x-dd-drop-nodrop",
23319 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23320 * over the current target element.
23321 * @param {String} cssClass The css class for the new drop status indicator image
23323 setStatus : function(cssClass){
23324 cssClass = cssClass || this.dropNotAllowed;
23325 if(this.dropStatus != cssClass){
23326 this.el.replaceClass(this.dropStatus, cssClass);
23327 this.dropStatus = cssClass;
23332 * Resets the status indicator to the default dropNotAllowed value
23333 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23335 reset : function(clearGhost){
23336 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23337 this.dropStatus = this.dropNotAllowed;
23339 this.ghost.update("");
23344 * Updates the contents of the ghost element
23345 * @param {String} html The html that will replace the current innerHTML of the ghost element
23347 update : function(html){
23348 if(typeof html == "string"){
23349 this.ghost.update(html);
23351 this.ghost.update("");
23352 html.style.margin = "0";
23353 this.ghost.dom.appendChild(html);
23355 // ensure float = none set?? cant remember why though.
23356 var el = this.ghost.dom.firstChild;
23358 Roo.fly(el).setStyle('float', 'none');
23363 * Returns the underlying proxy {@link Roo.Layer}
23364 * @return {Roo.Layer} el
23366 getEl : function(){
23371 * Returns the ghost element
23372 * @return {Roo.Element} el
23374 getGhost : function(){
23380 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23382 hide : function(clear){
23390 * Stops the repair animation if it's currently running
23393 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23399 * Displays this proxy
23406 * Force the Layer to sync its shadow and shim positions to the element
23413 * Causes the proxy to return to its position of origin via an animation. Should be called after an
23414 * invalid drop operation by the item being dragged.
23415 * @param {Array} xy The XY position of the element ([x, y])
23416 * @param {Function} callback The function to call after the repair is complete
23417 * @param {Object} scope The scope in which to execute the callback
23419 repair : function(xy, callback, scope){
23420 this.callback = callback;
23421 this.scope = scope;
23422 if(xy && this.animRepair !== false){
23423 this.el.addClass("x-dd-drag-repair");
23424 this.el.hideUnders(true);
23425 this.anim = this.el.shift({
23426 duration: this.repairDuration || .5,
23430 callback: this.afterRepair,
23434 this.afterRepair();
23439 afterRepair : function(){
23441 if(typeof this.callback == "function"){
23442 this.callback.call(this.scope || this);
23444 this.callback = null;
23449 * Ext JS Library 1.1.1
23450 * Copyright(c) 2006-2007, Ext JS, LLC.
23452 * Originally Released Under LGPL - original licence link has changed is not relivant.
23455 * <script type="text/javascript">
23459 * @class Roo.dd.DragSource
23460 * @extends Roo.dd.DDProxy
23461 * A simple class that provides the basic implementation needed to make any element draggable.
23463 * @param {String/HTMLElement/Element} el The container element
23464 * @param {Object} config
23466 Roo.dd.DragSource = function(el, config){
23467 this.el = Roo.get(el);
23468 this.dragData = {};
23470 Roo.apply(this, config);
23473 this.proxy = new Roo.dd.StatusProxy();
23476 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23477 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23479 this.dragging = false;
23482 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23484 * @cfg {String} dropAllowed
23485 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23487 dropAllowed : "x-dd-drop-ok",
23489 * @cfg {String} dropNotAllowed
23490 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23492 dropNotAllowed : "x-dd-drop-nodrop",
23495 * Returns the data object associated with this drag source
23496 * @return {Object} data An object containing arbitrary data
23498 getDragData : function(e){
23499 return this.dragData;
23503 onDragEnter : function(e, id){
23504 var target = Roo.dd.DragDropMgr.getDDById(id);
23505 this.cachedTarget = target;
23506 if(this.beforeDragEnter(target, e, id) !== false){
23507 if(target.isNotifyTarget){
23508 var status = target.notifyEnter(this, e, this.dragData);
23509 this.proxy.setStatus(status);
23511 this.proxy.setStatus(this.dropAllowed);
23514 if(this.afterDragEnter){
23516 * An empty function by default, but provided so that you can perform a custom action
23517 * when the dragged item enters the drop target by providing an implementation.
23518 * @param {Roo.dd.DragDrop} target The drop target
23519 * @param {Event} e The event object
23520 * @param {String} id The id of the dragged element
23521 * @method afterDragEnter
23523 this.afterDragEnter(target, e, id);
23529 * An empty function by default, but provided so that you can perform a custom action
23530 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23531 * @param {Roo.dd.DragDrop} target The drop target
23532 * @param {Event} e The event object
23533 * @param {String} id The id of the dragged element
23534 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23536 beforeDragEnter : function(target, e, id){
23541 alignElWithMouse: function() {
23542 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23547 onDragOver : function(e, id){
23548 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23549 if(this.beforeDragOver(target, e, id) !== false){
23550 if(target.isNotifyTarget){
23551 var status = target.notifyOver(this, e, this.dragData);
23552 this.proxy.setStatus(status);
23555 if(this.afterDragOver){
23557 * An empty function by default, but provided so that you can perform a custom action
23558 * while the dragged item is over the drop target by providing an implementation.
23559 * @param {Roo.dd.DragDrop} target The drop target
23560 * @param {Event} e The event object
23561 * @param {String} id The id of the dragged element
23562 * @method afterDragOver
23564 this.afterDragOver(target, e, id);
23570 * An empty function by default, but provided so that you can perform a custom action
23571 * while the dragged item is over the drop target and optionally cancel the onDragOver.
23572 * @param {Roo.dd.DragDrop} target The drop target
23573 * @param {Event} e The event object
23574 * @param {String} id The id of the dragged element
23575 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23577 beforeDragOver : function(target, e, id){
23582 onDragOut : function(e, id){
23583 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23584 if(this.beforeDragOut(target, e, id) !== false){
23585 if(target.isNotifyTarget){
23586 target.notifyOut(this, e, this.dragData);
23588 this.proxy.reset();
23589 if(this.afterDragOut){
23591 * An empty function by default, but provided so that you can perform a custom action
23592 * after the dragged item is dragged out of the target without dropping.
23593 * @param {Roo.dd.DragDrop} target The drop target
23594 * @param {Event} e The event object
23595 * @param {String} id The id of the dragged element
23596 * @method afterDragOut
23598 this.afterDragOut(target, e, id);
23601 this.cachedTarget = null;
23605 * An empty function by default, but provided so that you can perform a custom action before the dragged
23606 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23607 * @param {Roo.dd.DragDrop} target The drop target
23608 * @param {Event} e The event object
23609 * @param {String} id The id of the dragged element
23610 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23612 beforeDragOut : function(target, e, id){
23617 onDragDrop : function(e, id){
23618 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23619 if(this.beforeDragDrop(target, e, id) !== false){
23620 if(target.isNotifyTarget){
23621 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23622 this.onValidDrop(target, e, id);
23624 this.onInvalidDrop(target, e, id);
23627 this.onValidDrop(target, e, id);
23630 if(this.afterDragDrop){
23632 * An empty function by default, but provided so that you can perform a custom action
23633 * after a valid drag drop has occurred by providing an implementation.
23634 * @param {Roo.dd.DragDrop} target The drop target
23635 * @param {Event} e The event object
23636 * @param {String} id The id of the dropped element
23637 * @method afterDragDrop
23639 this.afterDragDrop(target, e, id);
23642 delete this.cachedTarget;
23646 * An empty function by default, but provided so that you can perform a custom action before the dragged
23647 * item is dropped onto the target and optionally cancel the onDragDrop.
23648 * @param {Roo.dd.DragDrop} target The drop target
23649 * @param {Event} e The event object
23650 * @param {String} id The id of the dragged element
23651 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23653 beforeDragDrop : function(target, e, id){
23658 onValidDrop : function(target, e, id){
23660 if(this.afterValidDrop){
23662 * An empty function by default, but provided so that you can perform a custom action
23663 * after a valid drop has occurred by providing an implementation.
23664 * @param {Object} target The target DD
23665 * @param {Event} e The event object
23666 * @param {String} id The id of the dropped element
23667 * @method afterInvalidDrop
23669 this.afterValidDrop(target, e, id);
23674 getRepairXY : function(e, data){
23675 return this.el.getXY();
23679 onInvalidDrop : function(target, e, id){
23680 this.beforeInvalidDrop(target, e, id);
23681 if(this.cachedTarget){
23682 if(this.cachedTarget.isNotifyTarget){
23683 this.cachedTarget.notifyOut(this, e, this.dragData);
23685 this.cacheTarget = null;
23687 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23689 if(this.afterInvalidDrop){
23691 * An empty function by default, but provided so that you can perform a custom action
23692 * after an invalid drop has occurred by providing an implementation.
23693 * @param {Event} e The event object
23694 * @param {String} id The id of the dropped element
23695 * @method afterInvalidDrop
23697 this.afterInvalidDrop(e, id);
23702 afterRepair : function(){
23704 this.el.highlight(this.hlColor || "c3daf9");
23706 this.dragging = false;
23710 * An empty function by default, but provided so that you can perform a custom action after an invalid
23711 * drop has occurred.
23712 * @param {Roo.dd.DragDrop} target The drop target
23713 * @param {Event} e The event object
23714 * @param {String} id The id of the dragged element
23715 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23717 beforeInvalidDrop : function(target, e, id){
23722 handleMouseDown : function(e){
23723 if(this.dragging) {
23726 var data = this.getDragData(e);
23727 if(data && this.onBeforeDrag(data, e) !== false){
23728 this.dragData = data;
23730 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23735 * An empty function by default, but provided so that you can perform a custom action before the initial
23736 * drag event begins and optionally cancel it.
23737 * @param {Object} data An object containing arbitrary data to be shared with drop targets
23738 * @param {Event} e The event object
23739 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23741 onBeforeDrag : function(data, e){
23746 * An empty function by default, but provided so that you can perform a custom action once the initial
23747 * drag event has begun. The drag cannot be canceled from this function.
23748 * @param {Number} x The x position of the click on the dragged object
23749 * @param {Number} y The y position of the click on the dragged object
23751 onStartDrag : Roo.emptyFn,
23753 // private - YUI override
23754 startDrag : function(x, y){
23755 this.proxy.reset();
23756 this.dragging = true;
23757 this.proxy.update("");
23758 this.onInitDrag(x, y);
23763 onInitDrag : function(x, y){
23764 var clone = this.el.dom.cloneNode(true);
23765 clone.id = Roo.id(); // prevent duplicate ids
23766 this.proxy.update(clone);
23767 this.onStartDrag(x, y);
23772 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23773 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23775 getProxy : function(){
23780 * Hides the drag source's {@link Roo.dd.StatusProxy}
23782 hideProxy : function(){
23784 this.proxy.reset(true);
23785 this.dragging = false;
23789 triggerCacheRefresh : function(){
23790 Roo.dd.DDM.refreshCache(this.groups);
23793 // private - override to prevent hiding
23794 b4EndDrag: function(e) {
23797 // private - override to prevent moving
23798 endDrag : function(e){
23799 this.onEndDrag(this.dragData, e);
23803 onEndDrag : function(data, e){
23806 // private - pin to cursor
23807 autoOffset : function(x, y) {
23808 this.setDelta(-12, -20);
23812 * Ext JS Library 1.1.1
23813 * Copyright(c) 2006-2007, Ext JS, LLC.
23815 * Originally Released Under LGPL - original licence link has changed is not relivant.
23818 * <script type="text/javascript">
23823 * @class Roo.dd.DropTarget
23824 * @extends Roo.dd.DDTarget
23825 * A simple class that provides the basic implementation needed to make any element a drop target that can have
23826 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
23828 * @param {String/HTMLElement/Element} el The container element
23829 * @param {Object} config
23831 Roo.dd.DropTarget = function(el, config){
23832 this.el = Roo.get(el);
23834 var listeners = false; ;
23835 if (config && config.listeners) {
23836 listeners= config.listeners;
23837 delete config.listeners;
23839 Roo.apply(this, config);
23841 if(this.containerScroll){
23842 Roo.dd.ScrollManager.register(this.el);
23846 * @scope Roo.dd.DropTarget
23851 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23852 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
23853 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
23855 * IMPORTANT : it should set this.valid to true|false
23857 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23858 * @param {Event} e The event
23859 * @param {Object} data An object containing arbitrary data supplied by the drag source
23865 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23866 * This method will be called on every mouse movement while the drag source is over the drop target.
23867 * This default implementation simply returns the dropAllowed config value.
23869 * IMPORTANT : it should set this.valid to true|false
23871 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23872 * @param {Event} e The event
23873 * @param {Object} data An object containing arbitrary data supplied by the drag source
23879 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23880 * out of the target without dropping. This default implementation simply removes the CSS class specified by
23881 * overClass (if any) from the drop element.
23884 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23885 * @param {Event} e The event
23886 * @param {Object} data An object containing arbitrary data supplied by the drag source
23892 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23893 * been dropped on it. This method has no default implementation and returns false, so you must provide an
23894 * implementation that does something to process the drop event and returns true so that the drag source's
23895 * repair action does not run.
23897 * IMPORTANT : it should set this.success
23899 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23900 * @param {Event} e The event
23901 * @param {Object} data An object containing arbitrary data supplied by the drag source
23907 Roo.dd.DropTarget.superclass.constructor.call( this,
23909 this.ddGroup || this.group,
23912 listeners : listeners || {}
23920 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23922 * @cfg {String} overClass
23923 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23926 * @cfg {String} ddGroup
23927 * The drag drop group to handle drop events for
23931 * @cfg {String} dropAllowed
23932 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23934 dropAllowed : "x-dd-drop-ok",
23936 * @cfg {String} dropNotAllowed
23937 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23939 dropNotAllowed : "x-dd-drop-nodrop",
23941 * @cfg {boolean} success
23942 * set this after drop listener..
23946 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23947 * if the drop point is valid for over/enter..
23954 isNotifyTarget : true,
23959 notifyEnter : function(dd, e, data)
23962 this.fireEvent('enter', dd, e, data);
23963 if(this.overClass){
23964 this.el.addClass(this.overClass);
23966 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23967 this.valid ? this.dropAllowed : this.dropNotAllowed
23974 notifyOver : function(dd, e, data)
23977 this.fireEvent('over', dd, e, data);
23978 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23979 this.valid ? this.dropAllowed : this.dropNotAllowed
23986 notifyOut : function(dd, e, data)
23988 this.fireEvent('out', dd, e, data);
23989 if(this.overClass){
23990 this.el.removeClass(this.overClass);
23997 notifyDrop : function(dd, e, data)
23999 this.success = false;
24000 this.fireEvent('drop', dd, e, data);
24001 return this.success;
24005 * Ext JS Library 1.1.1
24006 * Copyright(c) 2006-2007, Ext JS, LLC.
24008 * Originally Released Under LGPL - original licence link has changed is not relivant.
24011 * <script type="text/javascript">
24016 * @class Roo.dd.DragZone
24017 * @extends Roo.dd.DragSource
24018 * This class provides a container DD instance that proxies for multiple child node sources.<br />
24019 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24021 * @param {String/HTMLElement/Element} el The container element
24022 * @param {Object} config
24024 Roo.dd.DragZone = function(el, config){
24025 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24026 if(this.containerScroll){
24027 Roo.dd.ScrollManager.register(this.el);
24031 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24033 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24034 * for auto scrolling during drag operations.
24037 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24038 * method after a failed drop (defaults to "c3daf9" - light blue)
24042 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24043 * for a valid target to drag based on the mouse down. Override this method
24044 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24045 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24046 * @param {EventObject} e The mouse down event
24047 * @return {Object} The dragData
24049 getDragData : function(e){
24050 return Roo.dd.Registry.getHandleFromEvent(e);
24054 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24055 * this.dragData.ddel
24056 * @param {Number} x The x position of the click on the dragged object
24057 * @param {Number} y The y position of the click on the dragged object
24058 * @return {Boolean} true to continue the drag, false to cancel
24060 onInitDrag : function(x, y){
24061 this.proxy.update(this.dragData.ddel.cloneNode(true));
24062 this.onStartDrag(x, y);
24067 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
24069 afterRepair : function(){
24071 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24073 this.dragging = false;
24077 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24078 * the XY of this.dragData.ddel
24079 * @param {EventObject} e The mouse up event
24080 * @return {Array} The xy location (e.g. [100, 200])
24082 getRepairXY : function(e){
24083 return Roo.Element.fly(this.dragData.ddel).getXY();
24087 * Ext JS Library 1.1.1
24088 * Copyright(c) 2006-2007, Ext JS, LLC.
24090 * Originally Released Under LGPL - original licence link has changed is not relivant.
24093 * <script type="text/javascript">
24096 * @class Roo.dd.DropZone
24097 * @extends Roo.dd.DropTarget
24098 * This class provides a container DD instance that proxies for multiple child node targets.<br />
24099 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24101 * @param {String/HTMLElement/Element} el The container element
24102 * @param {Object} config
24104 Roo.dd.DropZone = function(el, config){
24105 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24108 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24110 * Returns a custom data object associated with the DOM node that is the target of the event. By default
24111 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24112 * provide your own custom lookup.
24113 * @param {Event} e The event
24114 * @return {Object} data The custom data
24116 getTargetFromEvent : function(e){
24117 return Roo.dd.Registry.getTargetFromEvent(e);
24121 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24122 * that it has registered. This method has no default implementation and should be overridden to provide
24123 * node-specific processing if necessary.
24124 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24125 * {@link #getTargetFromEvent} for this node)
24126 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24127 * @param {Event} e The event
24128 * @param {Object} data An object containing arbitrary data supplied by the drag source
24130 onNodeEnter : function(n, dd, e, data){
24135 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24136 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
24137 * overridden to provide the proper feedback.
24138 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24139 * {@link #getTargetFromEvent} for this node)
24140 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24141 * @param {Event} e The event
24142 * @param {Object} data An object containing arbitrary data supplied by the drag source
24143 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24144 * underlying {@link Roo.dd.StatusProxy} can be updated
24146 onNodeOver : function(n, dd, e, data){
24147 return this.dropAllowed;
24151 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24152 * the drop node without dropping. This method has no default implementation and should be overridden to provide
24153 * node-specific processing if necessary.
24154 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24155 * {@link #getTargetFromEvent} for this node)
24156 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24157 * @param {Event} e The event
24158 * @param {Object} data An object containing arbitrary data supplied by the drag source
24160 onNodeOut : function(n, dd, e, data){
24165 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24166 * the drop node. The default implementation returns false, so it should be overridden to provide the
24167 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24168 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24169 * {@link #getTargetFromEvent} for this node)
24170 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24171 * @param {Event} e The event
24172 * @param {Object} data An object containing arbitrary data supplied by the drag source
24173 * @return {Boolean} True if the drop was valid, else false
24175 onNodeDrop : function(n, dd, e, data){
24180 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24181 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
24182 * it should be overridden to provide the proper feedback if necessary.
24183 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24184 * @param {Event} e The event
24185 * @param {Object} data An object containing arbitrary data supplied by the drag source
24186 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24187 * underlying {@link Roo.dd.StatusProxy} can be updated
24189 onContainerOver : function(dd, e, data){
24190 return this.dropNotAllowed;
24194 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24195 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
24196 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24197 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
24198 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24199 * @param {Event} e The event
24200 * @param {Object} data An object containing arbitrary data supplied by the drag source
24201 * @return {Boolean} True if the drop was valid, else false
24203 onContainerDrop : function(dd, e, data){
24208 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24209 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
24210 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24211 * you should override this method and provide a custom implementation.
24212 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24213 * @param {Event} e The event
24214 * @param {Object} data An object containing arbitrary data supplied by the drag source
24215 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24216 * underlying {@link Roo.dd.StatusProxy} can be updated
24218 notifyEnter : function(dd, e, data){
24219 return this.dropNotAllowed;
24223 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24224 * This method will be called on every mouse movement while the drag source is over the drop zone.
24225 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24226 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24227 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24228 * registered node, it will call {@link #onContainerOver}.
24229 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24230 * @param {Event} e The event
24231 * @param {Object} data An object containing arbitrary data supplied by the drag source
24232 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24233 * underlying {@link Roo.dd.StatusProxy} can be updated
24235 notifyOver : function(dd, e, data){
24236 var n = this.getTargetFromEvent(e);
24237 if(!n){ // not over valid drop target
24238 if(this.lastOverNode){
24239 this.onNodeOut(this.lastOverNode, dd, e, data);
24240 this.lastOverNode = null;
24242 return this.onContainerOver(dd, e, data);
24244 if(this.lastOverNode != n){
24245 if(this.lastOverNode){
24246 this.onNodeOut(this.lastOverNode, dd, e, data);
24248 this.onNodeEnter(n, dd, e, data);
24249 this.lastOverNode = n;
24251 return this.onNodeOver(n, dd, e, data);
24255 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24256 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
24257 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24258 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24259 * @param {Event} e The event
24260 * @param {Object} data An object containing arbitrary data supplied by the drag zone
24262 notifyOut : function(dd, e, data){
24263 if(this.lastOverNode){
24264 this.onNodeOut(this.lastOverNode, dd, e, data);
24265 this.lastOverNode = null;
24270 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24271 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
24272 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24273 * otherwise it will call {@link #onContainerDrop}.
24274 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24275 * @param {Event} e The event
24276 * @param {Object} data An object containing arbitrary data supplied by the drag source
24277 * @return {Boolean} True if the drop was valid, else false
24279 notifyDrop : function(dd, e, data){
24280 if(this.lastOverNode){
24281 this.onNodeOut(this.lastOverNode, dd, e, data);
24282 this.lastOverNode = null;
24284 var n = this.getTargetFromEvent(e);
24286 this.onNodeDrop(n, dd, e, data) :
24287 this.onContainerDrop(dd, e, data);
24291 triggerCacheRefresh : function(){
24292 Roo.dd.DDM.refreshCache(this.groups);
24296 * Ext JS Library 1.1.1
24297 * Copyright(c) 2006-2007, Ext JS, LLC.
24299 * Originally Released Under LGPL - original licence link has changed is not relivant.
24302 * <script type="text/javascript">
24307 * @class Roo.data.SortTypes
24309 * Defines the default sorting (casting?) comparison functions used when sorting data.
24311 Roo.data.SortTypes = {
24313 * Default sort that does nothing
24314 * @param {Mixed} s The value being converted
24315 * @return {Mixed} The comparison value
24317 none : function(s){
24322 * The regular expression used to strip tags
24326 stripTagsRE : /<\/?[^>]+>/gi,
24329 * Strips all HTML tags to sort on text only
24330 * @param {Mixed} s The value being converted
24331 * @return {String} The comparison value
24333 asText : function(s){
24334 return String(s).replace(this.stripTagsRE, "");
24338 * Strips all HTML tags to sort on text only - Case insensitive
24339 * @param {Mixed} s The value being converted
24340 * @return {String} The comparison value
24342 asUCText : function(s){
24343 return String(s).toUpperCase().replace(this.stripTagsRE, "");
24347 * Case insensitive string
24348 * @param {Mixed} s The value being converted
24349 * @return {String} The comparison value
24351 asUCString : function(s) {
24352 return String(s).toUpperCase();
24357 * @param {Mixed} s The value being converted
24358 * @return {Number} The comparison value
24360 asDate : function(s) {
24364 if(s instanceof Date){
24365 return s.getTime();
24367 return Date.parse(String(s));
24372 * @param {Mixed} s The value being converted
24373 * @return {Float} The comparison value
24375 asFloat : function(s) {
24376 var val = parseFloat(String(s).replace(/,/g, ""));
24385 * @param {Mixed} s The value being converted
24386 * @return {Number} The comparison value
24388 asInt : function(s) {
24389 var val = parseInt(String(s).replace(/,/g, ""));
24397 * Ext JS Library 1.1.1
24398 * Copyright(c) 2006-2007, Ext JS, LLC.
24400 * Originally Released Under LGPL - original licence link has changed is not relivant.
24403 * <script type="text/javascript">
24407 * @class Roo.data.Record
24408 * Instances of this class encapsulate both record <em>definition</em> information, and record
24409 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24410 * to access Records cached in an {@link Roo.data.Store} object.<br>
24412 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24413 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24416 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24418 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24419 * {@link #create}. The parameters are the same.
24420 * @param {Array} data An associative Array of data values keyed by the field name.
24421 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24422 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24423 * not specified an integer id is generated.
24425 Roo.data.Record = function(data, id){
24426 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24431 * Generate a constructor for a specific record layout.
24432 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24433 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24434 * Each field definition object may contain the following properties: <ul>
24435 * <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,
24436 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24437 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24438 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24439 * is being used, then this is a string containing the javascript expression to reference the data relative to
24440 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24441 * to the data item relative to the record element. If the mapping expression is the same as the field name,
24442 * this may be omitted.</p></li>
24443 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24444 * <ul><li>auto (Default, implies no conversion)</li>
24449 * <li>date</li></ul></p></li>
24450 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24451 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24452 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24453 * by the Reader into an object that will be stored in the Record. It is passed the
24454 * following parameters:<ul>
24455 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24457 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24459 * <br>usage:<br><pre><code>
24460 var TopicRecord = Roo.data.Record.create(
24461 {name: 'title', mapping: 'topic_title'},
24462 {name: 'author', mapping: 'username'},
24463 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24464 {name: 'lastPost', mapping: 'post_time', type: 'date'},
24465 {name: 'lastPoster', mapping: 'user2'},
24466 {name: 'excerpt', mapping: 'post_text'}
24469 var myNewRecord = new TopicRecord({
24470 title: 'Do my job please',
24473 lastPost: new Date(),
24474 lastPoster: 'Animal',
24475 excerpt: 'No way dude!'
24477 myStore.add(myNewRecord);
24482 Roo.data.Record.create = function(o){
24483 var f = function(){
24484 f.superclass.constructor.apply(this, arguments);
24486 Roo.extend(f, Roo.data.Record);
24487 var p = f.prototype;
24488 p.fields = new Roo.util.MixedCollection(false, function(field){
24491 for(var i = 0, len = o.length; i < len; i++){
24492 p.fields.add(new Roo.data.Field(o[i]));
24494 f.getField = function(name){
24495 return p.fields.get(name);
24500 Roo.data.Record.AUTO_ID = 1000;
24501 Roo.data.Record.EDIT = 'edit';
24502 Roo.data.Record.REJECT = 'reject';
24503 Roo.data.Record.COMMIT = 'commit';
24505 Roo.data.Record.prototype = {
24507 * Readonly flag - true if this record has been modified.
24516 join : function(store){
24517 this.store = store;
24521 * Set the named field to the specified value.
24522 * @param {String} name The name of the field to set.
24523 * @param {Object} value The value to set the field to.
24525 set : function(name, value){
24526 if(this.data[name] == value){
24530 if(!this.modified){
24531 this.modified = {};
24533 if(typeof this.modified[name] == 'undefined'){
24534 this.modified[name] = this.data[name];
24536 this.data[name] = value;
24537 if(!this.editing && this.store){
24538 this.store.afterEdit(this);
24543 * Get the value of the named field.
24544 * @param {String} name The name of the field to get the value of.
24545 * @return {Object} The value of the field.
24547 get : function(name){
24548 return this.data[name];
24552 beginEdit : function(){
24553 this.editing = true;
24554 this.modified = {};
24558 cancelEdit : function(){
24559 this.editing = false;
24560 delete this.modified;
24564 endEdit : function(){
24565 this.editing = false;
24566 if(this.dirty && this.store){
24567 this.store.afterEdit(this);
24572 * Usually called by the {@link Roo.data.Store} which owns the Record.
24573 * Rejects all changes made to the Record since either creation, or the last commit operation.
24574 * Modified fields are reverted to their original values.
24576 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24577 * of reject operations.
24579 reject : function(){
24580 var m = this.modified;
24582 if(typeof m[n] != "function"){
24583 this.data[n] = m[n];
24586 this.dirty = false;
24587 delete this.modified;
24588 this.editing = false;
24590 this.store.afterReject(this);
24595 * Usually called by the {@link Roo.data.Store} which owns the Record.
24596 * Commits all changes made to the Record since either creation, or the last commit operation.
24598 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24599 * of commit operations.
24601 commit : function(){
24602 this.dirty = false;
24603 delete this.modified;
24604 this.editing = false;
24606 this.store.afterCommit(this);
24611 hasError : function(){
24612 return this.error != null;
24616 clearError : function(){
24621 * Creates a copy of this record.
24622 * @param {String} id (optional) A new record id if you don't want to use this record's id
24625 copy : function(newId) {
24626 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24630 * Ext JS Library 1.1.1
24631 * Copyright(c) 2006-2007, Ext JS, LLC.
24633 * Originally Released Under LGPL - original licence link has changed is not relivant.
24636 * <script type="text/javascript">
24642 * @class Roo.data.Store
24643 * @extends Roo.util.Observable
24644 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24645 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24647 * 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
24648 * has no knowledge of the format of the data returned by the Proxy.<br>
24650 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24651 * instances from the data object. These records are cached and made available through accessor functions.
24653 * Creates a new Store.
24654 * @param {Object} config A config object containing the objects needed for the Store to access data,
24655 * and read the data into Records.
24657 Roo.data.Store = function(config){
24658 this.data = new Roo.util.MixedCollection(false);
24659 this.data.getKey = function(o){
24662 this.baseParams = {};
24664 this.paramNames = {
24669 "multisort" : "_multisort"
24672 if(config && config.data){
24673 this.inlineData = config.data;
24674 delete config.data;
24677 Roo.apply(this, config);
24679 if(this.reader){ // reader passed
24680 this.reader = Roo.factory(this.reader, Roo.data);
24681 this.reader.xmodule = this.xmodule || false;
24682 if(!this.recordType){
24683 this.recordType = this.reader.recordType;
24685 if(this.reader.onMetaChange){
24686 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24690 if(this.recordType){
24691 this.fields = this.recordType.prototype.fields;
24693 this.modified = [];
24697 * @event datachanged
24698 * Fires when the data cache has changed, and a widget which is using this Store
24699 * as a Record cache should refresh its view.
24700 * @param {Store} this
24702 datachanged : true,
24704 * @event metachange
24705 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24706 * @param {Store} this
24707 * @param {Object} meta The JSON metadata
24712 * Fires when Records have been added to the Store
24713 * @param {Store} this
24714 * @param {Roo.data.Record[]} records The array of Records added
24715 * @param {Number} index The index at which the record(s) were added
24720 * Fires when a Record has been removed from the Store
24721 * @param {Store} this
24722 * @param {Roo.data.Record} record The Record that was removed
24723 * @param {Number} index The index at which the record was removed
24728 * Fires when a Record has been updated
24729 * @param {Store} this
24730 * @param {Roo.data.Record} record The Record that was updated
24731 * @param {String} operation The update operation being performed. Value may be one of:
24733 Roo.data.Record.EDIT
24734 Roo.data.Record.REJECT
24735 Roo.data.Record.COMMIT
24741 * Fires when the data cache has been cleared.
24742 * @param {Store} this
24746 * @event beforeload
24747 * Fires before a request is made for a new data object. If the beforeload handler returns false
24748 * the load action will be canceled.
24749 * @param {Store} this
24750 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24754 * @event beforeloadadd
24755 * Fires after a new set of Records has been loaded.
24756 * @param {Store} this
24757 * @param {Roo.data.Record[]} records The Records that were loaded
24758 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24760 beforeloadadd : true,
24763 * Fires after a new set of Records has been loaded, before they are added to the store.
24764 * @param {Store} this
24765 * @param {Roo.data.Record[]} records The Records that were loaded
24766 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24767 * @params {Object} return from reader
24771 * @event loadexception
24772 * Fires if an exception occurs in the Proxy during loading.
24773 * Called with the signature of the Proxy's "loadexception" event.
24774 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24777 * @param {Object} return from JsonData.reader() - success, totalRecords, records
24778 * @param {Object} load options
24779 * @param {Object} jsonData from your request (normally this contains the Exception)
24781 loadexception : true
24785 this.proxy = Roo.factory(this.proxy, Roo.data);
24786 this.proxy.xmodule = this.xmodule || false;
24787 this.relayEvents(this.proxy, ["loadexception"]);
24789 this.sortToggle = {};
24790 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24792 Roo.data.Store.superclass.constructor.call(this);
24794 if(this.inlineData){
24795 this.loadData(this.inlineData);
24796 delete this.inlineData;
24800 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24802 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
24803 * without a remote query - used by combo/forms at present.
24807 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24810 * @cfg {Array} data Inline data to be loaded when the store is initialized.
24813 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
24814 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24817 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24818 * on any HTTP request
24821 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24824 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24828 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24829 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24831 remoteSort : false,
24834 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24835 * loaded or when a record is removed. (defaults to false).
24837 pruneModifiedRecords : false,
24840 lastOptions : null,
24843 * Add Records to the Store and fires the add event.
24844 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24846 add : function(records){
24847 records = [].concat(records);
24848 for(var i = 0, len = records.length; i < len; i++){
24849 records[i].join(this);
24851 var index = this.data.length;
24852 this.data.addAll(records);
24853 this.fireEvent("add", this, records, index);
24857 * Remove a Record from the Store and fires the remove event.
24858 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24860 remove : function(record){
24861 var index = this.data.indexOf(record);
24862 this.data.removeAt(index);
24864 if(this.pruneModifiedRecords){
24865 this.modified.remove(record);
24867 this.fireEvent("remove", this, record, index);
24871 * Remove all Records from the Store and fires the clear event.
24873 removeAll : function(){
24875 if(this.pruneModifiedRecords){
24876 this.modified = [];
24878 this.fireEvent("clear", this);
24882 * Inserts Records to the Store at the given index and fires the add event.
24883 * @param {Number} index The start index at which to insert the passed Records.
24884 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24886 insert : function(index, records){
24887 records = [].concat(records);
24888 for(var i = 0, len = records.length; i < len; i++){
24889 this.data.insert(index, records[i]);
24890 records[i].join(this);
24892 this.fireEvent("add", this, records, index);
24896 * Get the index within the cache of the passed Record.
24897 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24898 * @return {Number} The index of the passed Record. Returns -1 if not found.
24900 indexOf : function(record){
24901 return this.data.indexOf(record);
24905 * Get the index within the cache of the Record with the passed id.
24906 * @param {String} id The id of the Record to find.
24907 * @return {Number} The index of the Record. Returns -1 if not found.
24909 indexOfId : function(id){
24910 return this.data.indexOfKey(id);
24914 * Get the Record with the specified id.
24915 * @param {String} id The id of the Record to find.
24916 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24918 getById : function(id){
24919 return this.data.key(id);
24923 * Get the Record at the specified index.
24924 * @param {Number} index The index of the Record to find.
24925 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24927 getAt : function(index){
24928 return this.data.itemAt(index);
24932 * Returns a range of Records between specified indices.
24933 * @param {Number} startIndex (optional) The starting index (defaults to 0)
24934 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24935 * @return {Roo.data.Record[]} An array of Records
24937 getRange : function(start, end){
24938 return this.data.getRange(start, end);
24942 storeOptions : function(o){
24943 o = Roo.apply({}, o);
24946 this.lastOptions = o;
24950 * Loads the Record cache from the configured Proxy using the configured Reader.
24952 * If using remote paging, then the first load call must specify the <em>start</em>
24953 * and <em>limit</em> properties in the options.params property to establish the initial
24954 * position within the dataset, and the number of Records to cache on each read from the Proxy.
24956 * <strong>It is important to note that for remote data sources, loading is asynchronous,
24957 * and this call will return before the new data has been loaded. Perform any post-processing
24958 * in a callback function, or in a "load" event handler.</strong>
24960 * @param {Object} options An object containing properties which control loading options:<ul>
24961 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24962 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24965 data : data, // array of key=>value data like JsonReader
24966 total : data.length,
24972 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24973 * passed the following arguments:<ul>
24974 * <li>r : Roo.data.Record[]</li>
24975 * <li>options: Options object from the load call</li>
24976 * <li>success: Boolean success indicator</li></ul></li>
24977 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24978 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24981 load : function(options){
24982 options = options || {};
24983 if(this.fireEvent("beforeload", this, options) !== false){
24984 this.storeOptions(options);
24985 var p = Roo.apply(options.params || {}, this.baseParams);
24986 // if meta was not loaded from remote source.. try requesting it.
24987 if (!this.reader.metaFromRemote) {
24988 p._requestMeta = 1;
24990 if(this.sortInfo && this.remoteSort){
24991 var pn = this.paramNames;
24992 p[pn["sort"]] = this.sortInfo.field;
24993 p[pn["dir"]] = this.sortInfo.direction;
24995 if (this.multiSort) {
24996 var pn = this.paramNames;
24997 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25000 this.proxy.load(p, this.reader, this.loadRecords, this, options);
25005 * Reloads the Record cache from the configured Proxy using the configured Reader and
25006 * the options from the last load operation performed.
25007 * @param {Object} options (optional) An object containing properties which may override the options
25008 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25009 * the most recently used options are reused).
25011 reload : function(options){
25012 this.load(Roo.applyIf(options||{}, this.lastOptions));
25016 // Called as a callback by the Reader during a load operation.
25017 loadRecords : function(o, options, success){
25020 if(success !== false){
25021 this.fireEvent("load", this, [], options, o);
25023 if(options.callback){
25024 options.callback.call(options.scope || this, [], options, false);
25028 // if data returned failure - throw an exception.
25029 if (o.success === false) {
25030 // show a message if no listener is registered.
25031 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25032 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25034 // loadmask wil be hooked into this..
25035 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25038 var r = o.records, t = o.totalRecords || r.length;
25040 this.fireEvent("beforeloadadd", this, r, options, o);
25042 if(!options || options.add !== true){
25043 if(this.pruneModifiedRecords){
25044 this.modified = [];
25046 for(var i = 0, len = r.length; i < len; i++){
25050 this.data = this.snapshot;
25051 delete this.snapshot;
25054 this.data.addAll(r);
25055 this.totalLength = t;
25057 this.fireEvent("datachanged", this);
25059 this.totalLength = Math.max(t, this.data.length+r.length);
25063 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25065 var e = new Roo.data.Record({});
25067 e.set(this.parent.displayField, this.parent.emptyTitle);
25068 e.set(this.parent.valueField, '');
25073 this.fireEvent("load", this, r, options, o);
25074 if(options.callback){
25075 options.callback.call(options.scope || this, r, options, true);
25081 * Loads data from a passed data block. A Reader which understands the format of the data
25082 * must have been configured in the constructor.
25083 * @param {Object} data The data block from which to read the Records. The format of the data expected
25084 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25085 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25087 loadData : function(o, append){
25088 var r = this.reader.readRecords(o);
25089 this.loadRecords(r, {add: append}, true);
25093 * using 'cn' the nested child reader read the child array into it's child stores.
25094 * @param {Object} rec The record with a 'children array
25096 loadDataFromChildren : function(rec)
25098 this.loadData(this.reader.toLoadData(rec));
25103 * Gets the number of cached records.
25105 * <em>If using paging, this may not be the total size of the dataset. If the data object
25106 * used by the Reader contains the dataset size, then the getTotalCount() function returns
25107 * the data set size</em>
25109 getCount : function(){
25110 return this.data.length || 0;
25114 * Gets the total number of records in the dataset as returned by the server.
25116 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25117 * the dataset size</em>
25119 getTotalCount : function(){
25120 return this.totalLength || 0;
25124 * Returns the sort state of the Store as an object with two properties:
25126 field {String} The name of the field by which the Records are sorted
25127 direction {String} The sort order, "ASC" or "DESC"
25130 getSortState : function(){
25131 return this.sortInfo;
25135 applySort : function(){
25136 if(this.sortInfo && !this.remoteSort){
25137 var s = this.sortInfo, f = s.field;
25138 var st = this.fields.get(f).sortType;
25139 var fn = function(r1, r2){
25140 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25141 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25143 this.data.sort(s.direction, fn);
25144 if(this.snapshot && this.snapshot != this.data){
25145 this.snapshot.sort(s.direction, fn);
25151 * Sets the default sort column and order to be used by the next load operation.
25152 * @param {String} fieldName The name of the field to sort by.
25153 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25155 setDefaultSort : function(field, dir){
25156 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25160 * Sort the Records.
25161 * If remote sorting is used, the sort is performed on the server, and the cache is
25162 * reloaded. If local sorting is used, the cache is sorted internally.
25163 * @param {String} fieldName The name of the field to sort by.
25164 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25166 sort : function(fieldName, dir){
25167 var f = this.fields.get(fieldName);
25169 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25171 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25172 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25177 this.sortToggle[f.name] = dir;
25178 this.sortInfo = {field: f.name, direction: dir};
25179 if(!this.remoteSort){
25181 this.fireEvent("datachanged", this);
25183 this.load(this.lastOptions);
25188 * Calls the specified function for each of the Records in the cache.
25189 * @param {Function} fn The function to call. The Record is passed as the first parameter.
25190 * Returning <em>false</em> aborts and exits the iteration.
25191 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25193 each : function(fn, scope){
25194 this.data.each(fn, scope);
25198 * Gets all records modified since the last commit. Modified records are persisted across load operations
25199 * (e.g., during paging).
25200 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25202 getModifiedRecords : function(){
25203 return this.modified;
25207 createFilterFn : function(property, value, anyMatch){
25208 if(!value.exec){ // not a regex
25209 value = String(value);
25210 if(value.length == 0){
25213 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25215 return function(r){
25216 return value.test(r.data[property]);
25221 * Sums the value of <i>property</i> for each record between start and end and returns the result.
25222 * @param {String} property A field on your records
25223 * @param {Number} start The record index to start at (defaults to 0)
25224 * @param {Number} end The last record index to include (defaults to length - 1)
25225 * @return {Number} The sum
25227 sum : function(property, start, end){
25228 var rs = this.data.items, v = 0;
25229 start = start || 0;
25230 end = (end || end === 0) ? end : rs.length-1;
25232 for(var i = start; i <= end; i++){
25233 v += (rs[i].data[property] || 0);
25239 * Filter the records by a specified property.
25240 * @param {String} field A field on your records
25241 * @param {String/RegExp} value Either a string that the field
25242 * should start with or a RegExp to test against the field
25243 * @param {Boolean} anyMatch True to match any part not just the beginning
25245 filter : function(property, value, anyMatch){
25246 var fn = this.createFilterFn(property, value, anyMatch);
25247 return fn ? this.filterBy(fn) : this.clearFilter();
25251 * Filter by a function. The specified function will be called with each
25252 * record in this data source. If the function returns true the record is included,
25253 * otherwise it is filtered.
25254 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25255 * @param {Object} scope (optional) The scope of the function (defaults to this)
25257 filterBy : function(fn, scope){
25258 this.snapshot = this.snapshot || this.data;
25259 this.data = this.queryBy(fn, scope||this);
25260 this.fireEvent("datachanged", this);
25264 * Query the records by a specified property.
25265 * @param {String} field A field on your records
25266 * @param {String/RegExp} value Either a string that the field
25267 * should start with or a RegExp to test against the field
25268 * @param {Boolean} anyMatch True to match any part not just the beginning
25269 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25271 query : function(property, value, anyMatch){
25272 var fn = this.createFilterFn(property, value, anyMatch);
25273 return fn ? this.queryBy(fn) : this.data.clone();
25277 * Query by a function. The specified function will be called with each
25278 * record in this data source. If the function returns true the record is included
25280 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25281 * @param {Object} scope (optional) The scope of the function (defaults to this)
25282 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25284 queryBy : function(fn, scope){
25285 var data = this.snapshot || this.data;
25286 return data.filterBy(fn, scope||this);
25290 * Collects unique values for a particular dataIndex from this store.
25291 * @param {String} dataIndex The property to collect
25292 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25293 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25294 * @return {Array} An array of the unique values
25296 collect : function(dataIndex, allowNull, bypassFilter){
25297 var d = (bypassFilter === true && this.snapshot) ?
25298 this.snapshot.items : this.data.items;
25299 var v, sv, r = [], l = {};
25300 for(var i = 0, len = d.length; i < len; i++){
25301 v = d[i].data[dataIndex];
25303 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25312 * Revert to a view of the Record cache with no filtering applied.
25313 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25315 clearFilter : function(suppressEvent){
25316 if(this.snapshot && this.snapshot != this.data){
25317 this.data = this.snapshot;
25318 delete this.snapshot;
25319 if(suppressEvent !== true){
25320 this.fireEvent("datachanged", this);
25326 afterEdit : function(record){
25327 if(this.modified.indexOf(record) == -1){
25328 this.modified.push(record);
25330 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25334 afterReject : function(record){
25335 this.modified.remove(record);
25336 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25340 afterCommit : function(record){
25341 this.modified.remove(record);
25342 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25346 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25347 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25349 commitChanges : function(){
25350 var m = this.modified.slice(0);
25351 this.modified = [];
25352 for(var i = 0, len = m.length; i < len; i++){
25358 * Cancel outstanding changes on all changed records.
25360 rejectChanges : function(){
25361 var m = this.modified.slice(0);
25362 this.modified = [];
25363 for(var i = 0, len = m.length; i < len; i++){
25368 onMetaChange : function(meta, rtype, o){
25369 this.recordType = rtype;
25370 this.fields = rtype.prototype.fields;
25371 delete this.snapshot;
25372 this.sortInfo = meta.sortInfo || this.sortInfo;
25373 this.modified = [];
25374 this.fireEvent('metachange', this, this.reader.meta);
25377 moveIndex : function(data, type)
25379 var index = this.indexOf(data);
25381 var newIndex = index + type;
25385 this.insert(newIndex, data);
25390 * Ext JS Library 1.1.1
25391 * Copyright(c) 2006-2007, Ext JS, LLC.
25393 * Originally Released Under LGPL - original licence link has changed is not relivant.
25396 * <script type="text/javascript">
25400 * @class Roo.data.SimpleStore
25401 * @extends Roo.data.Store
25402 * Small helper class to make creating Stores from Array data easier.
25403 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25404 * @cfg {Array} fields An array of field definition objects, or field name strings.
25405 * @cfg {Object} an existing reader (eg. copied from another store)
25406 * @cfg {Array} data The multi-dimensional array of data
25407 * @cfg {Roo.data.DataProxy} proxy [not-required]
25408 * @cfg {Roo.data.Reader} reader [not-required]
25410 * @param {Object} config
25412 Roo.data.SimpleStore = function(config)
25414 Roo.data.SimpleStore.superclass.constructor.call(this, {
25416 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25419 Roo.data.Record.create(config.fields)
25421 proxy : new Roo.data.MemoryProxy(config.data)
25425 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25427 * Ext JS Library 1.1.1
25428 * Copyright(c) 2006-2007, Ext JS, LLC.
25430 * Originally Released Under LGPL - original licence link has changed is not relivant.
25433 * <script type="text/javascript">
25438 * @extends Roo.data.Store
25439 * @class Roo.data.JsonStore
25440 * Small helper class to make creating Stores for JSON data easier. <br/>
25442 var store = new Roo.data.JsonStore({
25443 url: 'get-images.php',
25445 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25448 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25449 * JsonReader and HttpProxy (unless inline data is provided).</b>
25450 * @cfg {Array} fields An array of field definition objects, or field name strings.
25452 * @param {Object} config
25454 Roo.data.JsonStore = function(c){
25455 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25456 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25457 reader: new Roo.data.JsonReader(c, c.fields)
25460 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25462 * Ext JS Library 1.1.1
25463 * Copyright(c) 2006-2007, Ext JS, LLC.
25465 * Originally Released Under LGPL - original licence link has changed is not relivant.
25468 * <script type="text/javascript">
25472 Roo.data.Field = function(config){
25473 if(typeof config == "string"){
25474 config = {name: config};
25476 Roo.apply(this, config);
25479 this.type = "auto";
25482 var st = Roo.data.SortTypes;
25483 // named sortTypes are supported, here we look them up
25484 if(typeof this.sortType == "string"){
25485 this.sortType = st[this.sortType];
25488 // set default sortType for strings and dates
25489 if(!this.sortType){
25492 this.sortType = st.asUCString;
25495 this.sortType = st.asDate;
25498 this.sortType = st.none;
25503 var stripRe = /[\$,%]/g;
25505 // prebuilt conversion function for this field, instead of
25506 // switching every time we're reading a value
25508 var cv, dateFormat = this.dateFormat;
25513 cv = function(v){ return v; };
25516 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25520 return v !== undefined && v !== null && v !== '' ?
25521 parseInt(String(v).replace(stripRe, ""), 10) : '';
25526 return v !== undefined && v !== null && v !== '' ?
25527 parseFloat(String(v).replace(stripRe, ""), 10) : '';
25532 cv = function(v){ return v === true || v === "true" || v == 1; };
25539 if(v instanceof Date){
25543 if(dateFormat == "timestamp"){
25544 return new Date(v*1000);
25546 return Date.parseDate(v, dateFormat);
25548 var parsed = Date.parse(v);
25549 return parsed ? new Date(parsed) : null;
25558 Roo.data.Field.prototype = {
25566 * Ext JS Library 1.1.1
25567 * Copyright(c) 2006-2007, Ext JS, LLC.
25569 * Originally Released Under LGPL - original licence link has changed is not relivant.
25572 * <script type="text/javascript">
25575 // Base class for reading structured data from a data source. This class is intended to be
25576 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25579 * @class Roo.data.DataReader
25581 * Base class for reading structured data from a data source. This class is intended to be
25582 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25585 Roo.data.DataReader = function(meta, recordType){
25589 this.recordType = recordType instanceof Array ?
25590 Roo.data.Record.create(recordType) : recordType;
25593 Roo.data.DataReader.prototype = {
25596 readerType : 'Data',
25598 * Create an empty record
25599 * @param {Object} data (optional) - overlay some values
25600 * @return {Roo.data.Record} record created.
25602 newRow : function(d) {
25604 this.recordType.prototype.fields.each(function(c) {
25606 case 'int' : da[c.name] = 0; break;
25607 case 'date' : da[c.name] = new Date(); break;
25608 case 'float' : da[c.name] = 0.0; break;
25609 case 'boolean' : da[c.name] = false; break;
25610 default : da[c.name] = ""; break;
25614 return new this.recordType(Roo.apply(da, d));
25620 * Ext JS Library 1.1.1
25621 * Copyright(c) 2006-2007, Ext JS, LLC.
25623 * Originally Released Under LGPL - original licence link has changed is not relivant.
25626 * <script type="text/javascript">
25630 * @class Roo.data.DataProxy
25631 * @extends Roo.util.Observable
25633 * This class is an abstract base class for implementations which provide retrieval of
25634 * unformatted data objects.<br>
25636 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25637 * (of the appropriate type which knows how to parse the data object) to provide a block of
25638 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25640 * Custom implementations must implement the load method as described in
25641 * {@link Roo.data.HttpProxy#load}.
25643 Roo.data.DataProxy = function(){
25646 * @event beforeload
25647 * Fires before a network request is made to retrieve a data object.
25648 * @param {Object} This DataProxy object.
25649 * @param {Object} params The params parameter to the load function.
25654 * Fires before the load method's callback is called.
25655 * @param {Object} This DataProxy object.
25656 * @param {Object} o The data object.
25657 * @param {Object} arg The callback argument object passed to the load function.
25661 * @event loadexception
25662 * Fires if an Exception occurs during data retrieval.
25663 * @param {Object} This DataProxy object.
25664 * @param {Object} o The data object.
25665 * @param {Object} arg The callback argument object passed to the load function.
25666 * @param {Object} e The Exception.
25668 loadexception : true
25670 Roo.data.DataProxy.superclass.constructor.call(this);
25673 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25676 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25680 * Ext JS Library 1.1.1
25681 * Copyright(c) 2006-2007, Ext JS, LLC.
25683 * Originally Released Under LGPL - original licence link has changed is not relivant.
25686 * <script type="text/javascript">
25689 * @class Roo.data.MemoryProxy
25690 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25691 * to the Reader when its load method is called.
25693 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25695 Roo.data.MemoryProxy = function(data){
25699 Roo.data.MemoryProxy.superclass.constructor.call(this);
25703 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25706 * Load data from the requested source (in this case an in-memory
25707 * data object passed to the constructor), read the data object into
25708 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25709 * process that block using the passed callback.
25710 * @param {Object} params This parameter is not used by the MemoryProxy class.
25711 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25712 * object into a block of Roo.data.Records.
25713 * @param {Function} callback The function into which to pass the block of Roo.data.records.
25714 * The function must be passed <ul>
25715 * <li>The Record block object</li>
25716 * <li>The "arg" argument from the load function</li>
25717 * <li>A boolean success indicator</li>
25719 * @param {Object} scope The scope in which to call the callback
25720 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25722 load : function(params, reader, callback, scope, arg){
25723 params = params || {};
25726 result = reader.readRecords(params.data ? params.data :this.data);
25728 this.fireEvent("loadexception", this, arg, null, e);
25729 callback.call(scope, null, arg, false);
25732 callback.call(scope, result, arg, true);
25736 update : function(params, records){
25741 * Ext JS Library 1.1.1
25742 * Copyright(c) 2006-2007, Ext JS, LLC.
25744 * Originally Released Under LGPL - original licence link has changed is not relivant.
25747 * <script type="text/javascript">
25750 * @class Roo.data.HttpProxy
25751 * @extends Roo.data.DataProxy
25752 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25753 * configured to reference a certain URL.<br><br>
25755 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25756 * from which the running page was served.<br><br>
25758 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25760 * Be aware that to enable the browser to parse an XML document, the server must set
25761 * the Content-Type header in the HTTP response to "text/xml".
25763 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25764 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
25765 * will be used to make the request.
25767 Roo.data.HttpProxy = function(conn){
25768 Roo.data.HttpProxy.superclass.constructor.call(this);
25769 // is conn a conn config or a real conn?
25771 this.useAjax = !conn || !conn.events;
25775 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25776 // thse are take from connection...
25779 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25782 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25783 * extra parameters to each request made by this object. (defaults to undefined)
25786 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25787 * to each request made by this object. (defaults to undefined)
25790 * @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)
25793 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25796 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25802 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25806 * Return the {@link Roo.data.Connection} object being used by this Proxy.
25807 * @return {Connection} The Connection object. This object may be used to subscribe to events on
25808 * a finer-grained basis than the DataProxy events.
25810 getConnection : function(){
25811 return this.useAjax ? Roo.Ajax : this.conn;
25815 * Load data from the configured {@link Roo.data.Connection}, read the data object into
25816 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25817 * process that block using the passed callback.
25818 * @param {Object} params An object containing properties which are to be used as HTTP parameters
25819 * for the request to the remote server.
25820 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25821 * object into a block of Roo.data.Records.
25822 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25823 * The function must be passed <ul>
25824 * <li>The Record block object</li>
25825 * <li>The "arg" argument from the load function</li>
25826 * <li>A boolean success indicator</li>
25828 * @param {Object} scope The scope in which to call the callback
25829 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25831 load : function(params, reader, callback, scope, arg){
25832 if(this.fireEvent("beforeload", this, params) !== false){
25834 params : params || {},
25836 callback : callback,
25841 callback : this.loadResponse,
25845 Roo.applyIf(o, this.conn);
25846 if(this.activeRequest){
25847 Roo.Ajax.abort(this.activeRequest);
25849 this.activeRequest = Roo.Ajax.request(o);
25851 this.conn.request(o);
25854 callback.call(scope||this, null, arg, false);
25859 loadResponse : function(o, success, response){
25860 delete this.activeRequest;
25862 this.fireEvent("loadexception", this, o, response);
25863 o.request.callback.call(o.request.scope, null, o.request.arg, false);
25868 result = o.reader.read(response);
25871 o.raw = { errorMsg : response.responseText };
25872 this.fireEvent("loadexception", this, o, response, e);
25873 o.request.callback.call(o.request.scope, o, o.request.arg, false);
25877 this.fireEvent("load", this, o, o.request.arg);
25878 o.request.callback.call(o.request.scope, result, o.request.arg, true);
25882 update : function(dataSet){
25887 updateResponse : function(dataSet){
25892 * Ext JS Library 1.1.1
25893 * Copyright(c) 2006-2007, Ext JS, LLC.
25895 * Originally Released Under LGPL - original licence link has changed is not relivant.
25898 * <script type="text/javascript">
25902 * @class Roo.data.ScriptTagProxy
25903 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25904 * other than the originating domain of the running page.<br><br>
25906 * <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
25907 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25909 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25910 * source code that is used as the source inside a <script> tag.<br><br>
25912 * In order for the browser to process the returned data, the server must wrap the data object
25913 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25914 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25915 * depending on whether the callback name was passed:
25918 boolean scriptTag = false;
25919 String cb = request.getParameter("callback");
25922 response.setContentType("text/javascript");
25924 response.setContentType("application/x-json");
25926 Writer out = response.getWriter();
25928 out.write(cb + "(");
25930 out.print(dataBlock.toJsonString());
25937 * @param {Object} config A configuration object.
25939 Roo.data.ScriptTagProxy = function(config){
25940 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25941 Roo.apply(this, config);
25942 this.head = document.getElementsByTagName("head")[0];
25945 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25947 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25949 * @cfg {String} url The URL from which to request the data object.
25952 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25956 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25957 * the server the name of the callback function set up by the load call to process the returned data object.
25958 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25959 * javascript output which calls this named function passing the data object as its only parameter.
25961 callbackParam : "callback",
25963 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25964 * name to the request.
25969 * Load data from the configured URL, read the data object into
25970 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25971 * process that block using the passed callback.
25972 * @param {Object} params An object containing properties which are to be used as HTTP parameters
25973 * for the request to the remote server.
25974 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25975 * object into a block of Roo.data.Records.
25976 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25977 * The function must be passed <ul>
25978 * <li>The Record block object</li>
25979 * <li>The "arg" argument from the load function</li>
25980 * <li>A boolean success indicator</li>
25982 * @param {Object} scope The scope in which to call the callback
25983 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25985 load : function(params, reader, callback, scope, arg){
25986 if(this.fireEvent("beforeload", this, params) !== false){
25988 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25990 var url = this.url;
25991 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25993 url += "&_dc=" + (new Date().getTime());
25995 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25998 cb : "stcCallback"+transId,
25999 scriptId : "stcScript"+transId,
26003 callback : callback,
26009 window[trans.cb] = function(o){
26010 conn.handleResponse(o, trans);
26013 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26015 if(this.autoAbort !== false){
26019 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26021 var script = document.createElement("script");
26022 script.setAttribute("src", url);
26023 script.setAttribute("type", "text/javascript");
26024 script.setAttribute("id", trans.scriptId);
26025 this.head.appendChild(script);
26027 this.trans = trans;
26029 callback.call(scope||this, null, arg, false);
26034 isLoading : function(){
26035 return this.trans ? true : false;
26039 * Abort the current server request.
26041 abort : function(){
26042 if(this.isLoading()){
26043 this.destroyTrans(this.trans);
26048 destroyTrans : function(trans, isLoaded){
26049 this.head.removeChild(document.getElementById(trans.scriptId));
26050 clearTimeout(trans.timeoutId);
26052 window[trans.cb] = undefined;
26054 delete window[trans.cb];
26057 // if hasn't been loaded, wait for load to remove it to prevent script error
26058 window[trans.cb] = function(){
26059 window[trans.cb] = undefined;
26061 delete window[trans.cb];
26068 handleResponse : function(o, trans){
26069 this.trans = false;
26070 this.destroyTrans(trans, true);
26073 result = trans.reader.readRecords(o);
26075 this.fireEvent("loadexception", this, o, trans.arg, e);
26076 trans.callback.call(trans.scope||window, null, trans.arg, false);
26079 this.fireEvent("load", this, o, trans.arg);
26080 trans.callback.call(trans.scope||window, result, trans.arg, true);
26084 handleFailure : function(trans){
26085 this.trans = false;
26086 this.destroyTrans(trans, false);
26087 this.fireEvent("loadexception", this, null, trans.arg);
26088 trans.callback.call(trans.scope||window, null, trans.arg, false);
26092 * Ext JS Library 1.1.1
26093 * Copyright(c) 2006-2007, Ext JS, LLC.
26095 * Originally Released Under LGPL - original licence link has changed is not relivant.
26098 * <script type="text/javascript">
26102 * @class Roo.data.JsonReader
26103 * @extends Roo.data.DataReader
26104 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26105 * based on mappings in a provided Roo.data.Record constructor.
26107 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26108 * in the reply previously.
26113 var RecordDef = Roo.data.Record.create([
26114 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26115 {name: 'occupation'} // This field will use "occupation" as the mapping.
26117 var myReader = new Roo.data.JsonReader({
26118 totalProperty: "results", // The property which contains the total dataset size (optional)
26119 root: "rows", // The property which contains an Array of row objects
26120 id: "id" // The property within each row object that provides an ID for the record (optional)
26124 * This would consume a JSON file like this:
26126 { 'results': 2, 'rows': [
26127 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26128 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26131 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26132 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26133 * paged from the remote server.
26134 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26135 * @cfg {String} root name of the property which contains the Array of row objects.
26136 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26137 * @cfg {Array} fields Array of field definition objects
26139 * Create a new JsonReader
26140 * @param {Object} meta Metadata configuration options
26141 * @param {Object} recordType Either an Array of field definition objects,
26142 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26144 Roo.data.JsonReader = function(meta, recordType){
26147 // set some defaults:
26148 Roo.applyIf(meta, {
26149 totalProperty: 'total',
26150 successProperty : 'success',
26155 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26157 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26159 readerType : 'Json',
26162 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
26163 * Used by Store query builder to append _requestMeta to params.
26166 metaFromRemote : false,
26168 * This method is only used by a DataProxy which has retrieved data from a remote server.
26169 * @param {Object} response The XHR object which contains the JSON data in its responseText.
26170 * @return {Object} data A data block which is used by an Roo.data.Store object as
26171 * a cache of Roo.data.Records.
26173 read : function(response){
26174 var json = response.responseText;
26176 var o = /* eval:var:o */ eval("("+json+")");
26178 throw {message: "JsonReader.read: Json object not found"};
26184 this.metaFromRemote = true;
26185 this.meta = o.metaData;
26186 this.recordType = Roo.data.Record.create(o.metaData.fields);
26187 this.onMetaChange(this.meta, this.recordType, o);
26189 return this.readRecords(o);
26192 // private function a store will implement
26193 onMetaChange : function(meta, recordType, o){
26200 simpleAccess: function(obj, subsc) {
26207 getJsonAccessor: function(){
26209 return function(expr) {
26211 return(re.test(expr))
26212 ? new Function("obj", "return obj." + expr)
26217 return Roo.emptyFn;
26222 * Create a data block containing Roo.data.Records from an XML document.
26223 * @param {Object} o An object which contains an Array of row objects in the property specified
26224 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26225 * which contains the total size of the dataset.
26226 * @return {Object} data A data block which is used by an Roo.data.Store object as
26227 * a cache of Roo.data.Records.
26229 readRecords : function(o){
26231 * After any data loads, the raw JSON data is available for further custom processing.
26235 var s = this.meta, Record = this.recordType,
26236 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26238 // Generate extraction functions for the totalProperty, the root, the id, and for each field
26240 if(s.totalProperty) {
26241 this.getTotal = this.getJsonAccessor(s.totalProperty);
26243 if(s.successProperty) {
26244 this.getSuccess = this.getJsonAccessor(s.successProperty);
26246 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26248 var g = this.getJsonAccessor(s.id);
26249 this.getId = function(rec) {
26251 return (r === undefined || r === "") ? null : r;
26254 this.getId = function(){return null;};
26257 for(var jj = 0; jj < fl; jj++){
26259 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26260 this.ef[jj] = this.getJsonAccessor(map);
26264 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26265 if(s.totalProperty){
26266 var vt = parseInt(this.getTotal(o), 10);
26271 if(s.successProperty){
26272 var vs = this.getSuccess(o);
26273 if(vs === false || vs === 'false'){
26278 for(var i = 0; i < c; i++){
26281 var id = this.getId(n);
26282 for(var j = 0; j < fl; j++){
26284 var v = this.ef[j](n);
26286 Roo.log('missing convert for ' + f.name);
26290 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26294 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26300 var record = new Record(values, id);
26302 records[i] = record;
26308 totalRecords : totalRecords
26311 // used when loading children.. @see loadDataFromChildren
26312 toLoadData: function(rec)
26314 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26315 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26316 return { data : data, total : data.length };
26321 * Ext JS Library 1.1.1
26322 * Copyright(c) 2006-2007, Ext JS, LLC.
26324 * Originally Released Under LGPL - original licence link has changed is not relivant.
26327 * <script type="text/javascript">
26331 * @class Roo.data.XmlReader
26332 * @extends Roo.data.DataReader
26333 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26334 * based on mappings in a provided Roo.data.Record constructor.<br><br>
26336 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26337 * header in the HTTP response must be set to "text/xml".</em>
26341 var RecordDef = Roo.data.Record.create([
26342 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26343 {name: 'occupation'} // This field will use "occupation" as the mapping.
26345 var myReader = new Roo.data.XmlReader({
26346 totalRecords: "results", // The element which contains the total dataset size (optional)
26347 record: "row", // The repeated element which contains row information
26348 id: "id" // The element within the row that provides an ID for the record (optional)
26352 * This would consume an XML file like this:
26356 <results>2</results>
26359 <name>Bill</name>
26360 <occupation>Gardener</occupation>
26364 <name>Ben</name>
26365 <occupation>Horticulturalist</occupation>
26369 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26370 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26371 * paged from the remote server.
26372 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26373 * @cfg {String} success The DomQuery path to the success attribute used by forms.
26374 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26375 * a record identifier value.
26377 * Create a new XmlReader
26378 * @param {Object} meta Metadata configuration options
26379 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
26380 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26381 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
26383 Roo.data.XmlReader = function(meta, recordType){
26385 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26387 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26389 readerType : 'Xml',
26392 * This method is only used by a DataProxy which has retrieved data from a remote server.
26393 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
26394 * to contain a method called 'responseXML' that returns an XML document object.
26395 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26396 * a cache of Roo.data.Records.
26398 read : function(response){
26399 var doc = response.responseXML;
26401 throw {message: "XmlReader.read: XML Document not available"};
26403 return this.readRecords(doc);
26407 * Create a data block containing Roo.data.Records from an XML document.
26408 * @param {Object} doc A parsed XML document.
26409 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26410 * a cache of Roo.data.Records.
26412 readRecords : function(doc){
26414 * After any data loads/reads, the raw XML Document is available for further custom processing.
26415 * @type XMLDocument
26417 this.xmlData = doc;
26418 var root = doc.documentElement || doc;
26419 var q = Roo.DomQuery;
26420 var recordType = this.recordType, fields = recordType.prototype.fields;
26421 var sid = this.meta.id;
26422 var totalRecords = 0, success = true;
26423 if(this.meta.totalRecords){
26424 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26427 if(this.meta.success){
26428 var sv = q.selectValue(this.meta.success, root, true);
26429 success = sv !== false && sv !== 'false';
26432 var ns = q.select(this.meta.record, root);
26433 for(var i = 0, len = ns.length; i < len; i++) {
26436 var id = sid ? q.selectValue(sid, n) : undefined;
26437 for(var j = 0, jlen = fields.length; j < jlen; j++){
26438 var f = fields.items[j];
26439 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26441 values[f.name] = v;
26443 var record = new recordType(values, id);
26445 records[records.length] = record;
26451 totalRecords : totalRecords || records.length
26456 * Ext JS Library 1.1.1
26457 * Copyright(c) 2006-2007, Ext JS, LLC.
26459 * Originally Released Under LGPL - original licence link has changed is not relivant.
26462 * <script type="text/javascript">
26466 * @class Roo.data.ArrayReader
26467 * @extends Roo.data.DataReader
26468 * Data reader class to create an Array of Roo.data.Record objects from an Array.
26469 * Each element of that Array represents a row of data fields. The
26470 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26471 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26475 var RecordDef = Roo.data.Record.create([
26476 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
26477 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
26479 var myReader = new Roo.data.ArrayReader({
26480 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
26484 * This would consume an Array like this:
26486 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26490 * Create a new JsonReader
26491 * @param {Object} meta Metadata configuration options.
26492 * @param {Object|Array} recordType Either an Array of field definition objects
26494 * @cfg {Array} fields Array of field definition objects
26495 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26496 * as specified to {@link Roo.data.Record#create},
26497 * or an {@link Roo.data.Record} object
26500 * created using {@link Roo.data.Record#create}.
26502 Roo.data.ArrayReader = function(meta, recordType)
26504 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26507 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26510 * Create a data block containing Roo.data.Records from an XML document.
26511 * @param {Object} o An Array of row objects which represents the dataset.
26512 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26513 * a cache of Roo.data.Records.
26515 readRecords : function(o)
26517 var sid = this.meta ? this.meta.id : null;
26518 var recordType = this.recordType, fields = recordType.prototype.fields;
26521 for(var i = 0; i < root.length; i++){
26524 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26525 for(var j = 0, jlen = fields.length; j < jlen; j++){
26526 var f = fields.items[j];
26527 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26528 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26530 values[f.name] = v;
26532 var record = new recordType(values, id);
26534 records[records.length] = record;
26538 totalRecords : records.length
26541 // used when loading children.. @see loadDataFromChildren
26542 toLoadData: function(rec)
26544 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26545 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26552 * Ext JS Library 1.1.1
26553 * Copyright(c) 2006-2007, Ext JS, LLC.
26555 * Originally Released Under LGPL - original licence link has changed is not relivant.
26558 * <script type="text/javascript">
26563 * @class Roo.data.Tree
26564 * @extends Roo.util.Observable
26565 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26566 * in the tree have most standard DOM functionality.
26568 * @param {Node} root (optional) The root node
26570 Roo.data.Tree = function(root){
26571 this.nodeHash = {};
26573 * The root node for this tree
26578 this.setRootNode(root);
26583 * Fires when a new child node is appended to a node in this tree.
26584 * @param {Tree} tree The owner tree
26585 * @param {Node} parent The parent node
26586 * @param {Node} node The newly appended node
26587 * @param {Number} index The index of the newly appended node
26592 * Fires when a child node is removed from a node in this tree.
26593 * @param {Tree} tree The owner tree
26594 * @param {Node} parent The parent node
26595 * @param {Node} node The child node removed
26600 * Fires when a node is moved to a new location in the tree
26601 * @param {Tree} tree The owner tree
26602 * @param {Node} node The node moved
26603 * @param {Node} oldParent The old parent of this node
26604 * @param {Node} newParent The new parent of this node
26605 * @param {Number} index The index it was moved to
26610 * Fires when a new child node is inserted in a node in this tree.
26611 * @param {Tree} tree The owner tree
26612 * @param {Node} parent The parent node
26613 * @param {Node} node The child node inserted
26614 * @param {Node} refNode The child node the node was inserted before
26618 * @event beforeappend
26619 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26620 * @param {Tree} tree The owner tree
26621 * @param {Node} parent The parent node
26622 * @param {Node} node The child node to be appended
26624 "beforeappend" : true,
26626 * @event beforeremove
26627 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26628 * @param {Tree} tree The owner tree
26629 * @param {Node} parent The parent node
26630 * @param {Node} node The child node to be removed
26632 "beforeremove" : true,
26634 * @event beforemove
26635 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26636 * @param {Tree} tree The owner tree
26637 * @param {Node} node The node being moved
26638 * @param {Node} oldParent The parent of the node
26639 * @param {Node} newParent The new parent the node is moving to
26640 * @param {Number} index The index it is being moved to
26642 "beforemove" : true,
26644 * @event beforeinsert
26645 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26646 * @param {Tree} tree The owner tree
26647 * @param {Node} parent The parent node
26648 * @param {Node} node The child node to be inserted
26649 * @param {Node} refNode The child node the node is being inserted before
26651 "beforeinsert" : true
26654 Roo.data.Tree.superclass.constructor.call(this);
26657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26658 pathSeparator: "/",
26660 proxyNodeEvent : function(){
26661 return this.fireEvent.apply(this, arguments);
26665 * Returns the root node for this tree.
26668 getRootNode : function(){
26673 * Sets the root node for this tree.
26674 * @param {Node} node
26677 setRootNode : function(node){
26679 node.ownerTree = this;
26680 node.isRoot = true;
26681 this.registerNode(node);
26686 * Gets a node in this tree by its id.
26687 * @param {String} id
26690 getNodeById : function(id){
26691 return this.nodeHash[id];
26694 registerNode : function(node){
26695 this.nodeHash[node.id] = node;
26698 unregisterNode : function(node){
26699 delete this.nodeHash[node.id];
26702 toString : function(){
26703 return "[Tree"+(this.id?" "+this.id:"")+"]";
26708 * @class Roo.data.Node
26709 * @extends Roo.util.Observable
26710 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26711 * @cfg {String} id The id for this node. If one is not specified, one is generated.
26713 * @param {Object} attributes The attributes/config for the node
26715 Roo.data.Node = function(attributes){
26717 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26720 this.attributes = attributes || {};
26721 this.leaf = this.attributes.leaf;
26723 * The node id. @type String
26725 this.id = this.attributes.id;
26727 this.id = Roo.id(null, "ynode-");
26728 this.attributes.id = this.id;
26733 * All child nodes of this node. @type Array
26735 this.childNodes = [];
26736 if(!this.childNodes.indexOf){ // indexOf is a must
26737 this.childNodes.indexOf = function(o){
26738 for(var i = 0, len = this.length; i < len; i++){
26747 * The parent node for this node. @type Node
26749 this.parentNode = null;
26751 * The first direct child node of this node, or null if this node has no child nodes. @type Node
26753 this.firstChild = null;
26755 * The last direct child node of this node, or null if this node has no child nodes. @type Node
26757 this.lastChild = null;
26759 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26761 this.previousSibling = null;
26763 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26765 this.nextSibling = null;
26770 * Fires when a new child node is appended
26771 * @param {Tree} tree The owner tree
26772 * @param {Node} this This node
26773 * @param {Node} node The newly appended node
26774 * @param {Number} index The index of the newly appended node
26779 * Fires when a child node is removed
26780 * @param {Tree} tree The owner tree
26781 * @param {Node} this This node
26782 * @param {Node} node The removed node
26787 * Fires when this node is moved to a new location in the tree
26788 * @param {Tree} tree The owner tree
26789 * @param {Node} this This node
26790 * @param {Node} oldParent The old parent of this node
26791 * @param {Node} newParent The new parent of this node
26792 * @param {Number} index The index it was moved to
26797 * Fires when a new child node is inserted.
26798 * @param {Tree} tree The owner tree
26799 * @param {Node} this This node
26800 * @param {Node} node The child node inserted
26801 * @param {Node} refNode The child node the node was inserted before
26805 * @event beforeappend
26806 * Fires before a new child is appended, return false to cancel the append.
26807 * @param {Tree} tree The owner tree
26808 * @param {Node} this This node
26809 * @param {Node} node The child node to be appended
26811 "beforeappend" : true,
26813 * @event beforeremove
26814 * Fires before a child is removed, return false to cancel the remove.
26815 * @param {Tree} tree The owner tree
26816 * @param {Node} this This node
26817 * @param {Node} node The child node to be removed
26819 "beforeremove" : true,
26821 * @event beforemove
26822 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26823 * @param {Tree} tree The owner tree
26824 * @param {Node} this This node
26825 * @param {Node} oldParent The parent of this node
26826 * @param {Node} newParent The new parent this node is moving to
26827 * @param {Number} index The index it is being moved to
26829 "beforemove" : true,
26831 * @event beforeinsert
26832 * Fires before a new child is inserted, return false to cancel the insert.
26833 * @param {Tree} tree The owner tree
26834 * @param {Node} this This node
26835 * @param {Node} node The child node to be inserted
26836 * @param {Node} refNode The child node the node is being inserted before
26838 "beforeinsert" : true
26840 this.listeners = this.attributes.listeners;
26841 Roo.data.Node.superclass.constructor.call(this);
26844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26845 fireEvent : function(evtName){
26846 // first do standard event for this node
26847 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26850 // then bubble it up to the tree if the event wasn't cancelled
26851 var ot = this.getOwnerTree();
26853 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26861 * Returns true if this node is a leaf
26862 * @return {Boolean}
26864 isLeaf : function(){
26865 return this.leaf === true;
26869 setFirstChild : function(node){
26870 this.firstChild = node;
26874 setLastChild : function(node){
26875 this.lastChild = node;
26880 * Returns true if this node is the last child of its parent
26881 * @return {Boolean}
26883 isLast : function(){
26884 return (!this.parentNode ? true : this.parentNode.lastChild == this);
26888 * Returns true if this node is the first child of its parent
26889 * @return {Boolean}
26891 isFirst : function(){
26892 return (!this.parentNode ? true : this.parentNode.firstChild == this);
26895 hasChildNodes : function(){
26896 return !this.isLeaf() && this.childNodes.length > 0;
26900 * Insert node(s) as the last child node of this node.
26901 * @param {Node/Array} node The node or Array of nodes to append
26902 * @return {Node} The appended node if single append, or null if an array was passed
26904 appendChild : function(node){
26906 if(node instanceof Array){
26908 }else if(arguments.length > 1){
26912 // if passed an array or multiple args do them one by one
26914 for(var i = 0, len = multi.length; i < len; i++) {
26915 this.appendChild(multi[i]);
26918 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26921 var index = this.childNodes.length;
26922 var oldParent = node.parentNode;
26923 // it's a move, make sure we move it cleanly
26925 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26928 oldParent.removeChild(node);
26931 index = this.childNodes.length;
26933 this.setFirstChild(node);
26935 this.childNodes.push(node);
26936 node.parentNode = this;
26937 var ps = this.childNodes[index-1];
26939 node.previousSibling = ps;
26940 ps.nextSibling = node;
26942 node.previousSibling = null;
26944 node.nextSibling = null;
26945 this.setLastChild(node);
26946 node.setOwnerTree(this.getOwnerTree());
26947 this.fireEvent("append", this.ownerTree, this, node, index);
26948 if(this.ownerTree) {
26949 this.ownerTree.fireEvent("appendnode", this, node, index);
26952 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26959 * Removes a child node from this node.
26960 * @param {Node} node The node to remove
26961 * @return {Node} The removed node
26963 removeChild : function(node){
26964 var index = this.childNodes.indexOf(node);
26968 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26972 // remove it from childNodes collection
26973 this.childNodes.splice(index, 1);
26976 if(node.previousSibling){
26977 node.previousSibling.nextSibling = node.nextSibling;
26979 if(node.nextSibling){
26980 node.nextSibling.previousSibling = node.previousSibling;
26983 // update child refs
26984 if(this.firstChild == node){
26985 this.setFirstChild(node.nextSibling);
26987 if(this.lastChild == node){
26988 this.setLastChild(node.previousSibling);
26991 node.setOwnerTree(null);
26992 // clear any references from the node
26993 node.parentNode = null;
26994 node.previousSibling = null;
26995 node.nextSibling = null;
26996 this.fireEvent("remove", this.ownerTree, this, node);
27001 * Inserts the first node before the second node in this nodes childNodes collection.
27002 * @param {Node} node The node to insert
27003 * @param {Node} refNode The node to insert before (if null the node is appended)
27004 * @return {Node} The inserted node
27006 insertBefore : function(node, refNode){
27007 if(!refNode){ // like standard Dom, refNode can be null for append
27008 return this.appendChild(node);
27011 if(node == refNode){
27015 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27018 var index = this.childNodes.indexOf(refNode);
27019 var oldParent = node.parentNode;
27020 var refIndex = index;
27022 // when moving internally, indexes will change after remove
27023 if(oldParent == this && this.childNodes.indexOf(node) < index){
27027 // it's a move, make sure we move it cleanly
27029 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27032 oldParent.removeChild(node);
27035 this.setFirstChild(node);
27037 this.childNodes.splice(refIndex, 0, node);
27038 node.parentNode = this;
27039 var ps = this.childNodes[refIndex-1];
27041 node.previousSibling = ps;
27042 ps.nextSibling = node;
27044 node.previousSibling = null;
27046 node.nextSibling = refNode;
27047 refNode.previousSibling = node;
27048 node.setOwnerTree(this.getOwnerTree());
27049 this.fireEvent("insert", this.ownerTree, this, node, refNode);
27051 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27057 * Returns the child node at the specified index.
27058 * @param {Number} index
27061 item : function(index){
27062 return this.childNodes[index];
27066 * Replaces one child node in this node with another.
27067 * @param {Node} newChild The replacement node
27068 * @param {Node} oldChild The node to replace
27069 * @return {Node} The replaced node
27071 replaceChild : function(newChild, oldChild){
27072 this.insertBefore(newChild, oldChild);
27073 this.removeChild(oldChild);
27078 * Returns the index of a child node
27079 * @param {Node} node
27080 * @return {Number} The index of the node or -1 if it was not found
27082 indexOf : function(child){
27083 return this.childNodes.indexOf(child);
27087 * Returns the tree this node is in.
27090 getOwnerTree : function(){
27091 // if it doesn't have one, look for one
27092 if(!this.ownerTree){
27096 this.ownerTree = p.ownerTree;
27102 return this.ownerTree;
27106 * Returns depth of this node (the root node has a depth of 0)
27109 getDepth : function(){
27112 while(p.parentNode){
27120 setOwnerTree : function(tree){
27121 // if it's move, we need to update everyone
27122 if(tree != this.ownerTree){
27123 if(this.ownerTree){
27124 this.ownerTree.unregisterNode(this);
27126 this.ownerTree = tree;
27127 var cs = this.childNodes;
27128 for(var i = 0, len = cs.length; i < len; i++) {
27129 cs[i].setOwnerTree(tree);
27132 tree.registerNode(this);
27138 * Returns the path for this node. The path can be used to expand or select this node programmatically.
27139 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27140 * @return {String} The path
27142 getPath : function(attr){
27143 attr = attr || "id";
27144 var p = this.parentNode;
27145 var b = [this.attributes[attr]];
27147 b.unshift(p.attributes[attr]);
27150 var sep = this.getOwnerTree().pathSeparator;
27151 return sep + b.join(sep);
27155 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27156 * function call will be the scope provided or the current node. The arguments to the function
27157 * will be the args provided or the current node. If the function returns false at any point,
27158 * the bubble is stopped.
27159 * @param {Function} fn The function to call
27160 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27161 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27163 bubble : function(fn, scope, args){
27166 if(fn.call(scope || p, args || p) === false){
27174 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27175 * function call will be the scope provided or the current node. The arguments to the function
27176 * will be the args provided or the current node. If the function returns false at any point,
27177 * the cascade is stopped on that branch.
27178 * @param {Function} fn The function to call
27179 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27180 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27182 cascade : function(fn, scope, args){
27183 if(fn.call(scope || this, args || this) !== false){
27184 var cs = this.childNodes;
27185 for(var i = 0, len = cs.length; i < len; i++) {
27186 cs[i].cascade(fn, scope, args);
27192 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27193 * function call will be the scope provided or the current node. The arguments to the function
27194 * will be the args provided or the current node. If the function returns false at any point,
27195 * the iteration stops.
27196 * @param {Function} fn The function to call
27197 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27198 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27200 eachChild : function(fn, scope, args){
27201 var cs = this.childNodes;
27202 for(var i = 0, len = cs.length; i < len; i++) {
27203 if(fn.call(scope || this, args || cs[i]) === false){
27210 * Finds the first child that has the attribute with the specified value.
27211 * @param {String} attribute The attribute name
27212 * @param {Mixed} value The value to search for
27213 * @return {Node} The found child or null if none was found
27215 findChild : function(attribute, value){
27216 var cs = this.childNodes;
27217 for(var i = 0, len = cs.length; i < len; i++) {
27218 if(cs[i].attributes[attribute] == value){
27226 * Finds the first child by a custom function. The child matches if the function passed
27228 * @param {Function} fn
27229 * @param {Object} scope (optional)
27230 * @return {Node} The found child or null if none was found
27232 findChildBy : function(fn, scope){
27233 var cs = this.childNodes;
27234 for(var i = 0, len = cs.length; i < len; i++) {
27235 if(fn.call(scope||cs[i], cs[i]) === true){
27243 * Sorts this nodes children using the supplied sort function
27244 * @param {Function} fn
27245 * @param {Object} scope (optional)
27247 sort : function(fn, scope){
27248 var cs = this.childNodes;
27249 var len = cs.length;
27251 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27253 for(var i = 0; i < len; i++){
27255 n.previousSibling = cs[i-1];
27256 n.nextSibling = cs[i+1];
27258 this.setFirstChild(n);
27261 this.setLastChild(n);
27268 * Returns true if this node is an ancestor (at any point) of the passed node.
27269 * @param {Node} node
27270 * @return {Boolean}
27272 contains : function(node){
27273 return node.isAncestor(this);
27277 * Returns true if the passed node is an ancestor (at any point) of this node.
27278 * @param {Node} node
27279 * @return {Boolean}
27281 isAncestor : function(node){
27282 var p = this.parentNode;
27292 toString : function(){
27293 return "[Node"+(this.id?" "+this.id:"")+"]";
27297 * Ext JS Library 1.1.1
27298 * Copyright(c) 2006-2007, Ext JS, LLC.
27300 * Originally Released Under LGPL - original licence link has changed is not relivant.
27303 * <script type="text/javascript">
27308 * @class Roo.Shadow
27309 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
27310 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
27311 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27313 * Create a new Shadow
27314 * @param {Object} config The config object
27316 Roo.Shadow = function(config){
27317 Roo.apply(this, config);
27318 if(typeof this.mode != "string"){
27319 this.mode = this.defaultMode;
27321 var o = this.offset, a = {h: 0};
27322 var rad = Math.floor(this.offset/2);
27323 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27329 a.l -= this.offset + rad;
27330 a.t -= this.offset + rad;
27341 a.l -= (this.offset - rad);
27342 a.t -= this.offset + rad;
27344 a.w -= (this.offset - rad)*2;
27355 a.l -= (this.offset - rad);
27356 a.t -= (this.offset - rad);
27358 a.w -= (this.offset + rad + 1);
27359 a.h -= (this.offset + rad);
27368 Roo.Shadow.prototype = {
27370 * @cfg {String} mode
27371 * The shadow display mode. Supports the following options:<br />
27372 * sides: Shadow displays on both sides and bottom only<br />
27373 * frame: Shadow displays equally on all four sides<br />
27374 * drop: Traditional bottom-right drop shadow (default)
27378 * @cfg {String} offset
27379 * The number of pixels to offset the shadow from the element (defaults to 4)
27384 defaultMode: "drop",
27387 * Displays the shadow under the target element
27388 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27390 show : function(target){
27391 target = Roo.get(target);
27393 this.el = Roo.Shadow.Pool.pull();
27394 if(this.el.dom.nextSibling != target.dom){
27395 this.el.insertBefore(target);
27398 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27400 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27403 target.getLeft(true),
27404 target.getTop(true),
27408 this.el.dom.style.display = "block";
27412 * Returns true if the shadow is visible, else false
27414 isVisible : function(){
27415 return this.el ? true : false;
27419 * Direct alignment when values are already available. Show must be called at least once before
27420 * calling this method to ensure it is initialized.
27421 * @param {Number} left The target element left position
27422 * @param {Number} top The target element top position
27423 * @param {Number} width The target element width
27424 * @param {Number} height The target element height
27426 realign : function(l, t, w, h){
27430 var a = this.adjusts, d = this.el.dom, s = d.style;
27432 s.left = (l+a.l)+"px";
27433 s.top = (t+a.t)+"px";
27434 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27436 if(s.width != sws || s.height != shs){
27440 var cn = d.childNodes;
27441 var sww = Math.max(0, (sw-12))+"px";
27442 cn[0].childNodes[1].style.width = sww;
27443 cn[1].childNodes[1].style.width = sww;
27444 cn[2].childNodes[1].style.width = sww;
27445 cn[1].style.height = Math.max(0, (sh-12))+"px";
27451 * Hides this shadow
27455 this.el.dom.style.display = "none";
27456 Roo.Shadow.Pool.push(this.el);
27462 * Adjust the z-index of this shadow
27463 * @param {Number} zindex The new z-index
27465 setZIndex : function(z){
27468 this.el.setStyle("z-index", z);
27473 // Private utility class that manages the internal Shadow cache
27474 Roo.Shadow.Pool = function(){
27476 var markup = Roo.isIE ?
27477 '<div class="x-ie-shadow"></div>' :
27478 '<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>';
27481 var sh = p.shift();
27483 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27484 sh.autoBoxAdjust = false;
27489 push : function(sh){
27495 * Ext JS Library 1.1.1
27496 * Copyright(c) 2006-2007, Ext JS, LLC.
27498 * Originally Released Under LGPL - original licence link has changed is not relivant.
27501 * <script type="text/javascript">
27506 * @class Roo.SplitBar
27507 * @extends Roo.util.Observable
27508 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27512 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27513 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27514 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27515 split.minSize = 100;
27516 split.maxSize = 600;
27517 split.animate = true;
27518 split.on('moved', splitterMoved);
27521 * Create a new SplitBar
27522 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
27523 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
27524 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27525 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
27526 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27527 position of the SplitBar).
27529 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27532 this.el = Roo.get(dragElement, true);
27533 this.el.dom.unselectable = "on";
27535 this.resizingEl = Roo.get(resizingElement, true);
27539 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27540 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27543 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27546 * The minimum size of the resizing element. (Defaults to 0)
27552 * The maximum size of the resizing element. (Defaults to 2000)
27555 this.maxSize = 2000;
27558 * Whether to animate the transition to the new size
27561 this.animate = false;
27564 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27567 this.useShim = false;
27572 if(!existingProxy){
27574 this.proxy = Roo.SplitBar.createProxy(this.orientation);
27576 this.proxy = Roo.get(existingProxy).dom;
27579 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27582 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27585 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27588 this.dragSpecs = {};
27591 * @private The adapter to use to positon and resize elements
27593 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27594 this.adapter.init(this);
27596 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27598 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27599 this.el.addClass("x-splitbar-h");
27602 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27603 this.el.addClass("x-splitbar-v");
27609 * Fires when the splitter is moved (alias for {@link #event-moved})
27610 * @param {Roo.SplitBar} this
27611 * @param {Number} newSize the new width or height
27616 * Fires when the splitter is moved
27617 * @param {Roo.SplitBar} this
27618 * @param {Number} newSize the new width or height
27622 * @event beforeresize
27623 * Fires before the splitter is dragged
27624 * @param {Roo.SplitBar} this
27626 "beforeresize" : true,
27628 "beforeapply" : true
27631 Roo.util.Observable.call(this);
27634 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27635 onStartProxyDrag : function(x, y){
27636 this.fireEvent("beforeresize", this);
27638 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
27640 o.enableDisplayMode("block");
27641 // all splitbars share the same overlay
27642 Roo.SplitBar.prototype.overlay = o;
27644 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27645 this.overlay.show();
27646 Roo.get(this.proxy).setDisplayed("block");
27647 var size = this.adapter.getElementSize(this);
27648 this.activeMinSize = this.getMinimumSize();;
27649 this.activeMaxSize = this.getMaximumSize();;
27650 var c1 = size - this.activeMinSize;
27651 var c2 = Math.max(this.activeMaxSize - size, 0);
27652 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27653 this.dd.resetConstraints();
27654 this.dd.setXConstraint(
27655 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
27656 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27658 this.dd.setYConstraint(0, 0);
27660 this.dd.resetConstraints();
27661 this.dd.setXConstraint(0, 0);
27662 this.dd.setYConstraint(
27663 this.placement == Roo.SplitBar.TOP ? c1 : c2,
27664 this.placement == Roo.SplitBar.TOP ? c2 : c1
27667 this.dragSpecs.startSize = size;
27668 this.dragSpecs.startPoint = [x, y];
27669 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27673 * @private Called after the drag operation by the DDProxy
27675 onEndProxyDrag : function(e){
27676 Roo.get(this.proxy).setDisplayed(false);
27677 var endPoint = Roo.lib.Event.getXY(e);
27679 this.overlay.hide();
27682 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27683 newSize = this.dragSpecs.startSize +
27684 (this.placement == Roo.SplitBar.LEFT ?
27685 endPoint[0] - this.dragSpecs.startPoint[0] :
27686 this.dragSpecs.startPoint[0] - endPoint[0]
27689 newSize = this.dragSpecs.startSize +
27690 (this.placement == Roo.SplitBar.TOP ?
27691 endPoint[1] - this.dragSpecs.startPoint[1] :
27692 this.dragSpecs.startPoint[1] - endPoint[1]
27695 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27696 if(newSize != this.dragSpecs.startSize){
27697 if(this.fireEvent('beforeapply', this, newSize) !== false){
27698 this.adapter.setElementSize(this, newSize);
27699 this.fireEvent("moved", this, newSize);
27700 this.fireEvent("resize", this, newSize);
27706 * Get the adapter this SplitBar uses
27707 * @return The adapter object
27709 getAdapter : function(){
27710 return this.adapter;
27714 * Set the adapter this SplitBar uses
27715 * @param {Object} adapter A SplitBar adapter object
27717 setAdapter : function(adapter){
27718 this.adapter = adapter;
27719 this.adapter.init(this);
27723 * Gets the minimum size for the resizing element
27724 * @return {Number} The minimum size
27726 getMinimumSize : function(){
27727 return this.minSize;
27731 * Sets the minimum size for the resizing element
27732 * @param {Number} minSize The minimum size
27734 setMinimumSize : function(minSize){
27735 this.minSize = minSize;
27739 * Gets the maximum size for the resizing element
27740 * @return {Number} The maximum size
27742 getMaximumSize : function(){
27743 return this.maxSize;
27747 * Sets the maximum size for the resizing element
27748 * @param {Number} maxSize The maximum size
27750 setMaximumSize : function(maxSize){
27751 this.maxSize = maxSize;
27755 * Sets the initialize size for the resizing element
27756 * @param {Number} size The initial size
27758 setCurrentSize : function(size){
27759 var oldAnimate = this.animate;
27760 this.animate = false;
27761 this.adapter.setElementSize(this, size);
27762 this.animate = oldAnimate;
27766 * Destroy this splitbar.
27767 * @param {Boolean} removeEl True to remove the element
27769 destroy : function(removeEl){
27771 this.shim.remove();
27774 this.proxy.parentNode.removeChild(this.proxy);
27782 * @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.
27784 Roo.SplitBar.createProxy = function(dir){
27785 var proxy = new Roo.Element(document.createElement("div"));
27786 proxy.unselectable();
27787 var cls = 'x-splitbar-proxy';
27788 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27789 document.body.appendChild(proxy.dom);
27794 * @class Roo.SplitBar.BasicLayoutAdapter
27795 * Default Adapter. It assumes the splitter and resizing element are not positioned
27796 * elements and only gets/sets the width of the element. Generally used for table based layouts.
27798 Roo.SplitBar.BasicLayoutAdapter = function(){
27801 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27802 // do nothing for now
27803 init : function(s){
27807 * Called before drag operations to get the current size of the resizing element.
27808 * @param {Roo.SplitBar} s The SplitBar using this adapter
27810 getElementSize : function(s){
27811 if(s.orientation == Roo.SplitBar.HORIZONTAL){
27812 return s.resizingEl.getWidth();
27814 return s.resizingEl.getHeight();
27819 * Called after drag operations to set the size of the resizing element.
27820 * @param {Roo.SplitBar} s The SplitBar using this adapter
27821 * @param {Number} newSize The new size to set
27822 * @param {Function} onComplete A function to be invoked when resizing is complete
27824 setElementSize : function(s, newSize, onComplete){
27825 if(s.orientation == Roo.SplitBar.HORIZONTAL){
27827 s.resizingEl.setWidth(newSize);
27829 onComplete(s, newSize);
27832 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27837 s.resizingEl.setHeight(newSize);
27839 onComplete(s, newSize);
27842 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27849 *@class Roo.SplitBar.AbsoluteLayoutAdapter
27850 * @extends Roo.SplitBar.BasicLayoutAdapter
27851 * Adapter that moves the splitter element to align with the resized sizing element.
27852 * Used with an absolute positioned SplitBar.
27853 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27854 * document.body, make sure you assign an id to the body element.
27856 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27857 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27858 this.container = Roo.get(container);
27861 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27862 init : function(s){
27863 this.basic.init(s);
27866 getElementSize : function(s){
27867 return this.basic.getElementSize(s);
27870 setElementSize : function(s, newSize, onComplete){
27871 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27874 moveSplitter : function(s){
27875 var yes = Roo.SplitBar;
27876 switch(s.placement){
27878 s.el.setX(s.resizingEl.getRight());
27881 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27884 s.el.setY(s.resizingEl.getBottom());
27887 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27894 * Orientation constant - Create a vertical SplitBar
27898 Roo.SplitBar.VERTICAL = 1;
27901 * Orientation constant - Create a horizontal SplitBar
27905 Roo.SplitBar.HORIZONTAL = 2;
27908 * Placement constant - The resizing element is to the left of the splitter element
27912 Roo.SplitBar.LEFT = 1;
27915 * Placement constant - The resizing element is to the right of the splitter element
27919 Roo.SplitBar.RIGHT = 2;
27922 * Placement constant - The resizing element is positioned above the splitter element
27926 Roo.SplitBar.TOP = 3;
27929 * Placement constant - The resizing element is positioned under splitter element
27933 Roo.SplitBar.BOTTOM = 4;
27936 * Ext JS Library 1.1.1
27937 * Copyright(c) 2006-2007, Ext JS, LLC.
27939 * Originally Released Under LGPL - original licence link has changed is not relivant.
27942 * <script type="text/javascript">
27947 * @extends Roo.util.Observable
27948 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
27949 * This class also supports single and multi selection modes. <br>
27950 * Create a data model bound view:
27952 var store = new Roo.data.Store(...);
27954 var view = new Roo.View({
27956 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
27958 singleSelect: true,
27959 selectedClass: "ydataview-selected",
27963 // listen for node click?
27964 view.on("click", function(vw, index, node, e){
27965 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27969 dataModel.load("foobar.xml");
27971 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27973 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27974 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27976 * Note: old style constructor is still suported (container, template, config)
27979 * Create a new View
27980 * @param {Object} config The config object
27983 Roo.View = function(config, depreciated_tpl, depreciated_config){
27985 this.parent = false;
27987 if (typeof(depreciated_tpl) == 'undefined') {
27988 // new way.. - universal constructor.
27989 Roo.apply(this, config);
27990 this.el = Roo.get(this.el);
27993 this.el = Roo.get(config);
27994 this.tpl = depreciated_tpl;
27995 Roo.apply(this, depreciated_config);
27997 this.wrapEl = this.el.wrap().wrap();
27998 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28001 if(typeof(this.tpl) == "string"){
28002 this.tpl = new Roo.Template(this.tpl);
28004 // support xtype ctors..
28005 this.tpl = new Roo.factory(this.tpl, Roo);
28009 this.tpl.compile();
28014 * @event beforeclick
28015 * Fires before a click is processed. Returns false to cancel the default action.
28016 * @param {Roo.View} this
28017 * @param {Number} index The index of the target node
28018 * @param {HTMLElement} node The target node
28019 * @param {Roo.EventObject} e The raw event object
28021 "beforeclick" : true,
28024 * Fires when a template node is clicked.
28025 * @param {Roo.View} this
28026 * @param {Number} index The index of the target node
28027 * @param {HTMLElement} node The target node
28028 * @param {Roo.EventObject} e The raw event object
28033 * Fires when a template node is double clicked.
28034 * @param {Roo.View} this
28035 * @param {Number} index The index of the target node
28036 * @param {HTMLElement} node The target node
28037 * @param {Roo.EventObject} e The raw event object
28041 * @event contextmenu
28042 * Fires when a template node is right clicked.
28043 * @param {Roo.View} this
28044 * @param {Number} index The index of the target node
28045 * @param {HTMLElement} node The target node
28046 * @param {Roo.EventObject} e The raw event object
28048 "contextmenu" : true,
28050 * @event selectionchange
28051 * Fires when the selected nodes change.
28052 * @param {Roo.View} this
28053 * @param {Array} selections Array of the selected nodes
28055 "selectionchange" : true,
28058 * @event beforeselect
28059 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28060 * @param {Roo.View} this
28061 * @param {HTMLElement} node The node to be selected
28062 * @param {Array} selections Array of currently selected nodes
28064 "beforeselect" : true,
28066 * @event preparedata
28067 * Fires on every row to render, to allow you to change the data.
28068 * @param {Roo.View} this
28069 * @param {Object} data to be rendered (change this)
28071 "preparedata" : true
28079 "click": this.onClick,
28080 "dblclick": this.onDblClick,
28081 "contextmenu": this.onContextMenu,
28085 this.selections = [];
28087 this.cmp = new Roo.CompositeElementLite([]);
28089 this.store = Roo.factory(this.store, Roo.data);
28090 this.setStore(this.store, true);
28093 if ( this.footer && this.footer.xtype) {
28095 var fctr = this.wrapEl.appendChild(document.createElement("div"));
28097 this.footer.dataSource = this.store;
28098 this.footer.container = fctr;
28099 this.footer = Roo.factory(this.footer, Roo);
28100 fctr.insertFirst(this.el);
28102 // this is a bit insane - as the paging toolbar seems to detach the el..
28103 // dom.parentNode.parentNode.parentNode
28104 // they get detached?
28108 Roo.View.superclass.constructor.call(this);
28113 Roo.extend(Roo.View, Roo.util.Observable, {
28116 * @cfg {Roo.data.Store} store Data store to load data from.
28121 * @cfg {String|Roo.Element} el The container element.
28126 * @cfg {String|Roo.Template} tpl The template used by this View
28130 * @cfg {String} dataName the named area of the template to use as the data area
28131 * Works with domtemplates roo-name="name"
28135 * @cfg {String} selectedClass The css class to add to selected nodes
28137 selectedClass : "x-view-selected",
28139 * @cfg {String} emptyText The empty text to show when nothing is loaded.
28144 * @cfg {String} text to display on mask (default Loading)
28148 * @cfg {Boolean} multiSelect Allow multiple selection
28150 multiSelect : false,
28152 * @cfg {Boolean} singleSelect Allow single selection
28154 singleSelect: false,
28157 * @cfg {Boolean} toggleSelect - selecting
28159 toggleSelect : false,
28162 * @cfg {Boolean} tickable - selecting
28167 * Returns the element this view is bound to.
28168 * @return {Roo.Element}
28170 getEl : function(){
28171 return this.wrapEl;
28177 * Refreshes the view. - called by datachanged on the store. - do not call directly.
28179 refresh : function(){
28180 //Roo.log('refresh');
28183 // if we are using something like 'domtemplate', then
28184 // the what gets used is:
28185 // t.applySubtemplate(NAME, data, wrapping data..)
28186 // the outer template then get' applied with
28187 // the store 'extra data'
28188 // and the body get's added to the
28189 // roo-name="data" node?
28190 // <span class='roo-tpl-{name}'></span> ?????
28194 this.clearSelections();
28195 this.el.update("");
28197 var records = this.store.getRange();
28198 if(records.length < 1) {
28200 // is this valid?? = should it render a template??
28202 this.el.update(this.emptyText);
28206 if (this.dataName) {
28207 this.el.update(t.apply(this.store.meta)); //????
28208 el = this.el.child('.roo-tpl-' + this.dataName);
28211 for(var i = 0, len = records.length; i < len; i++){
28212 var data = this.prepareData(records[i].data, i, records[i]);
28213 this.fireEvent("preparedata", this, data, i, records[i]);
28215 var d = Roo.apply({}, data);
28218 Roo.apply(d, {'roo-id' : Roo.id()});
28222 Roo.each(this.parent.item, function(item){
28223 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28226 Roo.apply(d, {'roo-data-checked' : 'checked'});
28230 html[html.length] = Roo.util.Format.trim(
28232 t.applySubtemplate(this.dataName, d, this.store.meta) :
28239 el.update(html.join(""));
28240 this.nodes = el.dom.childNodes;
28241 this.updateIndexes(0);
28246 * Function to override to reformat the data that is sent to
28247 * the template for each node.
28248 * DEPRICATED - use the preparedata event handler.
28249 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28250 * a JSON object for an UpdateManager bound view).
28252 prepareData : function(data, index, record)
28254 this.fireEvent("preparedata", this, data, index, record);
28258 onUpdate : function(ds, record){
28259 // Roo.log('on update');
28260 this.clearSelections();
28261 var index = this.store.indexOf(record);
28262 var n = this.nodes[index];
28263 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28264 n.parentNode.removeChild(n);
28265 this.updateIndexes(index, index);
28271 onAdd : function(ds, records, index)
28273 //Roo.log(['on Add', ds, records, index] );
28274 this.clearSelections();
28275 if(this.nodes.length == 0){
28279 var n = this.nodes[index];
28280 for(var i = 0, len = records.length; i < len; i++){
28281 var d = this.prepareData(records[i].data, i, records[i]);
28283 this.tpl.insertBefore(n, d);
28286 this.tpl.append(this.el, d);
28289 this.updateIndexes(index);
28292 onRemove : function(ds, record, index){
28293 // Roo.log('onRemove');
28294 this.clearSelections();
28295 var el = this.dataName ?
28296 this.el.child('.roo-tpl-' + this.dataName) :
28299 el.dom.removeChild(this.nodes[index]);
28300 this.updateIndexes(index);
28304 * Refresh an individual node.
28305 * @param {Number} index
28307 refreshNode : function(index){
28308 this.onUpdate(this.store, this.store.getAt(index));
28311 updateIndexes : function(startIndex, endIndex){
28312 var ns = this.nodes;
28313 startIndex = startIndex || 0;
28314 endIndex = endIndex || ns.length - 1;
28315 for(var i = startIndex; i <= endIndex; i++){
28316 ns[i].nodeIndex = i;
28321 * Changes the data store this view uses and refresh the view.
28322 * @param {Store} store
28324 setStore : function(store, initial){
28325 if(!initial && this.store){
28326 this.store.un("datachanged", this.refresh);
28327 this.store.un("add", this.onAdd);
28328 this.store.un("remove", this.onRemove);
28329 this.store.un("update", this.onUpdate);
28330 this.store.un("clear", this.refresh);
28331 this.store.un("beforeload", this.onBeforeLoad);
28332 this.store.un("load", this.onLoad);
28333 this.store.un("loadexception", this.onLoad);
28337 store.on("datachanged", this.refresh, this);
28338 store.on("add", this.onAdd, this);
28339 store.on("remove", this.onRemove, this);
28340 store.on("update", this.onUpdate, this);
28341 store.on("clear", this.refresh, this);
28342 store.on("beforeload", this.onBeforeLoad, this);
28343 store.on("load", this.onLoad, this);
28344 store.on("loadexception", this.onLoad, this);
28352 * onbeforeLoad - masks the loading area.
28355 onBeforeLoad : function(store,opts)
28357 //Roo.log('onBeforeLoad');
28359 this.el.update("");
28361 this.el.mask(this.mask ? this.mask : "Loading" );
28363 onLoad : function ()
28370 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28371 * @param {HTMLElement} node
28372 * @return {HTMLElement} The template node
28374 findItemFromChild : function(node){
28375 var el = this.dataName ?
28376 this.el.child('.roo-tpl-' + this.dataName,true) :
28379 if(!node || node.parentNode == el){
28382 var p = node.parentNode;
28383 while(p && p != el){
28384 if(p.parentNode == el){
28393 onClick : function(e){
28394 var item = this.findItemFromChild(e.getTarget());
28396 var index = this.indexOf(item);
28397 if(this.onItemClick(item, index, e) !== false){
28398 this.fireEvent("click", this, index, item, e);
28401 this.clearSelections();
28406 onContextMenu : function(e){
28407 var item = this.findItemFromChild(e.getTarget());
28409 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28414 onDblClick : function(e){
28415 var item = this.findItemFromChild(e.getTarget());
28417 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28421 onItemClick : function(item, index, e)
28423 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28426 if (this.toggleSelect) {
28427 var m = this.isSelected(item) ? 'unselect' : 'select';
28430 _t[m](item, true, false);
28433 if(this.multiSelect || this.singleSelect){
28434 if(this.multiSelect && e.shiftKey && this.lastSelection){
28435 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28437 this.select(item, this.multiSelect && e.ctrlKey);
28438 this.lastSelection = item;
28441 if(!this.tickable){
28442 e.preventDefault();
28450 * Get the number of selected nodes.
28453 getSelectionCount : function(){
28454 return this.selections.length;
28458 * Get the currently selected nodes.
28459 * @return {Array} An array of HTMLElements
28461 getSelectedNodes : function(){
28462 return this.selections;
28466 * Get the indexes of the selected nodes.
28469 getSelectedIndexes : function(){
28470 var indexes = [], s = this.selections;
28471 for(var i = 0, len = s.length; i < len; i++){
28472 indexes.push(s[i].nodeIndex);
28478 * Clear all selections
28479 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28481 clearSelections : function(suppressEvent){
28482 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28483 this.cmp.elements = this.selections;
28484 this.cmp.removeClass(this.selectedClass);
28485 this.selections = [];
28486 if(!suppressEvent){
28487 this.fireEvent("selectionchange", this, this.selections);
28493 * Returns true if the passed node is selected
28494 * @param {HTMLElement/Number} node The node or node index
28495 * @return {Boolean}
28497 isSelected : function(node){
28498 var s = this.selections;
28502 node = this.getNode(node);
28503 return s.indexOf(node) !== -1;
28508 * @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
28509 * @param {Boolean} keepExisting (optional) true to keep existing selections
28510 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28512 select : function(nodeInfo, keepExisting, suppressEvent){
28513 if(nodeInfo instanceof Array){
28515 this.clearSelections(true);
28517 for(var i = 0, len = nodeInfo.length; i < len; i++){
28518 this.select(nodeInfo[i], true, true);
28522 var node = this.getNode(nodeInfo);
28523 if(!node || this.isSelected(node)){
28524 return; // already selected.
28527 this.clearSelections(true);
28530 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28531 Roo.fly(node).addClass(this.selectedClass);
28532 this.selections.push(node);
28533 if(!suppressEvent){
28534 this.fireEvent("selectionchange", this, this.selections);
28542 * @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
28543 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28544 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28546 unselect : function(nodeInfo, keepExisting, suppressEvent)
28548 if(nodeInfo instanceof Array){
28549 Roo.each(this.selections, function(s) {
28550 this.unselect(s, nodeInfo);
28554 var node = this.getNode(nodeInfo);
28555 if(!node || !this.isSelected(node)){
28556 //Roo.log("not selected");
28557 return; // not selected.
28561 Roo.each(this.selections, function(s) {
28563 Roo.fly(node).removeClass(this.selectedClass);
28570 this.selections= ns;
28571 this.fireEvent("selectionchange", this, this.selections);
28575 * Gets a template node.
28576 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28577 * @return {HTMLElement} The node or null if it wasn't found
28579 getNode : function(nodeInfo){
28580 if(typeof nodeInfo == "string"){
28581 return document.getElementById(nodeInfo);
28582 }else if(typeof nodeInfo == "number"){
28583 return this.nodes[nodeInfo];
28589 * Gets a range template nodes.
28590 * @param {Number} startIndex
28591 * @param {Number} endIndex
28592 * @return {Array} An array of nodes
28594 getNodes : function(start, end){
28595 var ns = this.nodes;
28596 start = start || 0;
28597 end = typeof end == "undefined" ? ns.length - 1 : end;
28600 for(var i = start; i <= end; i++){
28604 for(var i = start; i >= end; i--){
28612 * Finds the index of the passed node
28613 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28614 * @return {Number} The index of the node or -1
28616 indexOf : function(node){
28617 node = this.getNode(node);
28618 if(typeof node.nodeIndex == "number"){
28619 return node.nodeIndex;
28621 var ns = this.nodes;
28622 for(var i = 0, len = ns.length; i < len; i++){
28632 * Ext JS Library 1.1.1
28633 * Copyright(c) 2006-2007, Ext JS, LLC.
28635 * Originally Released Under LGPL - original licence link has changed is not relivant.
28638 * <script type="text/javascript">
28642 * @class Roo.JsonView
28643 * @extends Roo.View
28644 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28646 var view = new Roo.JsonView({
28647 container: "my-element",
28648 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
28653 // listen for node click?
28654 view.on("click", function(vw, index, node, e){
28655 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28658 // direct load of JSON data
28659 view.load("foobar.php");
28661 // Example from my blog list
28662 var tpl = new Roo.Template(
28663 '<div class="entry">' +
28664 '<a class="entry-title" href="{link}">{title}</a>' +
28665 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
28666 "</div><hr />"
28669 var moreView = new Roo.JsonView({
28670 container : "entry-list",
28674 moreView.on("beforerender", this.sortEntries, this);
28676 url: "/blog/get-posts.php",
28677 params: "allposts=true",
28678 text: "Loading Blog Entries..."
28682 * Note: old code is supported with arguments : (container, template, config)
28686 * Create a new JsonView
28688 * @param {Object} config The config object
28691 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28694 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28696 var um = this.el.getUpdateManager();
28697 um.setRenderer(this);
28698 um.on("update", this.onLoad, this);
28699 um.on("failure", this.onLoadException, this);
28702 * @event beforerender
28703 * Fires before rendering of the downloaded JSON data.
28704 * @param {Roo.JsonView} this
28705 * @param {Object} data The JSON data loaded
28709 * Fires when data is loaded.
28710 * @param {Roo.JsonView} this
28711 * @param {Object} data The JSON data loaded
28712 * @param {Object} response The raw Connect response object
28715 * @event loadexception
28716 * Fires when loading fails.
28717 * @param {Roo.JsonView} this
28718 * @param {Object} response The raw Connect response object
28721 'beforerender' : true,
28723 'loadexception' : true
28726 Roo.extend(Roo.JsonView, Roo.View, {
28728 * @type {String} The root property in the loaded JSON object that contains the data
28733 * Refreshes the view.
28735 refresh : function(){
28736 this.clearSelections();
28737 this.el.update("");
28739 var o = this.jsonData;
28740 if(o && o.length > 0){
28741 for(var i = 0, len = o.length; i < len; i++){
28742 var data = this.prepareData(o[i], i, o);
28743 html[html.length] = this.tpl.apply(data);
28746 html.push(this.emptyText);
28748 this.el.update(html.join(""));
28749 this.nodes = this.el.dom.childNodes;
28750 this.updateIndexes(0);
28754 * 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.
28755 * @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:
28758 url: "your-url.php",
28759 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28760 callback: yourFunction,
28761 scope: yourObject, //(optional scope)
28764 text: "Loading...",
28769 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28770 * 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.
28771 * @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}
28772 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28773 * @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.
28776 var um = this.el.getUpdateManager();
28777 um.update.apply(um, arguments);
28780 // note - render is a standard framework call...
28781 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28782 render : function(el, response){
28784 this.clearSelections();
28785 this.el.update("");
28788 if (response != '') {
28789 o = Roo.util.JSON.decode(response.responseText);
28792 o = o[this.jsonRoot];
28798 * The current JSON data or null
28801 this.beforeRender();
28806 * Get the number of records in the current JSON dataset
28809 getCount : function(){
28810 return this.jsonData ? this.jsonData.length : 0;
28814 * Returns the JSON object for the specified node(s)
28815 * @param {HTMLElement/Array} node The node or an array of nodes
28816 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28817 * you get the JSON object for the node
28819 getNodeData : function(node){
28820 if(node instanceof Array){
28822 for(var i = 0, len = node.length; i < len; i++){
28823 data.push(this.getNodeData(node[i]));
28827 return this.jsonData[this.indexOf(node)] || null;
28830 beforeRender : function(){
28831 this.snapshot = this.jsonData;
28833 this.sort.apply(this, this.sortInfo);
28835 this.fireEvent("beforerender", this, this.jsonData);
28838 onLoad : function(el, o){
28839 this.fireEvent("load", this, this.jsonData, o);
28842 onLoadException : function(el, o){
28843 this.fireEvent("loadexception", this, o);
28847 * Filter the data by a specific property.
28848 * @param {String} property A property on your JSON objects
28849 * @param {String/RegExp} value Either string that the property values
28850 * should start with, or a RegExp to test against the property
28852 filter : function(property, value){
28855 var ss = this.snapshot;
28856 if(typeof value == "string"){
28857 var vlen = value.length;
28859 this.clearFilter();
28862 value = value.toLowerCase();
28863 for(var i = 0, len = ss.length; i < len; i++){
28865 if(o[property].substr(0, vlen).toLowerCase() == value){
28869 } else if(value.exec){ // regex?
28870 for(var i = 0, len = ss.length; i < len; i++){
28872 if(value.test(o[property])){
28879 this.jsonData = data;
28885 * Filter by a function. The passed function will be called with each
28886 * object in the current dataset. If the function returns true the value is kept,
28887 * otherwise it is filtered.
28888 * @param {Function} fn
28889 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28891 filterBy : function(fn, scope){
28894 var ss = this.snapshot;
28895 for(var i = 0, len = ss.length; i < len; i++){
28897 if(fn.call(scope || this, o)){
28901 this.jsonData = data;
28907 * Clears the current filter.
28909 clearFilter : function(){
28910 if(this.snapshot && this.jsonData != this.snapshot){
28911 this.jsonData = this.snapshot;
28918 * Sorts the data for this view and refreshes it.
28919 * @param {String} property A property on your JSON objects to sort on
28920 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28921 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28923 sort : function(property, dir, sortType){
28924 this.sortInfo = Array.prototype.slice.call(arguments, 0);
28927 var dsc = dir && dir.toLowerCase() == "desc";
28928 var f = function(o1, o2){
28929 var v1 = sortType ? sortType(o1[p]) : o1[p];
28930 var v2 = sortType ? sortType(o2[p]) : o2[p];
28933 return dsc ? +1 : -1;
28934 } else if(v1 > v2){
28935 return dsc ? -1 : +1;
28940 this.jsonData.sort(f);
28942 if(this.jsonData != this.snapshot){
28943 this.snapshot.sort(f);
28949 * Ext JS Library 1.1.1
28950 * Copyright(c) 2006-2007, Ext JS, LLC.
28952 * Originally Released Under LGPL - original licence link has changed is not relivant.
28955 * <script type="text/javascript">
28960 * @class Roo.ColorPalette
28961 * @extends Roo.Component
28962 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
28963 * Here's an example of typical usage:
28965 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
28966 cp.render('my-div');
28968 cp.on('select', function(palette, selColor){
28969 // do something with selColor
28973 * Create a new ColorPalette
28974 * @param {Object} config The config object
28976 Roo.ColorPalette = function(config){
28977 Roo.ColorPalette.superclass.constructor.call(this, config);
28981 * Fires when a color is selected
28982 * @param {ColorPalette} this
28983 * @param {String} color The 6-digit color hex code (without the # symbol)
28989 this.on("select", this.handler, this.scope, true);
28992 Roo.extend(Roo.ColorPalette, Roo.Component, {
28994 * @cfg {String} itemCls
28995 * The CSS class to apply to the containing element (defaults to "x-color-palette")
28997 itemCls : "x-color-palette",
28999 * @cfg {String} value
29000 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
29001 * the hex codes are case-sensitive.
29004 clickEvent:'click',
29006 ctype: "Roo.ColorPalette",
29009 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29011 allowReselect : false,
29014 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
29015 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
29016 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29017 * of colors with the width setting until the box is symmetrical.</p>
29018 * <p>You can override individual colors if needed:</p>
29020 var cp = new Roo.ColorPalette();
29021 cp.colors[0] = "FF0000"; // change the first box to red
29024 Or you can provide a custom array of your own for complete control:
29026 var cp = new Roo.ColorPalette();
29027 cp.colors = ["000000", "993300", "333300"];
29032 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29033 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29034 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29035 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29036 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29040 onRender : function(container, position){
29041 var t = new Roo.MasterTemplate(
29042 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
29044 var c = this.colors;
29045 for(var i = 0, len = c.length; i < len; i++){
29048 var el = document.createElement("div");
29049 el.className = this.itemCls;
29051 container.dom.insertBefore(el, position);
29052 this.el = Roo.get(el);
29053 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
29054 if(this.clickEvent != 'click'){
29055 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
29060 afterRender : function(){
29061 Roo.ColorPalette.superclass.afterRender.call(this);
29063 var s = this.value;
29070 handleClick : function(e, t){
29071 e.preventDefault();
29072 if(!this.disabled){
29073 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29074 this.select(c.toUpperCase());
29079 * Selects the specified color in the palette (fires the select event)
29080 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29082 select : function(color){
29083 color = color.replace("#", "");
29084 if(color != this.value || this.allowReselect){
29087 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29089 el.child("a.color-"+color).addClass("x-color-palette-sel");
29090 this.value = color;
29091 this.fireEvent("select", this, color);
29096 * Ext JS Library 1.1.1
29097 * Copyright(c) 2006-2007, Ext JS, LLC.
29099 * Originally Released Under LGPL - original licence link has changed is not relivant.
29102 * <script type="text/javascript">
29106 * @class Roo.DatePicker
29107 * @extends Roo.Component
29108 * Simple date picker class.
29110 * Create a new DatePicker
29111 * @param {Object} config The config object
29113 Roo.DatePicker = function(config){
29114 Roo.DatePicker.superclass.constructor.call(this, config);
29116 this.value = config && config.value ?
29117 config.value.clearTime() : new Date().clearTime();
29122 * Fires when a date is selected
29123 * @param {DatePicker} this
29124 * @param {Date} date The selected date
29128 * @event monthchange
29129 * Fires when the displayed month changes
29130 * @param {DatePicker} this
29131 * @param {Date} date The selected month
29133 'monthchange': true
29137 this.on("select", this.handler, this.scope || this);
29139 // build the disabledDatesRE
29140 if(!this.disabledDatesRE && this.disabledDates){
29141 var dd = this.disabledDates;
29143 for(var i = 0; i < dd.length; i++){
29145 if(i != dd.length-1) {
29149 this.disabledDatesRE = new RegExp(re + ")");
29153 Roo.extend(Roo.DatePicker, Roo.Component, {
29155 * @cfg {String} todayText
29156 * The text to display on the button that selects the current date (defaults to "Today")
29158 todayText : "Today",
29160 * @cfg {String} okText
29161 * The text to display on the ok button
29163 okText : " OK ", //   to give the user extra clicking room
29165 * @cfg {String} cancelText
29166 * The text to display on the cancel button
29168 cancelText : "Cancel",
29170 * @cfg {String} todayTip
29171 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29173 todayTip : "{0} (Spacebar)",
29175 * @cfg {Date} minDate
29176 * Minimum allowable date (JavaScript date object, defaults to null)
29180 * @cfg {Date} maxDate
29181 * Maximum allowable date (JavaScript date object, defaults to null)
29185 * @cfg {String} minText
29186 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29188 minText : "This date is before the minimum date",
29190 * @cfg {String} maxText
29191 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29193 maxText : "This date is after the maximum date",
29195 * @cfg {String} format
29196 * The default date format string which can be overriden for localization support. The format must be
29197 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29201 * @cfg {Array} disabledDays
29202 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29204 disabledDays : null,
29206 * @cfg {String} disabledDaysText
29207 * The tooltip to display when the date falls on a disabled day (defaults to "")
29209 disabledDaysText : "",
29211 * @cfg {RegExp} disabledDatesRE
29212 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29214 disabledDatesRE : null,
29216 * @cfg {String} disabledDatesText
29217 * The tooltip text to display when the date falls on a disabled date (defaults to "")
29219 disabledDatesText : "",
29221 * @cfg {Boolean} constrainToViewport
29222 * True to constrain the date picker to the viewport (defaults to true)
29224 constrainToViewport : true,
29226 * @cfg {Array} monthNames
29227 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29229 monthNames : Date.monthNames,
29231 * @cfg {Array} dayNames
29232 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29234 dayNames : Date.dayNames,
29236 * @cfg {String} nextText
29237 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29239 nextText: 'Next Month (Control+Right)',
29241 * @cfg {String} prevText
29242 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29244 prevText: 'Previous Month (Control+Left)',
29246 * @cfg {String} monthYearText
29247 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29249 monthYearText: 'Choose a month (Control+Up/Down to move years)',
29251 * @cfg {Number} startDay
29252 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29256 * @cfg {Bool} showClear
29257 * Show a clear button (usefull for date form elements that can be blank.)
29263 * Sets the value of the date field
29264 * @param {Date} value The date to set
29266 setValue : function(value){
29267 var old = this.value;
29269 if (typeof(value) == 'string') {
29271 value = Date.parseDate(value, this.format);
29274 value = new Date();
29277 this.value = value.clearTime(true);
29279 this.update(this.value);
29284 * Gets the current selected value of the date field
29285 * @return {Date} The selected date
29287 getValue : function(){
29292 focus : function(){
29294 this.update(this.activeDate);
29299 onRender : function(container, position){
29302 '<table cellspacing="0">',
29303 '<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>',
29304 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29305 var dn = this.dayNames;
29306 for(var i = 0; i < 7; i++){
29307 var d = this.startDay+i;
29311 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29313 m[m.length] = "</tr></thead><tbody><tr>";
29314 for(var i = 0; i < 42; i++) {
29315 if(i % 7 == 0 && i != 0){
29316 m[m.length] = "</tr><tr>";
29318 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29320 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29321 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29323 var el = document.createElement("div");
29324 el.className = "x-date-picker";
29325 el.innerHTML = m.join("");
29327 container.dom.insertBefore(el, position);
29329 this.el = Roo.get(el);
29330 this.eventEl = Roo.get(el.firstChild);
29332 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29333 handler: this.showPrevMonth,
29335 preventDefault:true,
29339 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29340 handler: this.showNextMonth,
29342 preventDefault:true,
29346 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
29348 this.monthPicker = this.el.down('div.x-date-mp');
29349 this.monthPicker.enableDisplayMode('block');
29351 var kn = new Roo.KeyNav(this.eventEl, {
29352 "left" : function(e){
29354 this.showPrevMonth() :
29355 this.update(this.activeDate.add("d", -1));
29358 "right" : function(e){
29360 this.showNextMonth() :
29361 this.update(this.activeDate.add("d", 1));
29364 "up" : function(e){
29366 this.showNextYear() :
29367 this.update(this.activeDate.add("d", -7));
29370 "down" : function(e){
29372 this.showPrevYear() :
29373 this.update(this.activeDate.add("d", 7));
29376 "pageUp" : function(e){
29377 this.showNextMonth();
29380 "pageDown" : function(e){
29381 this.showPrevMonth();
29384 "enter" : function(e){
29385 e.stopPropagation();
29392 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
29394 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
29396 this.el.unselectable();
29398 this.cells = this.el.select("table.x-date-inner tbody td");
29399 this.textNodes = this.el.query("table.x-date-inner tbody span");
29401 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29403 tooltip: this.monthYearText
29406 this.mbtn.on('click', this.showMonthPicker, this);
29407 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29410 var today = (new Date()).dateFormat(this.format);
29412 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29413 if (this.showClear) {
29414 baseTb.add( new Roo.Toolbar.Fill());
29417 text: String.format(this.todayText, today),
29418 tooltip: String.format(this.todayTip, today),
29419 handler: this.selectToday,
29423 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29426 if (this.showClear) {
29428 baseTb.add( new Roo.Toolbar.Fill());
29431 cls: 'x-btn-icon x-btn-clear',
29432 handler: function() {
29434 this.fireEvent("select", this, '');
29444 this.update(this.value);
29447 createMonthPicker : function(){
29448 if(!this.monthPicker.dom.firstChild){
29449 var buf = ['<table border="0" cellspacing="0">'];
29450 for(var i = 0; i < 6; i++){
29452 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29453 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29455 '<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>' :
29456 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29460 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29462 '</button><button type="button" class="x-date-mp-cancel">',
29464 '</button></td></tr>',
29467 this.monthPicker.update(buf.join(''));
29468 this.monthPicker.on('click', this.onMonthClick, this);
29469 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29471 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29472 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29474 this.mpMonths.each(function(m, a, i){
29477 m.dom.xmonth = 5 + Math.round(i * .5);
29479 m.dom.xmonth = Math.round((i-1) * .5);
29485 showMonthPicker : function(){
29486 this.createMonthPicker();
29487 var size = this.el.getSize();
29488 this.monthPicker.setSize(size);
29489 this.monthPicker.child('table').setSize(size);
29491 this.mpSelMonth = (this.activeDate || this.value).getMonth();
29492 this.updateMPMonth(this.mpSelMonth);
29493 this.mpSelYear = (this.activeDate || this.value).getFullYear();
29494 this.updateMPYear(this.mpSelYear);
29496 this.monthPicker.slideIn('t', {duration:.2});
29499 updateMPYear : function(y){
29501 var ys = this.mpYears.elements;
29502 for(var i = 1; i <= 10; i++){
29503 var td = ys[i-1], y2;
29505 y2 = y + Math.round(i * .5);
29506 td.firstChild.innerHTML = y2;
29509 y2 = y - (5-Math.round(i * .5));
29510 td.firstChild.innerHTML = y2;
29513 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29517 updateMPMonth : function(sm){
29518 this.mpMonths.each(function(m, a, i){
29519 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29523 selectMPMonth: function(m){
29527 onMonthClick : function(e, t){
29529 var el = new Roo.Element(t), pn;
29530 if(el.is('button.x-date-mp-cancel')){
29531 this.hideMonthPicker();
29533 else if(el.is('button.x-date-mp-ok')){
29534 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29535 this.hideMonthPicker();
29537 else if(pn = el.up('td.x-date-mp-month', 2)){
29538 this.mpMonths.removeClass('x-date-mp-sel');
29539 pn.addClass('x-date-mp-sel');
29540 this.mpSelMonth = pn.dom.xmonth;
29542 else if(pn = el.up('td.x-date-mp-year', 2)){
29543 this.mpYears.removeClass('x-date-mp-sel');
29544 pn.addClass('x-date-mp-sel');
29545 this.mpSelYear = pn.dom.xyear;
29547 else if(el.is('a.x-date-mp-prev')){
29548 this.updateMPYear(this.mpyear-10);
29550 else if(el.is('a.x-date-mp-next')){
29551 this.updateMPYear(this.mpyear+10);
29555 onMonthDblClick : function(e, t){
29557 var el = new Roo.Element(t), pn;
29558 if(pn = el.up('td.x-date-mp-month', 2)){
29559 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29560 this.hideMonthPicker();
29562 else if(pn = el.up('td.x-date-mp-year', 2)){
29563 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29564 this.hideMonthPicker();
29568 hideMonthPicker : function(disableAnim){
29569 if(this.monthPicker){
29570 if(disableAnim === true){
29571 this.monthPicker.hide();
29573 this.monthPicker.slideOut('t', {duration:.2});
29579 showPrevMonth : function(e){
29580 this.update(this.activeDate.add("mo", -1));
29584 showNextMonth : function(e){
29585 this.update(this.activeDate.add("mo", 1));
29589 showPrevYear : function(){
29590 this.update(this.activeDate.add("y", -1));
29594 showNextYear : function(){
29595 this.update(this.activeDate.add("y", 1));
29599 handleMouseWheel : function(e){
29600 var delta = e.getWheelDelta();
29602 this.showPrevMonth();
29604 } else if(delta < 0){
29605 this.showNextMonth();
29611 handleDateClick : function(e, t){
29613 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29614 this.setValue(new Date(t.dateValue));
29615 this.fireEvent("select", this, this.value);
29620 selectToday : function(){
29621 this.setValue(new Date().clearTime());
29622 this.fireEvent("select", this, this.value);
29626 update : function(date)
29628 var vd = this.activeDate;
29629 this.activeDate = date;
29631 var t = date.getTime();
29632 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29633 this.cells.removeClass("x-date-selected");
29634 this.cells.each(function(c){
29635 if(c.dom.firstChild.dateValue == t){
29636 c.addClass("x-date-selected");
29637 setTimeout(function(){
29638 try{c.dom.firstChild.focus();}catch(e){}
29647 var days = date.getDaysInMonth();
29648 var firstOfMonth = date.getFirstDateOfMonth();
29649 var startingPos = firstOfMonth.getDay()-this.startDay;
29651 if(startingPos <= this.startDay){
29655 var pm = date.add("mo", -1);
29656 var prevStart = pm.getDaysInMonth()-startingPos;
29658 var cells = this.cells.elements;
29659 var textEls = this.textNodes;
29660 days += startingPos;
29662 // convert everything to numbers so it's fast
29663 var day = 86400000;
29664 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29665 var today = new Date().clearTime().getTime();
29666 var sel = date.clearTime().getTime();
29667 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29668 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29669 var ddMatch = this.disabledDatesRE;
29670 var ddText = this.disabledDatesText;
29671 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29672 var ddaysText = this.disabledDaysText;
29673 var format = this.format;
29675 var setCellClass = function(cal, cell){
29677 var t = d.getTime();
29678 cell.firstChild.dateValue = t;
29680 cell.className += " x-date-today";
29681 cell.title = cal.todayText;
29684 cell.className += " x-date-selected";
29685 setTimeout(function(){
29686 try{cell.firstChild.focus();}catch(e){}
29691 cell.className = " x-date-disabled";
29692 cell.title = cal.minText;
29696 cell.className = " x-date-disabled";
29697 cell.title = cal.maxText;
29701 if(ddays.indexOf(d.getDay()) != -1){
29702 cell.title = ddaysText;
29703 cell.className = " x-date-disabled";
29706 if(ddMatch && format){
29707 var fvalue = d.dateFormat(format);
29708 if(ddMatch.test(fvalue)){
29709 cell.title = ddText.replace("%0", fvalue);
29710 cell.className = " x-date-disabled";
29716 for(; i < startingPos; i++) {
29717 textEls[i].innerHTML = (++prevStart);
29718 d.setDate(d.getDate()+1);
29719 cells[i].className = "x-date-prevday";
29720 setCellClass(this, cells[i]);
29722 for(; i < days; i++){
29723 intDay = i - startingPos + 1;
29724 textEls[i].innerHTML = (intDay);
29725 d.setDate(d.getDate()+1);
29726 cells[i].className = "x-date-active";
29727 setCellClass(this, cells[i]);
29730 for(; i < 42; i++) {
29731 textEls[i].innerHTML = (++extraDays);
29732 d.setDate(d.getDate()+1);
29733 cells[i].className = "x-date-nextday";
29734 setCellClass(this, cells[i]);
29737 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29738 this.fireEvent('monthchange', this, date);
29740 if(!this.internalRender){
29741 var main = this.el.dom.firstChild;
29742 var w = main.offsetWidth;
29743 this.el.setWidth(w + this.el.getBorderWidth("lr"));
29744 Roo.fly(main).setWidth(w);
29745 this.internalRender = true;
29746 // opera does not respect the auto grow header center column
29747 // then, after it gets a width opera refuses to recalculate
29748 // without a second pass
29749 if(Roo.isOpera && !this.secondPass){
29750 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29751 this.secondPass = true;
29752 this.update.defer(10, this, [date]);
29760 * Ext JS Library 1.1.1
29761 * Copyright(c) 2006-2007, Ext JS, LLC.
29763 * Originally Released Under LGPL - original licence link has changed is not relivant.
29766 * <script type="text/javascript">
29769 * @class Roo.TabPanel
29770 * @extends Roo.util.Observable
29771 * A lightweight tab container.
29775 // basic tabs 1, built from existing content
29776 var tabs = new Roo.TabPanel("tabs1");
29777 tabs.addTab("script", "View Script");
29778 tabs.addTab("markup", "View Markup");
29779 tabs.activate("script");
29781 // more advanced tabs, built from javascript
29782 var jtabs = new Roo.TabPanel("jtabs");
29783 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29785 // set up the UpdateManager
29786 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29787 var updater = tab2.getUpdateManager();
29788 updater.setDefaultUrl("ajax1.htm");
29789 tab2.on('activate', updater.refresh, updater, true);
29791 // Use setUrl for Ajax loading
29792 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29793 tab3.setUrl("ajax2.htm", null, true);
29796 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29799 jtabs.activate("jtabs-1");
29802 * Create a new TabPanel.
29803 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29804 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29806 Roo.TabPanel = function(container, config){
29808 * The container element for this TabPanel.
29809 * @type Roo.Element
29811 this.el = Roo.get(container, true);
29813 if(typeof config == "boolean"){
29814 this.tabPosition = config ? "bottom" : "top";
29816 Roo.apply(this, config);
29819 if(this.tabPosition == "bottom"){
29820 this.bodyEl = Roo.get(this.createBody(this.el.dom));
29821 this.el.addClass("x-tabs-bottom");
29823 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29824 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29825 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29827 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29829 if(this.tabPosition != "bottom"){
29830 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29831 * @type Roo.Element
29833 this.bodyEl = Roo.get(this.createBody(this.el.dom));
29834 this.el.addClass("x-tabs-top");
29838 this.bodyEl.setStyle("position", "relative");
29840 this.active = null;
29841 this.activateDelegate = this.activate.createDelegate(this);
29846 * Fires when the active tab changes
29847 * @param {Roo.TabPanel} this
29848 * @param {Roo.TabPanelItem} activePanel The new active tab
29852 * @event beforetabchange
29853 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29854 * @param {Roo.TabPanel} this
29855 * @param {Object} e Set cancel to true on this object to cancel the tab change
29856 * @param {Roo.TabPanelItem} tab The tab being changed to
29858 "beforetabchange" : true
29861 Roo.EventManager.onWindowResize(this.onResize, this);
29862 this.cpad = this.el.getPadding("lr");
29863 this.hiddenCount = 0;
29866 // toolbar on the tabbar support...
29867 if (this.toolbar) {
29868 var tcfg = this.toolbar;
29869 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
29870 this.toolbar = new Roo.Toolbar(tcfg);
29871 if (Roo.isSafari) {
29872 var tbl = tcfg.container.child('table', true);
29873 tbl.setAttribute('width', '100%');
29880 Roo.TabPanel.superclass.constructor.call(this);
29883 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29885 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29887 tabPosition : "top",
29889 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29891 currentTabWidth : 0,
29893 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29897 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29901 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29903 preferredTabWidth : 175,
29905 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29907 resizeTabs : false,
29909 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29911 monitorResize : true,
29913 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
29918 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29919 * @param {String} id The id of the div to use <b>or create</b>
29920 * @param {String} text The text for the tab
29921 * @param {String} content (optional) Content to put in the TabPanelItem body
29922 * @param {Boolean} closable (optional) True to create a close icon on the tab
29923 * @return {Roo.TabPanelItem} The created TabPanelItem
29925 addTab : function(id, text, content, closable){
29926 var item = new Roo.TabPanelItem(this, id, text, closable);
29927 this.addTabItem(item);
29929 item.setContent(content);
29935 * Returns the {@link Roo.TabPanelItem} with the specified id/index
29936 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29937 * @return {Roo.TabPanelItem}
29939 getTab : function(id){
29940 return this.items[id];
29944 * Hides the {@link Roo.TabPanelItem} with the specified id/index
29945 * @param {String/Number} id The id or index of the TabPanelItem to hide.
29947 hideTab : function(id){
29948 var t = this.items[id];
29951 this.hiddenCount++;
29952 this.autoSizeTabs();
29957 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29958 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29960 unhideTab : function(id){
29961 var t = this.items[id];
29963 t.setHidden(false);
29964 this.hiddenCount--;
29965 this.autoSizeTabs();
29970 * Adds an existing {@link Roo.TabPanelItem}.
29971 * @param {Roo.TabPanelItem} item The TabPanelItem to add
29973 addTabItem : function(item){
29974 this.items[item.id] = item;
29975 this.items.push(item);
29976 if(this.resizeTabs){
29977 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29978 this.autoSizeTabs();
29985 * Removes a {@link Roo.TabPanelItem}.
29986 * @param {String/Number} id The id or index of the TabPanelItem to remove.
29988 removeTab : function(id){
29989 var items = this.items;
29990 var tab = items[id];
29991 if(!tab) { return; }
29992 var index = items.indexOf(tab);
29993 if(this.active == tab && items.length > 1){
29994 var newTab = this.getNextAvailable(index);
29999 this.stripEl.dom.removeChild(tab.pnode.dom);
30000 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30001 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30003 items.splice(index, 1);
30004 delete this.items[tab.id];
30005 tab.fireEvent("close", tab);
30006 tab.purgeListeners();
30007 this.autoSizeTabs();
30010 getNextAvailable : function(start){
30011 var items = this.items;
30013 // look for a next tab that will slide over to
30014 // replace the one being removed
30015 while(index < items.length){
30016 var item = items[++index];
30017 if(item && !item.isHidden()){
30021 // if one isn't found select the previous tab (on the left)
30024 var item = items[--index];
30025 if(item && !item.isHidden()){
30033 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30034 * @param {String/Number} id The id or index of the TabPanelItem to disable.
30036 disableTab : function(id){
30037 var tab = this.items[id];
30038 if(tab && this.active != tab){
30044 * Enables a {@link Roo.TabPanelItem} that is disabled.
30045 * @param {String/Number} id The id or index of the TabPanelItem to enable.
30047 enableTab : function(id){
30048 var tab = this.items[id];
30053 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30054 * @param {String/Number} id The id or index of the TabPanelItem to activate.
30055 * @return {Roo.TabPanelItem} The TabPanelItem.
30057 activate : function(id){
30058 var tab = this.items[id];
30062 if(tab == this.active || tab.disabled){
30066 this.fireEvent("beforetabchange", this, e, tab);
30067 if(e.cancel !== true && !tab.disabled){
30069 this.active.hide();
30071 this.active = this.items[id];
30072 this.active.show();
30073 this.fireEvent("tabchange", this, this.active);
30079 * Gets the active {@link Roo.TabPanelItem}.
30080 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30082 getActiveTab : function(){
30083 return this.active;
30087 * Updates the tab body element to fit the height of the container element
30088 * for overflow scrolling
30089 * @param {Number} targetHeight (optional) Override the starting height from the elements height
30091 syncHeight : function(targetHeight){
30092 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30093 var bm = this.bodyEl.getMargins();
30094 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30095 this.bodyEl.setHeight(newHeight);
30099 onResize : function(){
30100 if(this.monitorResize){
30101 this.autoSizeTabs();
30106 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30108 beginUpdate : function(){
30109 this.updating = true;
30113 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30115 endUpdate : function(){
30116 this.updating = false;
30117 this.autoSizeTabs();
30121 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30123 autoSizeTabs : function(){
30124 var count = this.items.length;
30125 var vcount = count - this.hiddenCount;
30126 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30129 var w = Math.max(this.el.getWidth() - this.cpad, 10);
30130 var availWidth = Math.floor(w / vcount);
30131 var b = this.stripBody;
30132 if(b.getWidth() > w){
30133 var tabs = this.items;
30134 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30135 if(availWidth < this.minTabWidth){
30136 /*if(!this.sleft){ // incomplete scrolling code
30137 this.createScrollButtons();
30140 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30143 if(this.currentTabWidth < this.preferredTabWidth){
30144 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30150 * Returns the number of tabs in this TabPanel.
30153 getCount : function(){
30154 return this.items.length;
30158 * Resizes all the tabs to the passed width
30159 * @param {Number} The new width
30161 setTabWidth : function(width){
30162 this.currentTabWidth = width;
30163 for(var i = 0, len = this.items.length; i < len; i++) {
30164 if(!this.items[i].isHidden()) {
30165 this.items[i].setWidth(width);
30171 * Destroys this TabPanel
30172 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30174 destroy : function(removeEl){
30175 Roo.EventManager.removeResizeListener(this.onResize, this);
30176 for(var i = 0, len = this.items.length; i < len; i++){
30177 this.items[i].purgeListeners();
30179 if(removeEl === true){
30180 this.el.update("");
30187 * @class Roo.TabPanelItem
30188 * @extends Roo.util.Observable
30189 * Represents an individual item (tab plus body) in a TabPanel.
30190 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30191 * @param {String} id The id of this TabPanelItem
30192 * @param {String} text The text for the tab of this TabPanelItem
30193 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30195 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30197 * The {@link Roo.TabPanel} this TabPanelItem belongs to
30198 * @type Roo.TabPanel
30200 this.tabPanel = tabPanel;
30202 * The id for this TabPanelItem
30207 this.disabled = false;
30211 this.loaded = false;
30212 this.closable = closable;
30215 * The body element for this TabPanelItem.
30216 * @type Roo.Element
30218 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30219 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30220 this.bodyEl.setStyle("display", "block");
30221 this.bodyEl.setStyle("zoom", "1");
30224 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30226 this.el = Roo.get(els.el, true);
30227 this.inner = Roo.get(els.inner, true);
30228 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30229 this.pnode = Roo.get(els.el.parentNode, true);
30230 this.el.on("mousedown", this.onTabMouseDown, this);
30231 this.el.on("click", this.onTabClick, this);
30234 var c = Roo.get(els.close, true);
30235 c.dom.title = this.closeText;
30236 c.addClassOnOver("close-over");
30237 c.on("click", this.closeClick, this);
30243 * Fires when this tab becomes the active tab.
30244 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30245 * @param {Roo.TabPanelItem} this
30249 * @event beforeclose
30250 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30251 * @param {Roo.TabPanelItem} this
30252 * @param {Object} e Set cancel to true on this object to cancel the close.
30254 "beforeclose": true,
30257 * Fires when this tab is closed.
30258 * @param {Roo.TabPanelItem} this
30262 * @event deactivate
30263 * Fires when this tab is no longer the active tab.
30264 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30265 * @param {Roo.TabPanelItem} this
30267 "deactivate" : true
30269 this.hidden = false;
30271 Roo.TabPanelItem.superclass.constructor.call(this);
30274 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30275 purgeListeners : function(){
30276 Roo.util.Observable.prototype.purgeListeners.call(this);
30277 this.el.removeAllListeners();
30280 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30283 this.pnode.addClass("on");
30286 this.tabPanel.stripWrap.repaint();
30288 this.fireEvent("activate", this.tabPanel, this);
30292 * Returns true if this tab is the active tab.
30293 * @return {Boolean}
30295 isActive : function(){
30296 return this.tabPanel.getActiveTab() == this;
30300 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30303 this.pnode.removeClass("on");
30305 this.fireEvent("deactivate", this.tabPanel, this);
30308 hideAction : function(){
30309 this.bodyEl.hide();
30310 this.bodyEl.setStyle("position", "absolute");
30311 this.bodyEl.setLeft("-20000px");
30312 this.bodyEl.setTop("-20000px");
30315 showAction : function(){
30316 this.bodyEl.setStyle("position", "relative");
30317 this.bodyEl.setTop("");
30318 this.bodyEl.setLeft("");
30319 this.bodyEl.show();
30323 * Set the tooltip for the tab.
30324 * @param {String} tooltip The tab's tooltip
30326 setTooltip : function(text){
30327 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30328 this.textEl.dom.qtip = text;
30329 this.textEl.dom.removeAttribute('title');
30331 this.textEl.dom.title = text;
30335 onTabClick : function(e){
30336 e.preventDefault();
30337 this.tabPanel.activate(this.id);
30340 onTabMouseDown : function(e){
30341 e.preventDefault();
30342 this.tabPanel.activate(this.id);
30345 getWidth : function(){
30346 return this.inner.getWidth();
30349 setWidth : function(width){
30350 var iwidth = width - this.pnode.getPadding("lr");
30351 this.inner.setWidth(iwidth);
30352 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30353 this.pnode.setWidth(width);
30357 * Show or hide the tab
30358 * @param {Boolean} hidden True to hide or false to show.
30360 setHidden : function(hidden){
30361 this.hidden = hidden;
30362 this.pnode.setStyle("display", hidden ? "none" : "");
30366 * Returns true if this tab is "hidden"
30367 * @return {Boolean}
30369 isHidden : function(){
30370 return this.hidden;
30374 * Returns the text for this tab
30377 getText : function(){
30381 autoSize : function(){
30382 //this.el.beginMeasure();
30383 this.textEl.setWidth(1);
30385 * #2804 [new] Tabs in Roojs
30386 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30388 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30389 //this.el.endMeasure();
30393 * Sets the text for the tab (Note: this also sets the tooltip text)
30394 * @param {String} text The tab's text and tooltip
30396 setText : function(text){
30398 this.textEl.update(text);
30399 this.setTooltip(text);
30400 if(!this.tabPanel.resizeTabs){
30405 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30407 activate : function(){
30408 this.tabPanel.activate(this.id);
30412 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30414 disable : function(){
30415 if(this.tabPanel.active != this){
30416 this.disabled = true;
30417 this.pnode.addClass("disabled");
30422 * Enables this TabPanelItem if it was previously disabled.
30424 enable : function(){
30425 this.disabled = false;
30426 this.pnode.removeClass("disabled");
30430 * Sets the content for this TabPanelItem.
30431 * @param {String} content The content
30432 * @param {Boolean} loadScripts true to look for and load scripts
30434 setContent : function(content, loadScripts){
30435 this.bodyEl.update(content, loadScripts);
30439 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30440 * @return {Roo.UpdateManager} The UpdateManager
30442 getUpdateManager : function(){
30443 return this.bodyEl.getUpdateManager();
30447 * Set a URL to be used to load the content for this TabPanelItem.
30448 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30449 * @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)
30450 * @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)
30451 * @return {Roo.UpdateManager} The UpdateManager
30453 setUrl : function(url, params, loadOnce){
30454 if(this.refreshDelegate){
30455 this.un('activate', this.refreshDelegate);
30457 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30458 this.on("activate", this.refreshDelegate);
30459 return this.bodyEl.getUpdateManager();
30463 _handleRefresh : function(url, params, loadOnce){
30464 if(!loadOnce || !this.loaded){
30465 var updater = this.bodyEl.getUpdateManager();
30466 updater.update(url, params, this._setLoaded.createDelegate(this));
30471 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
30472 * Will fail silently if the setUrl method has not been called.
30473 * This does not activate the panel, just updates its content.
30475 refresh : function(){
30476 if(this.refreshDelegate){
30477 this.loaded = false;
30478 this.refreshDelegate();
30483 _setLoaded : function(){
30484 this.loaded = true;
30488 closeClick : function(e){
30491 this.fireEvent("beforeclose", this, o);
30492 if(o.cancel !== true){
30493 this.tabPanel.removeTab(this.id);
30497 * The text displayed in the tooltip for the close icon.
30500 closeText : "Close this tab"
30504 Roo.TabPanel.prototype.createStrip = function(container){
30505 var strip = document.createElement("div");
30506 strip.className = "x-tabs-wrap";
30507 container.appendChild(strip);
30511 Roo.TabPanel.prototype.createStripList = function(strip){
30512 // div wrapper for retard IE
30513 // returns the "tr" element.
30514 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30515 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30516 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30517 return strip.firstChild.firstChild.firstChild.firstChild;
30520 Roo.TabPanel.prototype.createBody = function(container){
30521 var body = document.createElement("div");
30522 Roo.id(body, "tab-body");
30523 Roo.fly(body).addClass("x-tabs-body");
30524 container.appendChild(body);
30528 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30529 var body = Roo.getDom(id);
30531 body = document.createElement("div");
30534 Roo.fly(body).addClass("x-tabs-item-body");
30535 bodyEl.insertBefore(body, bodyEl.firstChild);
30539 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30540 var td = document.createElement("td");
30541 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30542 //stripEl.appendChild(td);
30544 td.className = "x-tabs-closable";
30545 if(!this.closeTpl){
30546 this.closeTpl = new Roo.Template(
30547 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30548 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30549 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
30552 var el = this.closeTpl.overwrite(td, {"text": text});
30553 var close = el.getElementsByTagName("div")[0];
30554 var inner = el.getElementsByTagName("em")[0];
30555 return {"el": el, "close": close, "inner": inner};
30558 this.tabTpl = new Roo.Template(
30559 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30560 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30563 var el = this.tabTpl.overwrite(td, {"text": text});
30564 var inner = el.getElementsByTagName("em")[0];
30565 return {"el": el, "inner": inner};
30569 * Ext JS Library 1.1.1
30570 * Copyright(c) 2006-2007, Ext JS, LLC.
30572 * Originally Released Under LGPL - original licence link has changed is not relivant.
30575 * <script type="text/javascript">
30579 * @class Roo.Button
30580 * @extends Roo.util.Observable
30581 * Simple Button class
30582 * @cfg {String} text The button text
30583 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30584 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30585 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30586 * @cfg {Object} scope The scope of the handler
30587 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30588 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30589 * @cfg {Boolean} hidden True to start hidden (defaults to false)
30590 * @cfg {Boolean} disabled True to start disabled (defaults to false)
30591 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30592 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30593 applies if enableToggle = true)
30594 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30595 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30596 an {@link Roo.util.ClickRepeater} config object (defaults to false).
30598 * Create a new button
30599 * @param {Object} config The config object
30601 Roo.Button = function(renderTo, config)
30605 renderTo = config.renderTo || false;
30608 Roo.apply(this, config);
30612 * Fires when this button is clicked
30613 * @param {Button} this
30614 * @param {EventObject} e The click event
30619 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30620 * @param {Button} this
30621 * @param {Boolean} pressed
30626 * Fires when the mouse hovers over the button
30627 * @param {Button} this
30628 * @param {Event} e The event object
30630 'mouseover' : true,
30633 * Fires when the mouse exits the button
30634 * @param {Button} this
30635 * @param {Event} e The event object
30640 * Fires when the button is rendered
30641 * @param {Button} this
30646 this.menu = Roo.menu.MenuMgr.get(this.menu);
30648 // register listeners first!! - so render can be captured..
30649 Roo.util.Observable.call(this);
30651 this.render(renderTo);
30657 Roo.extend(Roo.Button, Roo.util.Observable, {
30663 * Read-only. True if this button is hidden
30668 * Read-only. True if this button is disabled
30673 * Read-only. True if this button is pressed (only if enableToggle = true)
30679 * @cfg {Number} tabIndex
30680 * The DOM tabIndex for this button (defaults to undefined)
30682 tabIndex : undefined,
30685 * @cfg {Boolean} enableToggle
30686 * True to enable pressed/not pressed toggling (defaults to false)
30688 enableToggle: false,
30690 * @cfg {Roo.menu.Menu} menu
30691 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30695 * @cfg {String} menuAlign
30696 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30698 menuAlign : "tl-bl?",
30701 * @cfg {String} iconCls
30702 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30704 iconCls : undefined,
30706 * @cfg {String} type
30707 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
30712 menuClassTarget: 'tr',
30715 * @cfg {String} clickEvent
30716 * The type of event to map to the button's event handler (defaults to 'click')
30718 clickEvent : 'click',
30721 * @cfg {Boolean} handleMouseEvents
30722 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30724 handleMouseEvents : true,
30727 * @cfg {String} tooltipType
30728 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30730 tooltipType : 'qtip',
30733 * @cfg {String} cls
30734 * A CSS class to apply to the button's main element.
30738 * @cfg {Roo.Template} template (Optional)
30739 * An {@link Roo.Template} with which to create the Button's main element. This Template must
30740 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30741 * require code modifications if required elements (e.g. a button) aren't present.
30745 render : function(renderTo){
30747 if(this.hideParent){
30748 this.parentEl = Roo.get(renderTo);
30750 if(!this.dhconfig){
30751 if(!this.template){
30752 if(!Roo.Button.buttonTemplate){
30753 // hideous table template
30754 Roo.Button.buttonTemplate = new Roo.Template(
30755 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30756 '<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>',
30757 "</tr></tbody></table>");
30759 this.template = Roo.Button.buttonTemplate;
30761 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
30762 var btnEl = btn.child("button:first");
30763 btnEl.on('focus', this.onFocus, this);
30764 btnEl.on('blur', this.onBlur, this);
30766 btn.addClass(this.cls);
30769 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30772 btnEl.addClass(this.iconCls);
30774 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30777 if(this.tabIndex !== undefined){
30778 btnEl.dom.tabIndex = this.tabIndex;
30781 if(typeof this.tooltip == 'object'){
30782 Roo.QuickTips.tips(Roo.apply({
30786 btnEl.dom[this.tooltipType] = this.tooltip;
30790 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30794 this.el.dom.id = this.el.id = this.id;
30797 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30798 this.menu.on("show", this.onMenuShow, this);
30799 this.menu.on("hide", this.onMenuHide, this);
30801 btn.addClass("x-btn");
30802 if(Roo.isIE && !Roo.isIE7){
30803 this.autoWidth.defer(1, this);
30807 if(this.handleMouseEvents){
30808 btn.on("mouseover", this.onMouseOver, this);
30809 btn.on("mouseout", this.onMouseOut, this);
30810 btn.on("mousedown", this.onMouseDown, this);
30812 btn.on(this.clickEvent, this.onClick, this);
30813 //btn.on("mouseup", this.onMouseUp, this);
30820 Roo.ButtonToggleMgr.register(this);
30822 this.el.addClass("x-btn-pressed");
30825 var repeater = new Roo.util.ClickRepeater(btn,
30826 typeof this.repeat == "object" ? this.repeat : {}
30828 repeater.on("click", this.onClick, this);
30831 this.fireEvent('render', this);
30835 * Returns the button's underlying element
30836 * @return {Roo.Element} The element
30838 getEl : function(){
30843 * Destroys this Button and removes any listeners.
30845 destroy : function(){
30846 Roo.ButtonToggleMgr.unregister(this);
30847 this.el.removeAllListeners();
30848 this.purgeListeners();
30853 autoWidth : function(){
30855 this.el.setWidth("auto");
30856 if(Roo.isIE7 && Roo.isStrict){
30857 var ib = this.el.child('button');
30858 if(ib && ib.getWidth() > 20){
30860 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30865 this.el.beginMeasure();
30867 if(this.el.getWidth() < this.minWidth){
30868 this.el.setWidth(this.minWidth);
30871 this.el.endMeasure();
30878 * Assigns this button's click handler
30879 * @param {Function} handler The function to call when the button is clicked
30880 * @param {Object} scope (optional) Scope for the function passed in
30882 setHandler : function(handler, scope){
30883 this.handler = handler;
30884 this.scope = scope;
30888 * Sets this button's text
30889 * @param {String} text The button text
30891 setText : function(text){
30894 this.el.child("td.x-btn-center button.x-btn-text").update(text);
30900 * Gets the text for this button
30901 * @return {String} The button text
30903 getText : function(){
30911 this.hidden = false;
30913 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30921 this.hidden = true;
30923 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30928 * Convenience function for boolean show/hide
30929 * @param {Boolean} visible True to show, false to hide
30931 setVisible: function(visible){
30940 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30941 * @param {Boolean} state (optional) Force a particular state
30943 toggle : function(state){
30944 state = state === undefined ? !this.pressed : state;
30945 if(state != this.pressed){
30947 this.el.addClass("x-btn-pressed");
30948 this.pressed = true;
30949 this.fireEvent("toggle", this, true);
30951 this.el.removeClass("x-btn-pressed");
30952 this.pressed = false;
30953 this.fireEvent("toggle", this, false);
30955 if(this.toggleHandler){
30956 this.toggleHandler.call(this.scope || this, this, state);
30964 focus : function(){
30965 this.el.child('button:first').focus();
30969 * Disable this button
30971 disable : function(){
30973 this.el.addClass("x-btn-disabled");
30975 this.disabled = true;
30979 * Enable this button
30981 enable : function(){
30983 this.el.removeClass("x-btn-disabled");
30985 this.disabled = false;
30989 * Convenience function for boolean enable/disable
30990 * @param {Boolean} enabled True to enable, false to disable
30992 setDisabled : function(v){
30993 this[v !== true ? "enable" : "disable"]();
30997 onClick : function(e)
31000 e.preventDefault();
31005 if(!this.disabled){
31006 if(this.enableToggle){
31009 if(this.menu && !this.menu.isVisible()){
31010 this.menu.show(this.el, this.menuAlign);
31012 this.fireEvent("click", this, e);
31014 this.el.removeClass("x-btn-over");
31015 this.handler.call(this.scope || this, this, e);
31020 onMouseOver : function(e){
31021 if(!this.disabled){
31022 this.el.addClass("x-btn-over");
31023 this.fireEvent('mouseover', this, e);
31027 onMouseOut : function(e){
31028 if(!e.within(this.el, true)){
31029 this.el.removeClass("x-btn-over");
31030 this.fireEvent('mouseout', this, e);
31034 onFocus : function(e){
31035 if(!this.disabled){
31036 this.el.addClass("x-btn-focus");
31040 onBlur : function(e){
31041 this.el.removeClass("x-btn-focus");
31044 onMouseDown : function(e){
31045 if(!this.disabled && e.button == 0){
31046 this.el.addClass("x-btn-click");
31047 Roo.get(document).on('mouseup', this.onMouseUp, this);
31051 onMouseUp : function(e){
31053 this.el.removeClass("x-btn-click");
31054 Roo.get(document).un('mouseup', this.onMouseUp, this);
31058 onMenuShow : function(e){
31059 this.el.addClass("x-btn-menu-active");
31062 onMenuHide : function(e){
31063 this.el.removeClass("x-btn-menu-active");
31067 // Private utility class used by Button
31068 Roo.ButtonToggleMgr = function(){
31071 function toggleGroup(btn, state){
31073 var g = groups[btn.toggleGroup];
31074 for(var i = 0, l = g.length; i < l; i++){
31076 g[i].toggle(false);
31083 register : function(btn){
31084 if(!btn.toggleGroup){
31087 var g = groups[btn.toggleGroup];
31089 g = groups[btn.toggleGroup] = [];
31092 btn.on("toggle", toggleGroup);
31095 unregister : function(btn){
31096 if(!btn.toggleGroup){
31099 var g = groups[btn.toggleGroup];
31102 btn.un("toggle", toggleGroup);
31108 * Ext JS Library 1.1.1
31109 * Copyright(c) 2006-2007, Ext JS, LLC.
31111 * Originally Released Under LGPL - original licence link has changed is not relivant.
31114 * <script type="text/javascript">
31118 * @class Roo.SplitButton
31119 * @extends Roo.Button
31120 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31121 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
31122 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31123 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31124 * @cfg {String} arrowTooltip The title attribute of the arrow
31126 * Create a new menu button
31127 * @param {String/HTMLElement/Element} renderTo The element to append the button to
31128 * @param {Object} config The config object
31130 Roo.SplitButton = function(renderTo, config){
31131 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31133 * @event arrowclick
31134 * Fires when this button's arrow is clicked
31135 * @param {SplitButton} this
31136 * @param {EventObject} e The click event
31138 this.addEvents({"arrowclick":true});
31141 Roo.extend(Roo.SplitButton, Roo.Button, {
31142 render : function(renderTo){
31143 // this is one sweet looking template!
31144 var tpl = new Roo.Template(
31145 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31146 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31147 '<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>',
31148 "</tbody></table></td><td>",
31149 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31150 '<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>',
31151 "</tbody></table></td></tr></table>"
31153 var btn = tpl.append(renderTo, [this.text, this.type], true);
31154 var btnEl = btn.child("button");
31156 btn.addClass(this.cls);
31159 btnEl.setStyle('background-image', 'url(' +this.icon +')');
31162 btnEl.addClass(this.iconCls);
31164 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31168 if(this.handleMouseEvents){
31169 btn.on("mouseover", this.onMouseOver, this);
31170 btn.on("mouseout", this.onMouseOut, this);
31171 btn.on("mousedown", this.onMouseDown, this);
31172 btn.on("mouseup", this.onMouseUp, this);
31174 btn.on(this.clickEvent, this.onClick, this);
31176 if(typeof this.tooltip == 'object'){
31177 Roo.QuickTips.tips(Roo.apply({
31181 btnEl.dom[this.tooltipType] = this.tooltip;
31184 if(this.arrowTooltip){
31185 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31194 this.el.addClass("x-btn-pressed");
31196 if(Roo.isIE && !Roo.isIE7){
31197 this.autoWidth.defer(1, this);
31202 this.menu.on("show", this.onMenuShow, this);
31203 this.menu.on("hide", this.onMenuHide, this);
31205 this.fireEvent('render', this);
31209 autoWidth : function(){
31211 var tbl = this.el.child("table:first");
31212 var tbl2 = this.el.child("table:last");
31213 this.el.setWidth("auto");
31214 tbl.setWidth("auto");
31215 if(Roo.isIE7 && Roo.isStrict){
31216 var ib = this.el.child('button:first');
31217 if(ib && ib.getWidth() > 20){
31219 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31224 this.el.beginMeasure();
31226 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31227 tbl.setWidth(this.minWidth-tbl2.getWidth());
31230 this.el.endMeasure();
31233 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31237 * Sets this button's click handler
31238 * @param {Function} handler The function to call when the button is clicked
31239 * @param {Object} scope (optional) Scope for the function passed above
31241 setHandler : function(handler, scope){
31242 this.handler = handler;
31243 this.scope = scope;
31247 * Sets this button's arrow click handler
31248 * @param {Function} handler The function to call when the arrow is clicked
31249 * @param {Object} scope (optional) Scope for the function passed above
31251 setArrowHandler : function(handler, scope){
31252 this.arrowHandler = handler;
31253 this.scope = scope;
31259 focus : function(){
31261 this.el.child("button:first").focus();
31266 onClick : function(e){
31267 e.preventDefault();
31268 if(!this.disabled){
31269 if(e.getTarget(".x-btn-menu-arrow-wrap")){
31270 if(this.menu && !this.menu.isVisible()){
31271 this.menu.show(this.el, this.menuAlign);
31273 this.fireEvent("arrowclick", this, e);
31274 if(this.arrowHandler){
31275 this.arrowHandler.call(this.scope || this, this, e);
31278 this.fireEvent("click", this, e);
31280 this.handler.call(this.scope || this, this, e);
31286 onMouseDown : function(e){
31287 if(!this.disabled){
31288 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31292 onMouseUp : function(e){
31293 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31298 // backwards compat
31299 Roo.MenuButton = Roo.SplitButton;/*
31301 * Ext JS Library 1.1.1
31302 * Copyright(c) 2006-2007, Ext JS, LLC.
31304 * Originally Released Under LGPL - original licence link has changed is not relivant.
31307 * <script type="text/javascript">
31311 * @class Roo.Toolbar
31312 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31313 * Basic Toolbar class.
31315 * Creates a new Toolbar
31316 * @param {Object} container The config object
31318 Roo.Toolbar = function(container, buttons, config)
31320 /// old consturctor format still supported..
31321 if(container instanceof Array){ // omit the container for later rendering
31322 buttons = container;
31326 if (typeof(container) == 'object' && container.xtype) {
31327 config = container;
31328 container = config.container;
31329 buttons = config.buttons || []; // not really - use items!!
31332 if (config && config.items) {
31333 xitems = config.items;
31334 delete config.items;
31336 Roo.apply(this, config);
31337 this.buttons = buttons;
31340 this.render(container);
31342 this.xitems = xitems;
31343 Roo.each(xitems, function(b) {
31349 Roo.Toolbar.prototype = {
31351 * @cfg {Array} items
31352 * array of button configs or elements to add (will be converted to a MixedCollection)
31356 * @cfg {String/HTMLElement/Element} container
31357 * The id or element that will contain the toolbar
31360 render : function(ct){
31361 this.el = Roo.get(ct);
31363 this.el.addClass(this.cls);
31365 // using a table allows for vertical alignment
31366 // 100% width is needed by Safari...
31367 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31368 this.tr = this.el.child("tr", true);
31370 this.items = new Roo.util.MixedCollection(false, function(o){
31371 return o.id || ("item" + (++autoId));
31374 this.add.apply(this, this.buttons);
31375 delete this.buttons;
31380 * Adds element(s) to the toolbar -- this function takes a variable number of
31381 * arguments of mixed type and adds them to the toolbar.
31382 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31384 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31385 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31386 * <li>Field: Any form field (equivalent to {@link #addField})</li>
31387 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31388 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31389 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31390 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31391 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31392 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31394 * @param {Mixed} arg2
31395 * @param {Mixed} etc.
31398 var a = arguments, l = a.length;
31399 for(var i = 0; i < l; i++){
31404 _add : function(el) {
31407 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31410 if (el.applyTo){ // some kind of form field
31411 return this.addField(el);
31413 if (el.render){ // some kind of Toolbar.Item
31414 return this.addItem(el);
31416 if (typeof el == "string"){ // string
31417 if(el == "separator" || el == "-"){
31418 return this.addSeparator();
31421 return this.addSpacer();
31424 return this.addFill();
31426 return this.addText(el);
31429 if(el.tagName){ // element
31430 return this.addElement(el);
31432 if(typeof el == "object"){ // must be button config?
31433 return this.addButton(el);
31435 // and now what?!?!
31441 * Add an Xtype element
31442 * @param {Object} xtype Xtype Object
31443 * @return {Object} created Object
31445 addxtype : function(e){
31446 return this.add(e);
31450 * Returns the Element for this toolbar.
31451 * @return {Roo.Element}
31453 getEl : function(){
31459 * @return {Roo.Toolbar.Item} The separator item
31461 addSeparator : function(){
31462 return this.addItem(new Roo.Toolbar.Separator());
31466 * Adds a spacer element
31467 * @return {Roo.Toolbar.Spacer} The spacer item
31469 addSpacer : function(){
31470 return this.addItem(new Roo.Toolbar.Spacer());
31474 * Adds a fill element that forces subsequent additions to the right side of the toolbar
31475 * @return {Roo.Toolbar.Fill} The fill item
31477 addFill : function(){
31478 return this.addItem(new Roo.Toolbar.Fill());
31482 * Adds any standard HTML element to the toolbar
31483 * @param {String/HTMLElement/Element} el The element or id of the element to add
31484 * @return {Roo.Toolbar.Item} The element's item
31486 addElement : function(el){
31487 return this.addItem(new Roo.Toolbar.Item(el));
31490 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31491 * @type Roo.util.MixedCollection
31496 * Adds any Toolbar.Item or subclass
31497 * @param {Roo.Toolbar.Item} item
31498 * @return {Roo.Toolbar.Item} The item
31500 addItem : function(item){
31501 var td = this.nextBlock();
31503 this.items.add(item);
31508 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31509 * @param {Object/Array} config A button config or array of configs
31510 * @return {Roo.Toolbar.Button/Array}
31512 addButton : function(config){
31513 if(config instanceof Array){
31515 for(var i = 0, len = config.length; i < len; i++) {
31516 buttons.push(this.addButton(config[i]));
31521 if(!(config instanceof Roo.Toolbar.Button)){
31523 new Roo.Toolbar.SplitButton(config) :
31524 new Roo.Toolbar.Button(config);
31526 var td = this.nextBlock();
31533 * Adds text to the toolbar
31534 * @param {String} text The text to add
31535 * @return {Roo.Toolbar.Item} The element's item
31537 addText : function(text){
31538 return this.addItem(new Roo.Toolbar.TextItem(text));
31542 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31543 * @param {Number} index The index where the item is to be inserted
31544 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31545 * @return {Roo.Toolbar.Button/Item}
31547 insertButton : function(index, item){
31548 if(item instanceof Array){
31550 for(var i = 0, len = item.length; i < len; i++) {
31551 buttons.push(this.insertButton(index + i, item[i]));
31555 if (!(item instanceof Roo.Toolbar.Button)){
31556 item = new Roo.Toolbar.Button(item);
31558 var td = document.createElement("td");
31559 this.tr.insertBefore(td, this.tr.childNodes[index]);
31561 this.items.insert(index, item);
31566 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31567 * @param {Object} config
31568 * @return {Roo.Toolbar.Item} The element's item
31570 addDom : function(config, returnEl){
31571 var td = this.nextBlock();
31572 Roo.DomHelper.overwrite(td, config);
31573 var ti = new Roo.Toolbar.Item(td.firstChild);
31575 this.items.add(ti);
31580 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31581 * @type Roo.util.MixedCollection
31586 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31587 * Note: the field should not have been rendered yet. For a field that has already been
31588 * rendered, use {@link #addElement}.
31589 * @param {Roo.form.Field} field
31590 * @return {Roo.ToolbarItem}
31594 addField : function(field) {
31595 if (!this.fields) {
31597 this.fields = new Roo.util.MixedCollection(false, function(o){
31598 return o.id || ("item" + (++autoId));
31603 var td = this.nextBlock();
31605 var ti = new Roo.Toolbar.Item(td.firstChild);
31607 this.items.add(ti);
31608 this.fields.add(field);
31619 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31620 this.el.child('div').hide();
31628 this.el.child('div').show();
31632 nextBlock : function(){
31633 var td = document.createElement("td");
31634 this.tr.appendChild(td);
31639 destroy : function(){
31640 if(this.items){ // rendered?
31641 Roo.destroy.apply(Roo, this.items.items);
31643 if(this.fields){ // rendered?
31644 Roo.destroy.apply(Roo, this.fields.items);
31646 Roo.Element.uncache(this.el, this.tr);
31651 * @class Roo.Toolbar.Item
31652 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31654 * Creates a new Item
31655 * @param {HTMLElement} el
31657 Roo.Toolbar.Item = function(el){
31659 if (typeof (el.xtype) != 'undefined') {
31664 this.el = Roo.getDom(el);
31665 this.id = Roo.id(this.el);
31666 this.hidden = false;
31671 * Fires when the button is rendered
31672 * @param {Button} this
31676 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31678 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31679 //Roo.Toolbar.Item.prototype = {
31682 * Get this item's HTML Element
31683 * @return {HTMLElement}
31685 getEl : function(){
31690 render : function(td){
31693 td.appendChild(this.el);
31695 this.fireEvent('render', this);
31699 * Removes and destroys this item.
31701 destroy : function(){
31702 this.td.parentNode.removeChild(this.td);
31709 this.hidden = false;
31710 this.td.style.display = "";
31717 this.hidden = true;
31718 this.td.style.display = "none";
31722 * Convenience function for boolean show/hide.
31723 * @param {Boolean} visible true to show/false to hide
31725 setVisible: function(visible){
31734 * Try to focus this item.
31736 focus : function(){
31737 Roo.fly(this.el).focus();
31741 * Disables this item.
31743 disable : function(){
31744 Roo.fly(this.td).addClass("x-item-disabled");
31745 this.disabled = true;
31746 this.el.disabled = true;
31750 * Enables this item.
31752 enable : function(){
31753 Roo.fly(this.td).removeClass("x-item-disabled");
31754 this.disabled = false;
31755 this.el.disabled = false;
31761 * @class Roo.Toolbar.Separator
31762 * @extends Roo.Toolbar.Item
31763 * A simple toolbar separator class
31765 * Creates a new Separator
31767 Roo.Toolbar.Separator = function(cfg){
31769 var s = document.createElement("span");
31770 s.className = "ytb-sep";
31775 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31777 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31778 enable:Roo.emptyFn,
31779 disable:Roo.emptyFn,
31784 * @class Roo.Toolbar.Spacer
31785 * @extends Roo.Toolbar.Item
31786 * A simple element that adds extra horizontal space to a toolbar.
31788 * Creates a new Spacer
31790 Roo.Toolbar.Spacer = function(cfg){
31791 var s = document.createElement("div");
31792 s.className = "ytb-spacer";
31796 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31798 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31799 enable:Roo.emptyFn,
31800 disable:Roo.emptyFn,
31805 * @class Roo.Toolbar.Fill
31806 * @extends Roo.Toolbar.Spacer
31807 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31809 * Creates a new Spacer
31811 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31813 render : function(td){
31814 td.style.width = '100%';
31815 Roo.Toolbar.Fill.superclass.render.call(this, td);
31820 * @class Roo.Toolbar.TextItem
31821 * @extends Roo.Toolbar.Item
31822 * A simple class that renders text directly into a toolbar.
31824 * Creates a new TextItem
31825 * @cfg {string} text
31827 Roo.Toolbar.TextItem = function(cfg){
31828 var text = cfg || "";
31829 if (typeof(cfg) == 'object') {
31830 text = cfg.text || "";
31834 var s = document.createElement("span");
31835 s.className = "ytb-text";
31836 s.innerHTML = text;
31841 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
31843 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31846 enable:Roo.emptyFn,
31847 disable:Roo.emptyFn,
31850 * Shows this button
31853 this.hidden = false;
31854 this.el.style.display = "";
31858 * Hides this button
31861 this.hidden = true;
31862 this.el.style.display = "none";
31868 * @class Roo.Toolbar.Button
31869 * @extends Roo.Button
31870 * A button that renders into a toolbar.
31872 * Creates a new Button
31873 * @param {Object} config A standard {@link Roo.Button} config object
31875 Roo.Toolbar.Button = function(config){
31876 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31878 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31882 render : function(td){
31884 Roo.Toolbar.Button.superclass.render.call(this, td);
31888 * Removes and destroys this button
31890 destroy : function(){
31891 Roo.Toolbar.Button.superclass.destroy.call(this);
31892 this.td.parentNode.removeChild(this.td);
31896 * Shows this button
31899 this.hidden = false;
31900 this.td.style.display = "";
31904 * Hides this button
31907 this.hidden = true;
31908 this.td.style.display = "none";
31912 * Disables this item
31914 disable : function(){
31915 Roo.fly(this.td).addClass("x-item-disabled");
31916 this.disabled = true;
31920 * Enables this item
31922 enable : function(){
31923 Roo.fly(this.td).removeClass("x-item-disabled");
31924 this.disabled = false;
31927 // backwards compat
31928 Roo.ToolbarButton = Roo.Toolbar.Button;
31931 * @class Roo.Toolbar.SplitButton
31932 * @extends Roo.SplitButton
31933 * A menu button that renders into a toolbar.
31935 * Creates a new SplitButton
31936 * @param {Object} config A standard {@link Roo.SplitButton} config object
31938 Roo.Toolbar.SplitButton = function(config){
31939 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31941 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31942 render : function(td){
31944 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31948 * Removes and destroys this button
31950 destroy : function(){
31951 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31952 this.td.parentNode.removeChild(this.td);
31956 * Shows this button
31959 this.hidden = false;
31960 this.td.style.display = "";
31964 * Hides this button
31967 this.hidden = true;
31968 this.td.style.display = "none";
31972 // backwards compat
31973 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31975 * Ext JS Library 1.1.1
31976 * Copyright(c) 2006-2007, Ext JS, LLC.
31978 * Originally Released Under LGPL - original licence link has changed is not relivant.
31981 * <script type="text/javascript">
31985 * @class Roo.PagingToolbar
31986 * @extends Roo.Toolbar
31987 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31988 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31990 * Create a new PagingToolbar
31991 * @param {Object} config The config object
31993 Roo.PagingToolbar = function(el, ds, config)
31995 // old args format still supported... - xtype is prefered..
31996 if (typeof(el) == 'object' && el.xtype) {
31997 // created from xtype...
31999 ds = el.dataSource;
32000 el = config.container;
32003 if (config.items) {
32004 items = config.items;
32008 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32011 this.renderButtons(this.el);
32014 // supprot items array.
32016 Roo.each(items, function(e) {
32017 this.add(Roo.factory(e));
32022 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32025 * @cfg {String/HTMLElement/Element} container
32026 * container The id or element that will contain the toolbar
32029 * @cfg {Boolean} displayInfo
32030 * True to display the displayMsg (defaults to false)
32035 * @cfg {Number} pageSize
32036 * The number of records to display per page (defaults to 20)
32040 * @cfg {String} displayMsg
32041 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32043 displayMsg : 'Displaying {0} - {1} of {2}',
32045 * @cfg {String} emptyMsg
32046 * The message to display when no records are found (defaults to "No data to display")
32048 emptyMsg : 'No data to display',
32050 * Customizable piece of the default paging text (defaults to "Page")
32053 beforePageText : "Page",
32055 * Customizable piece of the default paging text (defaults to "of %0")
32058 afterPageText : "of {0}",
32060 * Customizable piece of the default paging text (defaults to "First Page")
32063 firstText : "First Page",
32065 * Customizable piece of the default paging text (defaults to "Previous Page")
32068 prevText : "Previous Page",
32070 * Customizable piece of the default paging text (defaults to "Next Page")
32073 nextText : "Next Page",
32075 * Customizable piece of the default paging text (defaults to "Last Page")
32078 lastText : "Last Page",
32080 * Customizable piece of the default paging text (defaults to "Refresh")
32083 refreshText : "Refresh",
32086 renderButtons : function(el){
32087 Roo.PagingToolbar.superclass.render.call(this, el);
32088 this.first = this.addButton({
32089 tooltip: this.firstText,
32090 cls: "x-btn-icon x-grid-page-first",
32092 handler: this.onClick.createDelegate(this, ["first"])
32094 this.prev = this.addButton({
32095 tooltip: this.prevText,
32096 cls: "x-btn-icon x-grid-page-prev",
32098 handler: this.onClick.createDelegate(this, ["prev"])
32100 //this.addSeparator();
32101 this.add(this.beforePageText);
32102 this.field = Roo.get(this.addDom({
32107 cls: "x-grid-page-number"
32109 this.field.on("keydown", this.onPagingKeydown, this);
32110 this.field.on("focus", function(){this.dom.select();});
32111 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32112 this.field.setHeight(18);
32113 //this.addSeparator();
32114 this.next = this.addButton({
32115 tooltip: this.nextText,
32116 cls: "x-btn-icon x-grid-page-next",
32118 handler: this.onClick.createDelegate(this, ["next"])
32120 this.last = this.addButton({
32121 tooltip: this.lastText,
32122 cls: "x-btn-icon x-grid-page-last",
32124 handler: this.onClick.createDelegate(this, ["last"])
32126 //this.addSeparator();
32127 this.loading = this.addButton({
32128 tooltip: this.refreshText,
32129 cls: "x-btn-icon x-grid-loading",
32130 handler: this.onClick.createDelegate(this, ["refresh"])
32133 if(this.displayInfo){
32134 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32139 updateInfo : function(){
32140 if(this.displayEl){
32141 var count = this.ds.getCount();
32142 var msg = count == 0 ?
32146 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32148 this.displayEl.update(msg);
32153 onLoad : function(ds, r, o){
32154 this.cursor = o.params ? o.params.start : 0;
32155 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32157 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32158 this.field.dom.value = ap;
32159 this.first.setDisabled(ap == 1);
32160 this.prev.setDisabled(ap == 1);
32161 this.next.setDisabled(ap == ps);
32162 this.last.setDisabled(ap == ps);
32163 this.loading.enable();
32168 getPageData : function(){
32169 var total = this.ds.getTotalCount();
32172 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32173 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32178 onLoadError : function(){
32179 this.loading.enable();
32183 onPagingKeydown : function(e){
32184 var k = e.getKey();
32185 var d = this.getPageData();
32187 var v = this.field.dom.value, pageNum;
32188 if(!v || isNaN(pageNum = parseInt(v, 10))){
32189 this.field.dom.value = d.activePage;
32192 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32193 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32196 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))
32198 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32199 this.field.dom.value = pageNum;
32200 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32203 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32205 var v = this.field.dom.value, pageNum;
32206 var increment = (e.shiftKey) ? 10 : 1;
32207 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32210 if(!v || isNaN(pageNum = parseInt(v, 10))) {
32211 this.field.dom.value = d.activePage;
32214 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32216 this.field.dom.value = parseInt(v, 10) + increment;
32217 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32218 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32225 beforeLoad : function(){
32227 this.loading.disable();
32232 onClick : function(which){
32236 ds.load({params:{start: 0, limit: this.pageSize}});
32239 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32242 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32245 var total = ds.getTotalCount();
32246 var extra = total % this.pageSize;
32247 var lastStart = extra ? (total - extra) : total-this.pageSize;
32248 ds.load({params:{start: lastStart, limit: this.pageSize}});
32251 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32257 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32258 * @param {Roo.data.Store} store The data store to unbind
32260 unbind : function(ds){
32261 ds.un("beforeload", this.beforeLoad, this);
32262 ds.un("load", this.onLoad, this);
32263 ds.un("loadexception", this.onLoadError, this);
32264 ds.un("remove", this.updateInfo, this);
32265 ds.un("add", this.updateInfo, this);
32266 this.ds = undefined;
32270 * Binds the paging toolbar to the specified {@link Roo.data.Store}
32271 * @param {Roo.data.Store} store The data store to bind
32273 bind : function(ds){
32274 ds.on("beforeload", this.beforeLoad, this);
32275 ds.on("load", this.onLoad, this);
32276 ds.on("loadexception", this.onLoadError, this);
32277 ds.on("remove", this.updateInfo, this);
32278 ds.on("add", this.updateInfo, this);
32283 * Ext JS Library 1.1.1
32284 * Copyright(c) 2006-2007, Ext JS, LLC.
32286 * Originally Released Under LGPL - original licence link has changed is not relivant.
32289 * <script type="text/javascript">
32293 * @class Roo.Resizable
32294 * @extends Roo.util.Observable
32295 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32296 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32297 * 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
32298 * the element will be wrapped for you automatically.</p>
32299 * <p>Here is the list of valid resize handles:</p>
32302 ------ -------------------
32311 'hd' horizontal drag
32314 * <p>Here's an example showing the creation of a typical Resizable:</p>
32316 var resizer = new Roo.Resizable("element-id", {
32324 resizer.on("resize", myHandler);
32326 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32327 * resizer.east.setDisplayed(false);</p>
32328 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32329 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32330 * resize operation's new size (defaults to [0, 0])
32331 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32332 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32333 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32334 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32335 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32336 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32337 * @cfg {Number} width The width of the element in pixels (defaults to null)
32338 * @cfg {Number} height The height of the element in pixels (defaults to null)
32339 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32340 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32341 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32342 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32343 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
32344 * in favor of the handles config option (defaults to false)
32345 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32346 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32347 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32348 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32349 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32350 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32351 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32352 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32353 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32354 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32355 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32357 * Create a new resizable component
32358 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32359 * @param {Object} config configuration options
32361 Roo.Resizable = function(el, config)
32363 this.el = Roo.get(el);
32365 if(config && config.wrap){
32366 config.resizeChild = this.el;
32367 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32368 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32369 this.el.setStyle("overflow", "hidden");
32370 this.el.setPositioning(config.resizeChild.getPositioning());
32371 config.resizeChild.clearPositioning();
32372 if(!config.width || !config.height){
32373 var csize = config.resizeChild.getSize();
32374 this.el.setSize(csize.width, csize.height);
32376 if(config.pinned && !config.adjustments){
32377 config.adjustments = "auto";
32381 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32382 this.proxy.unselectable();
32383 this.proxy.enableDisplayMode('block');
32385 Roo.apply(this, config);
32388 this.disableTrackOver = true;
32389 this.el.addClass("x-resizable-pinned");
32391 // if the element isn't positioned, make it relative
32392 var position = this.el.getStyle("position");
32393 if(position != "absolute" && position != "fixed"){
32394 this.el.setStyle("position", "relative");
32396 if(!this.handles){ // no handles passed, must be legacy style
32397 this.handles = 's,e,se';
32398 if(this.multiDirectional){
32399 this.handles += ',n,w';
32402 if(this.handles == "all"){
32403 this.handles = "n s e w ne nw se sw";
32405 var hs = this.handles.split(/\s*?[,;]\s*?| /);
32406 var ps = Roo.Resizable.positions;
32407 for(var i = 0, len = hs.length; i < len; i++){
32408 if(hs[i] && ps[hs[i]]){
32409 var pos = ps[hs[i]];
32410 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32414 this.corner = this.southeast;
32416 // updateBox = the box can move..
32417 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32418 this.updateBox = true;
32421 this.activeHandle = null;
32423 if(this.resizeChild){
32424 if(typeof this.resizeChild == "boolean"){
32425 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32427 this.resizeChild = Roo.get(this.resizeChild, true);
32431 if(this.adjustments == "auto"){
32432 var rc = this.resizeChild;
32433 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32434 if(rc && (hw || hn)){
32435 rc.position("relative");
32436 rc.setLeft(hw ? hw.el.getWidth() : 0);
32437 rc.setTop(hn ? hn.el.getHeight() : 0);
32439 this.adjustments = [
32440 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32441 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32445 if(this.draggable){
32446 this.dd = this.dynamic ?
32447 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32448 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32454 * @event beforeresize
32455 * Fired before resize is allowed. Set enabled to false to cancel resize.
32456 * @param {Roo.Resizable} this
32457 * @param {Roo.EventObject} e The mousedown event
32459 "beforeresize" : true,
32462 * Fired a resizing.
32463 * @param {Roo.Resizable} this
32464 * @param {Number} x The new x position
32465 * @param {Number} y The new y position
32466 * @param {Number} w The new w width
32467 * @param {Number} h The new h hight
32468 * @param {Roo.EventObject} e The mouseup event
32473 * Fired after a resize.
32474 * @param {Roo.Resizable} this
32475 * @param {Number} width The new width
32476 * @param {Number} height The new height
32477 * @param {Roo.EventObject} e The mouseup event
32482 if(this.width !== null && this.height !== null){
32483 this.resizeTo(this.width, this.height);
32485 this.updateChildSize();
32488 this.el.dom.style.zoom = 1;
32490 Roo.Resizable.superclass.constructor.call(this);
32493 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32494 resizeChild : false,
32495 adjustments : [0, 0],
32505 multiDirectional : false,
32506 disableTrackOver : false,
32507 easing : 'easeOutStrong',
32508 widthIncrement : 0,
32509 heightIncrement : 0,
32513 preserveRatio : false,
32514 transparent: false,
32520 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32522 constrainTo: undefined,
32524 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32526 resizeRegion: undefined,
32530 * Perform a manual resize
32531 * @param {Number} width
32532 * @param {Number} height
32534 resizeTo : function(width, height){
32535 this.el.setSize(width, height);
32536 this.updateChildSize();
32537 this.fireEvent("resize", this, width, height, null);
32541 startSizing : function(e, handle){
32542 this.fireEvent("beforeresize", this, e);
32543 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32546 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
32547 this.overlay.unselectable();
32548 this.overlay.enableDisplayMode("block");
32549 this.overlay.on("mousemove", this.onMouseMove, this);
32550 this.overlay.on("mouseup", this.onMouseUp, this);
32552 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32554 this.resizing = true;
32555 this.startBox = this.el.getBox();
32556 this.startPoint = e.getXY();
32557 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32558 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32560 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32561 this.overlay.show();
32563 if(this.constrainTo) {
32564 var ct = Roo.get(this.constrainTo);
32565 this.resizeRegion = ct.getRegion().adjust(
32566 ct.getFrameWidth('t'),
32567 ct.getFrameWidth('l'),
32568 -ct.getFrameWidth('b'),
32569 -ct.getFrameWidth('r')
32573 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32575 this.proxy.setBox(this.startBox);
32577 this.proxy.setStyle('visibility', 'visible');
32583 onMouseDown : function(handle, e){
32586 this.activeHandle = handle;
32587 this.startSizing(e, handle);
32592 onMouseUp : function(e){
32593 var size = this.resizeElement();
32594 this.resizing = false;
32596 this.overlay.hide();
32598 this.fireEvent("resize", this, size.width, size.height, e);
32602 updateChildSize : function(){
32604 if(this.resizeChild){
32606 var child = this.resizeChild;
32607 var adj = this.adjustments;
32608 if(el.dom.offsetWidth){
32609 var b = el.getSize(true);
32610 child.setSize(b.width+adj[0], b.height+adj[1]);
32612 // Second call here for IE
32613 // The first call enables instant resizing and
32614 // the second call corrects scroll bars if they
32617 setTimeout(function(){
32618 if(el.dom.offsetWidth){
32619 var b = el.getSize(true);
32620 child.setSize(b.width+adj[0], b.height+adj[1]);
32628 snap : function(value, inc, min){
32629 if(!inc || !value) {
32632 var newValue = value;
32633 var m = value % inc;
32636 newValue = value + (inc-m);
32638 newValue = value - m;
32641 return Math.max(min, newValue);
32645 resizeElement : function(){
32646 var box = this.proxy.getBox();
32647 if(this.updateBox){
32648 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32650 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32652 this.updateChildSize();
32660 constrain : function(v, diff, m, mx){
32663 }else if(v - diff > mx){
32670 onMouseMove : function(e){
32673 try{// try catch so if something goes wrong the user doesn't get hung
32675 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32679 //var curXY = this.startPoint;
32680 var curSize = this.curSize || this.startBox;
32681 var x = this.startBox.x, y = this.startBox.y;
32682 var ox = x, oy = y;
32683 var w = curSize.width, h = curSize.height;
32684 var ow = w, oh = h;
32685 var mw = this.minWidth, mh = this.minHeight;
32686 var mxw = this.maxWidth, mxh = this.maxHeight;
32687 var wi = this.widthIncrement;
32688 var hi = this.heightIncrement;
32690 var eventXY = e.getXY();
32691 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32692 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32694 var pos = this.activeHandle.position;
32699 w = Math.min(Math.max(mw, w), mxw);
32704 h = Math.min(Math.max(mh, h), mxh);
32709 w = Math.min(Math.max(mw, w), mxw);
32710 h = Math.min(Math.max(mh, h), mxh);
32713 diffY = this.constrain(h, diffY, mh, mxh);
32720 var adiffX = Math.abs(diffX);
32721 var sub = (adiffX % wi); // how much
32722 if (sub > (wi/2)) { // far enough to snap
32723 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32725 // remove difference..
32726 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32730 x = Math.max(this.minX, x);
32733 diffX = this.constrain(w, diffX, mw, mxw);
32739 w = Math.min(Math.max(mw, w), mxw);
32740 diffY = this.constrain(h, diffY, mh, mxh);
32745 diffX = this.constrain(w, diffX, mw, mxw);
32746 diffY = this.constrain(h, diffY, mh, mxh);
32753 diffX = this.constrain(w, diffX, mw, mxw);
32755 h = Math.min(Math.max(mh, h), mxh);
32761 var sw = this.snap(w, wi, mw);
32762 var sh = this.snap(h, hi, mh);
32763 if(sw != w || sh != h){
32786 if(this.preserveRatio){
32791 h = Math.min(Math.max(mh, h), mxh);
32796 w = Math.min(Math.max(mw, w), mxw);
32801 w = Math.min(Math.max(mw, w), mxw);
32807 w = Math.min(Math.max(mw, w), mxw);
32813 h = Math.min(Math.max(mh, h), mxh);
32821 h = Math.min(Math.max(mh, h), mxh);
32831 h = Math.min(Math.max(mh, h), mxh);
32839 if (pos == 'hdrag') {
32842 this.proxy.setBounds(x, y, w, h);
32844 this.resizeElement();
32848 this.fireEvent("resizing", this, x, y, w, h, e);
32852 handleOver : function(){
32854 this.el.addClass("x-resizable-over");
32859 handleOut : function(){
32860 if(!this.resizing){
32861 this.el.removeClass("x-resizable-over");
32866 * Returns the element this component is bound to.
32867 * @return {Roo.Element}
32869 getEl : function(){
32874 * Returns the resizeChild element (or null).
32875 * @return {Roo.Element}
32877 getResizeChild : function(){
32878 return this.resizeChild;
32880 groupHandler : function()
32885 * Destroys this resizable. If the element was wrapped and
32886 * removeEl is not true then the element remains.
32887 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32889 destroy : function(removeEl){
32890 this.proxy.remove();
32892 this.overlay.removeAllListeners();
32893 this.overlay.remove();
32895 var ps = Roo.Resizable.positions;
32897 if(typeof ps[k] != "function" && this[ps[k]]){
32898 var h = this[ps[k]];
32899 h.el.removeAllListeners();
32904 this.el.update("");
32911 // hash to map config positions to true positions
32912 Roo.Resizable.positions = {
32913 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
32918 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32920 // only initialize the template if resizable is used
32921 var tpl = Roo.DomHelper.createTemplate(
32922 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32925 Roo.Resizable.Handle.prototype.tpl = tpl;
32927 this.position = pos;
32929 // show north drag fro topdra
32930 var handlepos = pos == 'hdrag' ? 'north' : pos;
32932 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32933 if (pos == 'hdrag') {
32934 this.el.setStyle('cursor', 'pointer');
32936 this.el.unselectable();
32938 this.el.setOpacity(0);
32940 this.el.on("mousedown", this.onMouseDown, this);
32941 if(!disableTrackOver){
32942 this.el.on("mouseover", this.onMouseOver, this);
32943 this.el.on("mouseout", this.onMouseOut, this);
32948 Roo.Resizable.Handle.prototype = {
32949 afterResize : function(rz){
32954 onMouseDown : function(e){
32955 this.rz.onMouseDown(this, e);
32958 onMouseOver : function(e){
32959 this.rz.handleOver(this, e);
32962 onMouseOut : function(e){
32963 this.rz.handleOut(this, e);
32967 * Ext JS Library 1.1.1
32968 * Copyright(c) 2006-2007, Ext JS, LLC.
32970 * Originally Released Under LGPL - original licence link has changed is not relivant.
32973 * <script type="text/javascript">
32977 * @class Roo.Editor
32978 * @extends Roo.Component
32979 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32981 * Create a new Editor
32982 * @param {Roo.form.Field} field The Field object (or descendant)
32983 * @param {Object} config The config object
32985 Roo.Editor = function(field, config){
32986 Roo.Editor.superclass.constructor.call(this, config);
32987 this.field = field;
32990 * @event beforestartedit
32991 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
32992 * false from the handler of this event.
32993 * @param {Editor} this
32994 * @param {Roo.Element} boundEl The underlying element bound to this editor
32995 * @param {Mixed} value The field value being set
32997 "beforestartedit" : true,
33000 * Fires when this editor is displayed
33001 * @param {Roo.Element} boundEl The underlying element bound to this editor
33002 * @param {Mixed} value The starting field value
33004 "startedit" : true,
33006 * @event beforecomplete
33007 * Fires after a change has been made to the field, but before the change is reflected in the underlying
33008 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
33009 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33010 * event will not fire since no edit actually occurred.
33011 * @param {Editor} this
33012 * @param {Mixed} value The current field value
33013 * @param {Mixed} startValue The original field value
33015 "beforecomplete" : true,
33018 * Fires after editing is complete and any changed value has been written to the underlying field.
33019 * @param {Editor} this
33020 * @param {Mixed} value The current field value
33021 * @param {Mixed} startValue The original field value
33025 * @event specialkey
33026 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
33027 * {@link Roo.EventObject#getKey} to determine which key was pressed.
33028 * @param {Roo.form.Field} this
33029 * @param {Roo.EventObject} e The event object
33031 "specialkey" : true
33035 Roo.extend(Roo.Editor, Roo.Component, {
33037 * @cfg {Boolean/String} autosize
33038 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33039 * or "height" to adopt the height only (defaults to false)
33042 * @cfg {Boolean} revertInvalid
33043 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33044 * validation fails (defaults to true)
33047 * @cfg {Boolean} ignoreNoChange
33048 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33049 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
33050 * will never be ignored.
33053 * @cfg {Boolean} hideEl
33054 * False to keep the bound element visible while the editor is displayed (defaults to true)
33057 * @cfg {Mixed} value
33058 * The data value of the underlying field (defaults to "")
33062 * @cfg {String} alignment
33063 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33067 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33068 * for bottom-right shadow (defaults to "frame")
33072 * @cfg {Boolean} constrain True to constrain the editor to the viewport
33076 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33078 completeOnEnter : false,
33080 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33082 cancelOnEsc : false,
33084 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33089 onRender : function(ct, position){
33090 this.el = new Roo.Layer({
33091 shadow: this.shadow,
33097 constrain: this.constrain
33099 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33100 if(this.field.msgTarget != 'title'){
33101 this.field.msgTarget = 'qtip';
33103 this.field.render(this.el);
33105 this.field.el.dom.setAttribute('autocomplete', 'off');
33107 this.field.on("specialkey", this.onSpecialKey, this);
33108 if(this.swallowKeys){
33109 this.field.el.swallowEvent(['keydown','keypress']);
33112 this.field.on("blur", this.onBlur, this);
33113 if(this.field.grow){
33114 this.field.on("autosize", this.el.sync, this.el, {delay:1});
33118 onSpecialKey : function(field, e)
33120 //Roo.log('editor onSpecialKey');
33121 if(this.completeOnEnter && e.getKey() == e.ENTER){
33123 this.completeEdit();
33126 // do not fire special key otherwise it might hide close the editor...
33127 if(e.getKey() == e.ENTER){
33130 if(this.cancelOnEsc && e.getKey() == e.ESC){
33134 this.fireEvent('specialkey', field, e);
33139 * Starts the editing process and shows the editor.
33140 * @param {String/HTMLElement/Element} el The element to edit
33141 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33142 * to the innerHTML of el.
33144 startEdit : function(el, value){
33146 this.completeEdit();
33148 this.boundEl = Roo.get(el);
33149 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33150 if(!this.rendered){
33151 this.render(this.parentEl || document.body);
33153 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33156 this.startValue = v;
33157 this.field.setValue(v);
33159 var sz = this.boundEl.getSize();
33160 switch(this.autoSize){
33162 this.setSize(sz.width, "");
33165 this.setSize("", sz.height);
33168 this.setSize(sz.width, sz.height);
33171 this.el.alignTo(this.boundEl, this.alignment);
33172 this.editing = true;
33174 Roo.QuickTips.disable();
33180 * Sets the height and width of this editor.
33181 * @param {Number} width The new width
33182 * @param {Number} height The new height
33184 setSize : function(w, h){
33185 this.field.setSize(w, h);
33192 * Realigns the editor to the bound field based on the current alignment config value.
33194 realign : function(){
33195 this.el.alignTo(this.boundEl, this.alignment);
33199 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33200 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33202 completeEdit : function(remainVisible){
33206 var v = this.getValue();
33207 if(this.revertInvalid !== false && !this.field.isValid()){
33208 v = this.startValue;
33209 this.cancelEdit(true);
33211 if(String(v) === String(this.startValue) && this.ignoreNoChange){
33212 this.editing = false;
33216 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33217 this.editing = false;
33218 if(this.updateEl && this.boundEl){
33219 this.boundEl.update(v);
33221 if(remainVisible !== true){
33224 this.fireEvent("complete", this, v, this.startValue);
33229 onShow : function(){
33231 if(this.hideEl !== false){
33232 this.boundEl.hide();
33235 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33236 this.fixIEFocus = true;
33237 this.deferredFocus.defer(50, this);
33239 this.field.focus();
33241 this.fireEvent("startedit", this.boundEl, this.startValue);
33244 deferredFocus : function(){
33246 this.field.focus();
33251 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
33252 * reverted to the original starting value.
33253 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33254 * cancel (defaults to false)
33256 cancelEdit : function(remainVisible){
33258 this.setValue(this.startValue);
33259 if(remainVisible !== true){
33266 onBlur : function(){
33267 if(this.allowBlur !== true && this.editing){
33268 this.completeEdit();
33273 onHide : function(){
33275 this.completeEdit();
33279 if(this.field.collapse){
33280 this.field.collapse();
33283 if(this.hideEl !== false){
33284 this.boundEl.show();
33287 Roo.QuickTips.enable();
33292 * Sets the data value of the editor
33293 * @param {Mixed} value Any valid value supported by the underlying field
33295 setValue : function(v){
33296 this.field.setValue(v);
33300 * Gets the data value of the editor
33301 * @return {Mixed} The data value
33303 getValue : function(){
33304 return this.field.getValue();
33308 * Ext JS Library 1.1.1
33309 * Copyright(c) 2006-2007, Ext JS, LLC.
33311 * Originally Released Under LGPL - original licence link has changed is not relivant.
33314 * <script type="text/javascript">
33318 * @class Roo.BasicDialog
33319 * @extends Roo.util.Observable
33320 * @parent none builder
33321 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
33323 var dlg = new Roo.BasicDialog("my-dlg", {
33332 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33333 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
33334 dlg.addButton('Cancel', dlg.hide, dlg);
33337 <b>A Dialog should always be a direct child of the body element.</b>
33338 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33339 * @cfg {String} title Default text to display in the title bar (defaults to null)
33340 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33341 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33342 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33343 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33344 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33345 * (defaults to null with no animation)
33346 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33347 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33348 * property for valid values (defaults to 'all')
33349 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33350 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33351 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33352 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33353 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33354 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33355 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33356 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33357 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33358 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33359 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33360 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33361 * draggable = true (defaults to false)
33362 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33363 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33364 * shadow (defaults to false)
33365 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33366 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33367 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33368 * @cfg {Array} buttons Array of buttons
33369 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33371 * Create a new BasicDialog.
33372 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33373 * @param {Object} config Configuration options
33375 Roo.BasicDialog = function(el, config){
33376 this.el = Roo.get(el);
33377 var dh = Roo.DomHelper;
33378 if(!this.el && config && config.autoCreate){
33379 if(typeof config.autoCreate == "object"){
33380 if(!config.autoCreate.id){
33381 config.autoCreate.id = el;
33383 this.el = dh.append(document.body,
33384 config.autoCreate, true);
33386 this.el = dh.append(document.body,
33387 {tag: "div", id: el, style:'visibility:hidden;'}, true);
33391 el.setDisplayed(true);
33392 el.hide = this.hideAction;
33394 el.addClass("x-dlg");
33396 Roo.apply(this, config);
33398 this.proxy = el.createProxy("x-dlg-proxy");
33399 this.proxy.hide = this.hideAction;
33400 this.proxy.setOpacity(.5);
33404 el.setWidth(config.width);
33407 el.setHeight(config.height);
33409 this.size = el.getSize();
33410 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33411 this.xy = [config.x,config.y];
33413 this.xy = el.getCenterXY(true);
33415 /** The header element @type Roo.Element */
33416 this.header = el.child("> .x-dlg-hd");
33417 /** The body element @type Roo.Element */
33418 this.body = el.child("> .x-dlg-bd");
33419 /** The footer element @type Roo.Element */
33420 this.footer = el.child("> .x-dlg-ft");
33423 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
33426 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33429 this.header.unselectable();
33431 this.header.update(this.title);
33433 // this element allows the dialog to be focused for keyboard event
33434 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33435 this.focusEl.swallowEvent("click", true);
33437 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33439 // wrap the body and footer for special rendering
33440 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33442 this.bwrap.dom.appendChild(this.footer.dom);
33445 this.bg = this.el.createChild({
33446 tag: "div", cls:"x-dlg-bg",
33447 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
33449 this.centerBg = this.bg.child("div.x-dlg-bg-center");
33452 if(this.autoScroll !== false && !this.autoTabs){
33453 this.body.setStyle("overflow", "auto");
33456 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33458 if(this.closable !== false){
33459 this.el.addClass("x-dlg-closable");
33460 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33461 this.close.on("click", this.closeClick, this);
33462 this.close.addClassOnOver("x-dlg-close-over");
33464 if(this.collapsible !== false){
33465 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33466 this.collapseBtn.on("click", this.collapseClick, this);
33467 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33468 this.header.on("dblclick", this.collapseClick, this);
33470 if(this.resizable !== false){
33471 this.el.addClass("x-dlg-resizable");
33472 this.resizer = new Roo.Resizable(el, {
33473 minWidth: this.minWidth || 80,
33474 minHeight:this.minHeight || 80,
33475 handles: this.resizeHandles || "all",
33478 this.resizer.on("beforeresize", this.beforeResize, this);
33479 this.resizer.on("resize", this.onResize, this);
33481 if(this.draggable !== false){
33482 el.addClass("x-dlg-draggable");
33483 if (!this.proxyDrag) {
33484 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33487 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33489 dd.setHandleElId(this.header.id);
33490 dd.endDrag = this.endMove.createDelegate(this);
33491 dd.startDrag = this.startMove.createDelegate(this);
33492 dd.onDrag = this.onDrag.createDelegate(this);
33497 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33498 this.mask.enableDisplayMode("block");
33500 this.el.addClass("x-dlg-modal");
33503 this.shadow = new Roo.Shadow({
33504 mode : typeof this.shadow == "string" ? this.shadow : "sides",
33505 offset : this.shadowOffset
33508 this.shadowOffset = 0;
33510 if(Roo.useShims && this.shim !== false){
33511 this.shim = this.el.createShim();
33512 this.shim.hide = this.hideAction;
33520 if (this.buttons) {
33521 var bts= this.buttons;
33523 Roo.each(bts, function(b) {
33532 * Fires when a key is pressed
33533 * @param {Roo.BasicDialog} this
33534 * @param {Roo.EventObject} e
33539 * Fires when this dialog is moved by the user.
33540 * @param {Roo.BasicDialog} this
33541 * @param {Number} x The new page X
33542 * @param {Number} y The new page Y
33547 * Fires when this dialog is resized by the user.
33548 * @param {Roo.BasicDialog} this
33549 * @param {Number} width The new width
33550 * @param {Number} height The new height
33554 * @event beforehide
33555 * Fires before this dialog is hidden.
33556 * @param {Roo.BasicDialog} this
33558 "beforehide" : true,
33561 * Fires when this dialog is hidden.
33562 * @param {Roo.BasicDialog} this
33566 * @event beforeshow
33567 * Fires before this dialog is shown.
33568 * @param {Roo.BasicDialog} this
33570 "beforeshow" : true,
33573 * Fires when this dialog is shown.
33574 * @param {Roo.BasicDialog} this
33578 el.on("keydown", this.onKeyDown, this);
33579 el.on("mousedown", this.toFront, this);
33580 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33582 Roo.DialogManager.register(this);
33583 Roo.BasicDialog.superclass.constructor.call(this);
33586 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33587 shadowOffset: Roo.isIE ? 6 : 5,
33590 minButtonWidth: 75,
33591 defaultButton: null,
33592 buttonAlign: "right",
33597 * Sets the dialog title text
33598 * @param {String} text The title text to display
33599 * @return {Roo.BasicDialog} this
33601 setTitle : function(text){
33602 this.header.update(text);
33607 closeClick : function(){
33612 collapseClick : function(){
33613 this[this.collapsed ? "expand" : "collapse"]();
33617 * Collapses the dialog to its minimized state (only the title bar is visible).
33618 * Equivalent to the user clicking the collapse dialog button.
33620 collapse : function(){
33621 if(!this.collapsed){
33622 this.collapsed = true;
33623 this.el.addClass("x-dlg-collapsed");
33624 this.restoreHeight = this.el.getHeight();
33625 this.resizeTo(this.el.getWidth(), this.header.getHeight());
33630 * Expands a collapsed dialog back to its normal state. Equivalent to the user
33631 * clicking the expand dialog button.
33633 expand : function(){
33634 if(this.collapsed){
33635 this.collapsed = false;
33636 this.el.removeClass("x-dlg-collapsed");
33637 this.resizeTo(this.el.getWidth(), this.restoreHeight);
33642 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33643 * @return {Roo.TabPanel} The tabs component
33645 initTabs : function(){
33646 var tabs = this.getTabs();
33647 while(tabs.getTab(0)){
33650 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33652 tabs.addTab(Roo.id(dom), dom.title);
33660 beforeResize : function(){
33661 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33665 onResize : function(){
33666 this.refreshSize();
33667 this.syncBodyHeight();
33668 this.adjustAssets();
33670 this.fireEvent("resize", this, this.size.width, this.size.height);
33674 onKeyDown : function(e){
33675 if(this.isVisible()){
33676 this.fireEvent("keydown", this, e);
33681 * Resizes the dialog.
33682 * @param {Number} width
33683 * @param {Number} height
33684 * @return {Roo.BasicDialog} this
33686 resizeTo : function(width, height){
33687 this.el.setSize(width, height);
33688 this.size = {width: width, height: height};
33689 this.syncBodyHeight();
33690 if(this.fixedcenter){
33693 if(this.isVisible()){
33694 this.constrainXY();
33695 this.adjustAssets();
33697 this.fireEvent("resize", this, width, height);
33703 * Resizes the dialog to fit the specified content size.
33704 * @param {Number} width
33705 * @param {Number} height
33706 * @return {Roo.BasicDialog} this
33708 setContentSize : function(w, h){
33709 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33710 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33711 //if(!this.el.isBorderBox()){
33712 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33713 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33716 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33717 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33719 this.resizeTo(w, h);
33724 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
33725 * executed in response to a particular key being pressed while the dialog is active.
33726 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33727 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33728 * @param {Function} fn The function to call
33729 * @param {Object} scope (optional) The scope of the function
33730 * @return {Roo.BasicDialog} this
33732 addKeyListener : function(key, fn, scope){
33733 var keyCode, shift, ctrl, alt;
33734 if(typeof key == "object" && !(key instanceof Array)){
33735 keyCode = key["key"];
33736 shift = key["shift"];
33737 ctrl = key["ctrl"];
33742 var handler = function(dlg, e){
33743 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
33744 var k = e.getKey();
33745 if(keyCode instanceof Array){
33746 for(var i = 0, len = keyCode.length; i < len; i++){
33747 if(keyCode[i] == k){
33748 fn.call(scope || window, dlg, k, e);
33754 fn.call(scope || window, dlg, k, e);
33759 this.on("keydown", handler);
33764 * Returns the TabPanel component (creates it if it doesn't exist).
33765 * Note: If you wish to simply check for the existence of tabs without creating them,
33766 * check for a null 'tabs' property.
33767 * @return {Roo.TabPanel} The tabs component
33769 getTabs : function(){
33771 this.el.addClass("x-dlg-auto-tabs");
33772 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33773 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33779 * Adds a button to the footer section of the dialog.
33780 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33781 * object or a valid Roo.DomHelper element config
33782 * @param {Function} handler The function called when the button is clicked
33783 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33784 * @return {Roo.Button} The new button
33786 addButton : function(config, handler, scope){
33787 var dh = Roo.DomHelper;
33789 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33791 if(!this.btnContainer){
33792 var tb = this.footer.createChild({
33794 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33795 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33797 this.btnContainer = tb.firstChild.firstChild.firstChild;
33802 minWidth: this.minButtonWidth,
33805 if(typeof config == "string"){
33806 bconfig.text = config;
33809 bconfig.dhconfig = config;
33811 Roo.apply(bconfig, config);
33815 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33816 bconfig.position = Math.max(0, bconfig.position);
33817 fc = this.btnContainer.childNodes[bconfig.position];
33820 var btn = new Roo.Button(
33822 this.btnContainer.insertBefore(document.createElement("td"),fc)
33823 : this.btnContainer.appendChild(document.createElement("td")),
33824 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
33827 this.syncBodyHeight();
33830 * Array of all the buttons that have been added to this dialog via addButton
33835 this.buttons.push(btn);
33840 * Sets the default button to be focused when the dialog is displayed.
33841 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33842 * @return {Roo.BasicDialog} this
33844 setDefaultButton : function(btn){
33845 this.defaultButton = btn;
33850 getHeaderFooterHeight : function(safe){
33853 height += this.header.getHeight();
33856 var fm = this.footer.getMargins();
33857 height += (this.footer.getHeight()+fm.top+fm.bottom);
33859 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33860 height += this.centerBg.getPadding("tb");
33865 syncBodyHeight : function()
33867 var bd = this.body, // the text
33868 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33870 var height = this.size.height - this.getHeaderFooterHeight(false);
33871 bd.setHeight(height-bd.getMargins("tb"));
33872 var hh = this.header.getHeight();
33873 var h = this.size.height-hh;
33876 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33877 bw.setHeight(h-cb.getPadding("tb"));
33879 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33880 bd.setWidth(bw.getWidth(true));
33882 this.tabs.syncHeight();
33884 this.tabs.el.repaint();
33890 * Restores the previous state of the dialog if Roo.state is configured.
33891 * @return {Roo.BasicDialog} this
33893 restoreState : function(){
33894 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33895 if(box && box.width){
33896 this.xy = [box.x, box.y];
33897 this.resizeTo(box.width, box.height);
33903 beforeShow : function(){
33905 if(this.fixedcenter){
33906 this.xy = this.el.getCenterXY(true);
33909 Roo.get(document.body).addClass("x-body-masked");
33910 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33913 this.constrainXY();
33917 animShow : function(){
33918 var b = Roo.get(this.animateTarget).getBox();
33919 this.proxy.setSize(b.width, b.height);
33920 this.proxy.setLocation(b.x, b.y);
33922 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33923 true, .35, this.showEl.createDelegate(this));
33927 * Shows the dialog.
33928 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33929 * @return {Roo.BasicDialog} this
33931 show : function(animateTarget){
33932 if (this.fireEvent("beforeshow", this) === false){
33935 if(this.syncHeightBeforeShow){
33936 this.syncBodyHeight();
33937 }else if(this.firstShow){
33938 this.firstShow = false;
33939 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33941 this.animateTarget = animateTarget || this.animateTarget;
33942 if(!this.el.isVisible()){
33944 if(this.animateTarget && Roo.get(this.animateTarget)){
33954 showEl : function(){
33956 this.el.setXY(this.xy);
33958 this.adjustAssets(true);
33961 // IE peekaboo bug - fix found by Dave Fenwick
33965 this.fireEvent("show", this);
33969 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
33970 * dialog itself will receive focus.
33972 focus : function(){
33973 if(this.defaultButton){
33974 this.defaultButton.focus();
33976 this.focusEl.focus();
33981 constrainXY : function(){
33982 if(this.constraintoviewport !== false){
33983 if(!this.viewSize){
33984 if(this.container){
33985 var s = this.container.getSize();
33986 this.viewSize = [s.width, s.height];
33988 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33991 var s = Roo.get(this.container||document).getScroll();
33993 var x = this.xy[0], y = this.xy[1];
33994 var w = this.size.width, h = this.size.height;
33995 var vw = this.viewSize[0], vh = this.viewSize[1];
33996 // only move it if it needs it
33998 // first validate right/bottom
33999 if(x + w > vw+s.left){
34003 if(y + h > vh+s.top){
34007 // then make sure top/left isn't negative
34019 if(this.isVisible()){
34020 this.el.setLocation(x, y);
34021 this.adjustAssets();
34028 onDrag : function(){
34029 if(!this.proxyDrag){
34030 this.xy = this.el.getXY();
34031 this.adjustAssets();
34036 adjustAssets : function(doShow){
34037 var x = this.xy[0], y = this.xy[1];
34038 var w = this.size.width, h = this.size.height;
34039 if(doShow === true){
34041 this.shadow.show(this.el);
34047 if(this.shadow && this.shadow.isVisible()){
34048 this.shadow.show(this.el);
34050 if(this.shim && this.shim.isVisible()){
34051 this.shim.setBounds(x, y, w, h);
34056 adjustViewport : function(w, h){
34058 w = Roo.lib.Dom.getViewWidth();
34059 h = Roo.lib.Dom.getViewHeight();
34062 this.viewSize = [w, h];
34063 if(this.modal && this.mask.isVisible()){
34064 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34065 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34067 if(this.isVisible()){
34068 this.constrainXY();
34073 * Destroys this dialog and all its supporting elements (including any tabs, shim,
34074 * shadow, proxy, mask, etc.) Also removes all event listeners.
34075 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34077 destroy : function(removeEl){
34078 if(this.isVisible()){
34079 this.animateTarget = null;
34082 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34084 this.tabs.destroy(removeEl);
34097 for(var i = 0, len = this.buttons.length; i < len; i++){
34098 this.buttons[i].destroy();
34101 this.el.removeAllListeners();
34102 if(removeEl === true){
34103 this.el.update("");
34106 Roo.DialogManager.unregister(this);
34110 startMove : function(){
34111 if(this.proxyDrag){
34114 if(this.constraintoviewport !== false){
34115 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34120 endMove : function(){
34121 if(!this.proxyDrag){
34122 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34124 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34127 this.refreshSize();
34128 this.adjustAssets();
34130 this.fireEvent("move", this, this.xy[0], this.xy[1]);
34134 * Brings this dialog to the front of any other visible dialogs
34135 * @return {Roo.BasicDialog} this
34137 toFront : function(){
34138 Roo.DialogManager.bringToFront(this);
34143 * Sends this dialog to the back (under) of any other visible dialogs
34144 * @return {Roo.BasicDialog} this
34146 toBack : function(){
34147 Roo.DialogManager.sendToBack(this);
34152 * Centers this dialog in the viewport
34153 * @return {Roo.BasicDialog} this
34155 center : function(){
34156 var xy = this.el.getCenterXY(true);
34157 this.moveTo(xy[0], xy[1]);
34162 * Moves the dialog's top-left corner to the specified point
34163 * @param {Number} x
34164 * @param {Number} y
34165 * @return {Roo.BasicDialog} this
34167 moveTo : function(x, y){
34169 if(this.isVisible()){
34170 this.el.setXY(this.xy);
34171 this.adjustAssets();
34177 * Aligns the dialog to the specified element
34178 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34179 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34180 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34181 * @return {Roo.BasicDialog} this
34183 alignTo : function(element, position, offsets){
34184 this.xy = this.el.getAlignToXY(element, position, offsets);
34185 if(this.isVisible()){
34186 this.el.setXY(this.xy);
34187 this.adjustAssets();
34193 * Anchors an element to another element and realigns it when the window is resized.
34194 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34195 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34196 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34197 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34198 * is a number, it is used as the buffer delay (defaults to 50ms).
34199 * @return {Roo.BasicDialog} this
34201 anchorTo : function(el, alignment, offsets, monitorScroll){
34202 var action = function(){
34203 this.alignTo(el, alignment, offsets);
34205 Roo.EventManager.onWindowResize(action, this);
34206 var tm = typeof monitorScroll;
34207 if(tm != 'undefined'){
34208 Roo.EventManager.on(window, 'scroll', action, this,
34209 {buffer: tm == 'number' ? monitorScroll : 50});
34216 * Returns true if the dialog is visible
34217 * @return {Boolean}
34219 isVisible : function(){
34220 return this.el.isVisible();
34224 animHide : function(callback){
34225 var b = Roo.get(this.animateTarget).getBox();
34227 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34229 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34230 this.hideEl.createDelegate(this, [callback]));
34234 * Hides the dialog.
34235 * @param {Function} callback (optional) Function to call when the dialog is hidden
34236 * @return {Roo.BasicDialog} this
34238 hide : function(callback){
34239 if (this.fireEvent("beforehide", this) === false){
34243 this.shadow.hide();
34248 // sometimes animateTarget seems to get set.. causing problems...
34249 // this just double checks..
34250 if(this.animateTarget && Roo.get(this.animateTarget)) {
34251 this.animHide(callback);
34254 this.hideEl(callback);
34260 hideEl : function(callback){
34264 Roo.get(document.body).removeClass("x-body-masked");
34266 this.fireEvent("hide", this);
34267 if(typeof callback == "function"){
34273 hideAction : function(){
34274 this.setLeft("-10000px");
34275 this.setTop("-10000px");
34276 this.setStyle("visibility", "hidden");
34280 refreshSize : function(){
34281 this.size = this.el.getSize();
34282 this.xy = this.el.getXY();
34283 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34287 // z-index is managed by the DialogManager and may be overwritten at any time
34288 setZIndex : function(index){
34290 this.mask.setStyle("z-index", index);
34293 this.shim.setStyle("z-index", ++index);
34296 this.shadow.setZIndex(++index);
34298 this.el.setStyle("z-index", ++index);
34300 this.proxy.setStyle("z-index", ++index);
34303 this.resizer.proxy.setStyle("z-index", ++index);
34306 this.lastZIndex = index;
34310 * Returns the element for this dialog
34311 * @return {Roo.Element} The underlying dialog Element
34313 getEl : function(){
34319 * @class Roo.DialogManager
34320 * Provides global access to BasicDialogs that have been created and
34321 * support for z-indexing (layering) multiple open dialogs.
34323 Roo.DialogManager = function(){
34325 var accessList = [];
34329 var sortDialogs = function(d1, d2){
34330 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34334 var orderDialogs = function(){
34335 accessList.sort(sortDialogs);
34336 var seed = Roo.DialogManager.zseed;
34337 for(var i = 0, len = accessList.length; i < len; i++){
34338 var dlg = accessList[i];
34340 dlg.setZIndex(seed + (i*10));
34347 * The starting z-index for BasicDialogs (defaults to 9000)
34348 * @type Number The z-index value
34353 register : function(dlg){
34354 list[dlg.id] = dlg;
34355 accessList.push(dlg);
34359 unregister : function(dlg){
34360 delete list[dlg.id];
34363 if(!accessList.indexOf){
34364 for( i = 0, len = accessList.length; i < len; i++){
34365 if(accessList[i] == dlg){
34366 accessList.splice(i, 1);
34371 i = accessList.indexOf(dlg);
34373 accessList.splice(i, 1);
34379 * Gets a registered dialog by id
34380 * @param {String/Object} id The id of the dialog or a dialog
34381 * @return {Roo.BasicDialog} this
34383 get : function(id){
34384 return typeof id == "object" ? id : list[id];
34388 * Brings the specified dialog to the front
34389 * @param {String/Object} dlg The id of the dialog or a dialog
34390 * @return {Roo.BasicDialog} this
34392 bringToFront : function(dlg){
34393 dlg = this.get(dlg);
34396 dlg._lastAccess = new Date().getTime();
34403 * Sends the specified dialog to the back
34404 * @param {String/Object} dlg The id of the dialog or a dialog
34405 * @return {Roo.BasicDialog} this
34407 sendToBack : function(dlg){
34408 dlg = this.get(dlg);
34409 dlg._lastAccess = -(new Date().getTime());
34415 * Hides all dialogs
34417 hideAll : function(){
34418 for(var id in list){
34419 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34428 * @class Roo.LayoutDialog
34429 * @extends Roo.BasicDialog
34430 * @children Roo.ContentPanel
34431 * @parent builder none
34432 * Dialog which provides adjustments for working with a layout in a Dialog.
34433 * Add your necessary layout config options to the dialog's config.<br>
34434 * Example usage (including a nested layout):
34437 dialog = new Roo.LayoutDialog("download-dlg", {
34446 // layout config merges with the dialog config
34448 tabPosition: "top",
34449 alwaysShowTabs: true
34452 dialog.addKeyListener(27, dialog.hide, dialog);
34453 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34454 dialog.addButton("Build It!", this.getDownload, this);
34456 // we can even add nested layouts
34457 var innerLayout = new Roo.BorderLayout("dl-inner", {
34467 innerLayout.beginUpdate();
34468 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34469 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34470 innerLayout.endUpdate(true);
34472 var layout = dialog.getLayout();
34473 layout.beginUpdate();
34474 layout.add("center", new Roo.ContentPanel("standard-panel",
34475 {title: "Download the Source", fitToFrame:true}));
34476 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34477 {title: "Build your own roo.js"}));
34478 layout.getRegion("center").showPanel(sp);
34479 layout.endUpdate();
34483 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34484 * @param {Object} config configuration options
34486 Roo.LayoutDialog = function(el, cfg){
34489 if (typeof(cfg) == 'undefined') {
34490 config = Roo.apply({}, el);
34491 // not sure why we use documentElement here.. - it should always be body.
34492 // IE7 borks horribly if we use documentElement.
34493 // webkit also does not like documentElement - it creates a body element...
34494 el = Roo.get( document.body || document.documentElement ).createChild();
34495 //config.autoCreate = true;
34499 config.autoTabs = false;
34500 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34501 this.body.setStyle({overflow:"hidden", position:"relative"});
34502 this.layout = new Roo.BorderLayout(this.body.dom, config);
34503 this.layout.monitorWindowResize = false;
34504 this.el.addClass("x-dlg-auto-layout");
34505 // fix case when center region overwrites center function
34506 this.center = Roo.BasicDialog.prototype.center;
34507 this.on("show", this.layout.layout, this.layout, true);
34508 if (config.items) {
34509 var xitems = config.items;
34510 delete config.items;
34511 Roo.each(xitems, this.addxtype, this);
34516 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34520 * @cfg {Roo.LayoutRegion} east
34523 * @cfg {Roo.LayoutRegion} west
34526 * @cfg {Roo.LayoutRegion} south
34529 * @cfg {Roo.LayoutRegion} north
34532 * @cfg {Roo.LayoutRegion} center
34535 * @cfg {Roo.Button} buttons[] Bottom buttons..
34540 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34543 endUpdate : function(){
34544 this.layout.endUpdate();
34548 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34551 beginUpdate : function(){
34552 this.layout.beginUpdate();
34556 * Get the BorderLayout for this dialog
34557 * @return {Roo.BorderLayout}
34559 getLayout : function(){
34560 return this.layout;
34563 showEl : function(){
34564 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34566 this.layout.layout();
34571 // Use the syncHeightBeforeShow config option to control this automatically
34572 syncBodyHeight : function(){
34573 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34574 if(this.layout){this.layout.layout();}
34578 * Add an xtype element (actually adds to the layout.)
34579 * @return {Object} xdata xtype object data.
34582 addxtype : function(c) {
34583 return this.layout.addxtype(c);
34587 * Ext JS Library 1.1.1
34588 * Copyright(c) 2006-2007, Ext JS, LLC.
34590 * Originally Released Under LGPL - original licence link has changed is not relivant.
34593 * <script type="text/javascript">
34597 * @class Roo.MessageBox
34598 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
34602 Roo.Msg.alert('Status', 'Changes saved successfully.');
34604 // Prompt for user data:
34605 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34607 // process text value...
34611 // Show a dialog using config options:
34613 title:'Save Changes?',
34614 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34615 buttons: Roo.Msg.YESNOCANCEL,
34622 Roo.MessageBox = function(){
34623 var dlg, opt, mask, waitTimer;
34624 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34625 var buttons, activeTextEl, bwidth;
34628 var handleButton = function(button){
34630 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34634 var handleHide = function(){
34635 if(opt && opt.cls){
34636 dlg.el.removeClass(opt.cls);
34639 Roo.TaskMgr.stop(waitTimer);
34645 var updateButtons = function(b){
34648 buttons["ok"].hide();
34649 buttons["cancel"].hide();
34650 buttons["yes"].hide();
34651 buttons["no"].hide();
34652 dlg.footer.dom.style.display = 'none';
34655 dlg.footer.dom.style.display = '';
34656 for(var k in buttons){
34657 if(typeof buttons[k] != "function"){
34660 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34661 width += buttons[k].el.getWidth()+15;
34671 var handleEsc = function(d, k, e){
34672 if(opt && opt.closable !== false){
34682 * Returns a reference to the underlying {@link Roo.BasicDialog} element
34683 * @return {Roo.BasicDialog} The BasicDialog element
34685 getDialog : function(){
34687 dlg = new Roo.BasicDialog("x-msg-box", {
34692 constraintoviewport:false,
34694 collapsible : false,
34697 width:400, height:100,
34698 buttonAlign:"center",
34699 closeClick : function(){
34700 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34701 handleButton("no");
34703 handleButton("cancel");
34707 dlg.on("hide", handleHide);
34709 dlg.addKeyListener(27, handleEsc);
34711 var bt = this.buttonText;
34712 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34713 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34714 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34715 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34716 bodyEl = dlg.body.createChild({
34718 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>'
34720 msgEl = bodyEl.dom.firstChild;
34721 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34722 textboxEl.enableDisplayMode();
34723 textboxEl.addKeyListener([10,13], function(){
34724 if(dlg.isVisible() && opt && opt.buttons){
34725 if(opt.buttons.ok){
34726 handleButton("ok");
34727 }else if(opt.buttons.yes){
34728 handleButton("yes");
34732 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34733 textareaEl.enableDisplayMode();
34734 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34735 progressEl.enableDisplayMode();
34736 var pf = progressEl.dom.firstChild;
34738 pp = Roo.get(pf.firstChild);
34739 pp.setHeight(pf.offsetHeight);
34747 * Updates the message box body text
34748 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34749 * the XHTML-compliant non-breaking space character '&#160;')
34750 * @return {Roo.MessageBox} This message box
34752 updateText : function(text){
34753 if(!dlg.isVisible() && !opt.width){
34754 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34756 msgEl.innerHTML = text || ' ';
34758 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34759 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34761 Math.min(opt.width || cw , this.maxWidth),
34762 Math.max(opt.minWidth || this.minWidth, bwidth)
34765 activeTextEl.setWidth(w);
34767 if(dlg.isVisible()){
34768 dlg.fixedcenter = false;
34770 // to big, make it scroll. = But as usual stupid IE does not support
34773 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34774 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34775 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34777 bodyEl.dom.style.height = '';
34778 bodyEl.dom.style.overflowY = '';
34781 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34783 bodyEl.dom.style.overflowX = '';
34786 dlg.setContentSize(w, bodyEl.getHeight());
34787 if(dlg.isVisible()){
34788 dlg.fixedcenter = true;
34794 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
34795 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34796 * @param {Number} value Any number between 0 and 1 (e.g., .5)
34797 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34798 * @return {Roo.MessageBox} This message box
34800 updateProgress : function(value, text){
34802 this.updateText(text);
34804 if (pp) { // weird bug on my firefox - for some reason this is not defined
34805 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34811 * Returns true if the message box is currently displayed
34812 * @return {Boolean} True if the message box is visible, else false
34814 isVisible : function(){
34815 return dlg && dlg.isVisible();
34819 * Hides the message box if it is displayed
34822 if(this.isVisible()){
34828 * Displays a new message box, or reinitializes an existing message box, based on the config options
34829 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34830 * The following config object properties are supported:
34832 Property Type Description
34833 ---------- --------------- ------------------------------------------------------------------------------------
34834 animEl String/Element An id or Element from which the message box should animate as it opens and
34835 closes (defaults to undefined)
34836 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34837 cancel:'Bar'}), or false to not show any buttons (defaults to false)
34838 closable Boolean False to hide the top-right close button (defaults to true). Note that
34839 progress and wait dialogs will ignore this property and always hide the
34840 close button as they can only be closed programmatically.
34841 cls String A custom CSS class to apply to the message box element
34842 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
34843 displayed (defaults to 75)
34844 fn Function A callback function to execute after closing the dialog. The arguments to the
34845 function will be btn (the name of the button that was clicked, if applicable,
34846 e.g. "ok"), and text (the value of the active text field, if applicable).
34847 Progress and wait dialogs will ignore this option since they do not respond to
34848 user actions and can only be closed programmatically, so any required function
34849 should be called by the same code after it closes the dialog.
34850 icon String A CSS class that provides a background image to be used as an icon for
34851 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34852 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
34853 minWidth Number The minimum width in pixels of the message box (defaults to 100)
34854 modal Boolean False to allow user interaction with the page while the message box is
34855 displayed (defaults to true)
34856 msg String A string that will replace the existing message box body text (defaults
34857 to the XHTML-compliant non-breaking space character ' ')
34858 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
34859 progress Boolean True to display a progress bar (defaults to false)
34860 progressText String The text to display inside the progress bar if progress = true (defaults to '')
34861 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
34862 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
34863 title String The title text
34864 value String The string value to set into the active textbox element if displayed
34865 wait Boolean True to display a progress bar (defaults to false)
34866 width Number The width of the dialog in pixels
34873 msg: 'Please enter your address:',
34875 buttons: Roo.MessageBox.OKCANCEL,
34878 animEl: 'addAddressBtn'
34881 * @param {Object} config Configuration options
34882 * @return {Roo.MessageBox} This message box
34884 show : function(options)
34887 // this causes nightmares if you show one dialog after another
34888 // especially on callbacks..
34890 if(this.isVisible()){
34893 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34894 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
34895 Roo.log("New Dialog Message:" + options.msg )
34896 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34897 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34900 var d = this.getDialog();
34902 d.setTitle(opt.title || " ");
34903 d.close.setDisplayed(opt.closable !== false);
34904 activeTextEl = textboxEl;
34905 opt.prompt = opt.prompt || (opt.multiline ? true : false);
34910 textareaEl.setHeight(typeof opt.multiline == "number" ?
34911 opt.multiline : this.defaultTextHeight);
34912 activeTextEl = textareaEl;
34921 progressEl.setDisplayed(opt.progress === true);
34922 this.updateProgress(0);
34923 activeTextEl.dom.value = opt.value || "";
34925 dlg.setDefaultButton(activeTextEl);
34927 var bs = opt.buttons;
34930 db = buttons["ok"];
34931 }else if(bs && bs.yes){
34932 db = buttons["yes"];
34934 dlg.setDefaultButton(db);
34936 bwidth = updateButtons(opt.buttons);
34937 this.updateText(opt.msg);
34939 d.el.addClass(opt.cls);
34941 d.proxyDrag = opt.proxyDrag === true;
34942 d.modal = opt.modal !== false;
34943 d.mask = opt.modal !== false ? mask : false;
34944 if(!d.isVisible()){
34945 // force it to the end of the z-index stack so it gets a cursor in FF
34946 document.body.appendChild(dlg.el.dom);
34947 d.animateTarget = null;
34948 d.show(options.animEl);
34954 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
34955 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34956 * and closing the message box when the process is complete.
34957 * @param {String} title The title bar text
34958 * @param {String} msg The message box body text
34959 * @return {Roo.MessageBox} This message box
34961 progress : function(title, msg){
34968 minWidth: this.minProgressWidth,
34975 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34976 * If a callback function is passed it will be called after the user clicks the button, and the
34977 * id of the button that was clicked will be passed as the only parameter to the callback
34978 * (could also be the top-right close button).
34979 * @param {String} title The title bar text
34980 * @param {String} msg The message box body text
34981 * @param {Function} fn (optional) The callback function invoked after the message box is closed
34982 * @param {Object} scope (optional) The scope of the callback function
34983 * @return {Roo.MessageBox} This message box
34985 alert : function(title, msg, fn, scope){
34998 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
34999 * interaction while waiting for a long-running process to complete that does not have defined intervals.
35000 * You are responsible for closing the message box when the process is complete.
35001 * @param {String} msg The message box body text
35002 * @param {String} title (optional) The title bar text
35003 * @return {Roo.MessageBox} This message box
35005 wait : function(msg, title){
35016 waitTimer = Roo.TaskMgr.start({
35018 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35026 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35027 * If a callback function is passed it will be called after the user clicks either button, and the id of the
35028 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35029 * @param {String} title The title bar text
35030 * @param {String} msg The message box body text
35031 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35032 * @param {Object} scope (optional) The scope of the callback function
35033 * @return {Roo.MessageBox} This message box
35035 confirm : function(title, msg, fn, scope){
35039 buttons: this.YESNO,
35048 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35049 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
35050 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35051 * (could also be the top-right close button) and the text that was entered will be passed as the two
35052 * parameters to the callback.
35053 * @param {String} title The title bar text
35054 * @param {String} msg The message box body text
35055 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35056 * @param {Object} scope (optional) The scope of the callback function
35057 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35058 * property, or the height in pixels to create the textbox (defaults to false / single-line)
35059 * @return {Roo.MessageBox} This message box
35061 prompt : function(title, msg, fn, scope, multiline){
35065 buttons: this.OKCANCEL,
35070 multiline: multiline,
35077 * Button config that displays a single OK button
35082 * Button config that displays Yes and No buttons
35085 YESNO : {yes:true, no:true},
35087 * Button config that displays OK and Cancel buttons
35090 OKCANCEL : {ok:true, cancel:true},
35092 * Button config that displays Yes, No and Cancel buttons
35095 YESNOCANCEL : {yes:true, no:true, cancel:true},
35098 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35101 defaultTextHeight : 75,
35103 * The maximum width in pixels of the message box (defaults to 600)
35108 * The minimum width in pixels of the message box (defaults to 100)
35113 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
35114 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35117 minProgressWidth : 250,
35119 * An object containing the default button text strings that can be overriden for localized language support.
35120 * Supported properties are: ok, cancel, yes and no.
35121 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35134 * Shorthand for {@link Roo.MessageBox}
35136 Roo.Msg = Roo.MessageBox;/*
35138 * Ext JS Library 1.1.1
35139 * Copyright(c) 2006-2007, Ext JS, LLC.
35141 * Originally Released Under LGPL - original licence link has changed is not relivant.
35144 * <script type="text/javascript">
35147 * @class Roo.QuickTips
35148 * Provides attractive and customizable tooltips for any element.
35151 Roo.QuickTips = function(){
35152 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35153 var ce, bd, xy, dd;
35154 var visible = false, disabled = true, inited = false;
35155 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35157 var onOver = function(e){
35161 var t = e.getTarget();
35162 if(!t || t.nodeType !== 1 || t == document || t == document.body){
35165 if(ce && t == ce.el){
35166 clearTimeout(hideProc);
35169 if(t && tagEls[t.id]){
35170 tagEls[t.id].el = t;
35171 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35174 var ttp, et = Roo.fly(t);
35175 var ns = cfg.namespace;
35176 if(tm.interceptTitles && t.title){
35179 t.removeAttribute("title");
35180 e.preventDefault();
35182 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35185 showProc = show.defer(tm.showDelay, tm, [{
35187 text: ttp.replace(/\\n/g,'<br/>'),
35188 width: et.getAttributeNS(ns, cfg.width),
35189 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35190 title: et.getAttributeNS(ns, cfg.title),
35191 cls: et.getAttributeNS(ns, cfg.cls)
35196 var onOut = function(e){
35197 clearTimeout(showProc);
35198 var t = e.getTarget();
35199 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35200 hideProc = setTimeout(hide, tm.hideDelay);
35204 var onMove = function(e){
35210 if(tm.trackMouse && ce){
35215 var onDown = function(e){
35216 clearTimeout(showProc);
35217 clearTimeout(hideProc);
35219 if(tm.hideOnClick){
35222 tm.enable.defer(100, tm);
35227 var getPad = function(){
35228 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35231 var show = function(o){
35235 clearTimeout(dismissProc);
35237 if(removeCls){ // in case manually hidden
35238 el.removeClass(removeCls);
35242 el.addClass(ce.cls);
35243 removeCls = ce.cls;
35246 tipTitle.update(ce.title);
35249 tipTitle.update('');
35252 el.dom.style.width = tm.maxWidth+'px';
35253 //tipBody.dom.style.width = '';
35254 tipBodyText.update(o.text);
35255 var p = getPad(), w = ce.width;
35257 var td = tipBodyText.dom;
35258 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35259 if(aw > tm.maxWidth){
35261 }else if(aw < tm.minWidth){
35267 //tipBody.setWidth(w);
35268 el.setWidth(parseInt(w, 10) + p);
35269 if(ce.autoHide === false){
35270 close.setDisplayed(true);
35275 close.setDisplayed(false);
35281 el.avoidY = xy[1]-18;
35286 el.setStyle("visibility", "visible");
35287 el.fadeIn({callback: afterShow});
35293 var afterShow = function(){
35297 if(tm.autoDismiss && ce.autoHide !== false){
35298 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35303 var hide = function(noanim){
35304 clearTimeout(dismissProc);
35305 clearTimeout(hideProc);
35307 if(el.isVisible()){
35309 if(noanim !== true && tm.animate){
35310 el.fadeOut({callback: afterHide});
35317 var afterHide = function(){
35320 el.removeClass(removeCls);
35327 * @cfg {Number} minWidth
35328 * The minimum width of the quick tip (defaults to 40)
35332 * @cfg {Number} maxWidth
35333 * The maximum width of the quick tip (defaults to 300)
35337 * @cfg {Boolean} interceptTitles
35338 * True to automatically use the element's DOM title value if available (defaults to false)
35340 interceptTitles : false,
35342 * @cfg {Boolean} trackMouse
35343 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35345 trackMouse : false,
35347 * @cfg {Boolean} hideOnClick
35348 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35350 hideOnClick : true,
35352 * @cfg {Number} showDelay
35353 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35357 * @cfg {Number} hideDelay
35358 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35362 * @cfg {Boolean} autoHide
35363 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35364 * Used in conjunction with hideDelay.
35369 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35370 * (defaults to true). Used in conjunction with autoDismissDelay.
35372 autoDismiss : true,
35375 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35377 autoDismissDelay : 5000,
35379 * @cfg {Boolean} animate
35380 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35385 * @cfg {String} title
35386 * Title text to display (defaults to ''). This can be any valid HTML markup.
35390 * @cfg {String} text
35391 * Body text to display (defaults to ''). This can be any valid HTML markup.
35395 * @cfg {String} cls
35396 * A CSS class to apply to the base quick tip element (defaults to '').
35400 * @cfg {Number} width
35401 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
35402 * minWidth or maxWidth.
35407 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
35408 * or display QuickTips in a page.
35411 tm = Roo.QuickTips;
35412 cfg = tm.tagConfig;
35414 if(!Roo.isReady){ // allow calling of init() before onReady
35415 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35418 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35419 el.fxDefaults = {stopFx: true};
35420 // maximum custom styling
35421 //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>');
35422 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>');
35423 tipTitle = el.child('h3');
35424 tipTitle.enableDisplayMode("block");
35425 tipBody = el.child('div.x-tip-bd');
35426 tipBodyText = el.child('div.x-tip-bd-inner');
35427 //bdLeft = el.child('div.x-tip-bd-left');
35428 //bdRight = el.child('div.x-tip-bd-right');
35429 close = el.child('div.x-tip-close');
35430 close.enableDisplayMode("block");
35431 close.on("click", hide);
35432 var d = Roo.get(document);
35433 d.on("mousedown", onDown);
35434 d.on("mouseover", onOver);
35435 d.on("mouseout", onOut);
35436 d.on("mousemove", onMove);
35437 esc = d.addKeyListener(27, hide);
35440 dd = el.initDD("default", null, {
35441 onDrag : function(){
35445 dd.setHandleElId(tipTitle.id);
35454 * Configures a new quick tip instance and assigns it to a target element. The following config options
35457 Property Type Description
35458 ---------- --------------------- ------------------------------------------------------------------------
35459 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
35461 * @param {Object} config The config object
35463 register : function(config){
35464 var cs = config instanceof Array ? config : arguments;
35465 for(var i = 0, len = cs.length; i < len; i++) {
35467 var target = c.target;
35469 if(target instanceof Array){
35470 for(var j = 0, jlen = target.length; j < jlen; j++){
35471 tagEls[target[j]] = c;
35474 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35481 * Removes this quick tip from its element and destroys it.
35482 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35484 unregister : function(el){
35485 delete tagEls[Roo.id(el)];
35489 * Enable this quick tip.
35491 enable : function(){
35492 if(inited && disabled){
35494 if(locks.length < 1){
35501 * Disable this quick tip.
35503 disable : function(){
35505 clearTimeout(showProc);
35506 clearTimeout(hideProc);
35507 clearTimeout(dismissProc);
35515 * Returns true if the quick tip is enabled, else false.
35517 isEnabled : function(){
35523 namespace : "roo", // was ext?? this may break..
35524 alt_namespace : "ext",
35525 attribute : "qtip",
35535 // backwards compat
35536 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35538 * Ext JS Library 1.1.1
35539 * Copyright(c) 2006-2007, Ext JS, LLC.
35541 * Originally Released Under LGPL - original licence link has changed is not relivant.
35544 * <script type="text/javascript">
35549 * @class Roo.tree.TreePanel
35550 * @extends Roo.data.Tree
35551 * @cfg {Roo.tree.TreeNode} root The root node
35552 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35553 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35554 * @cfg {Boolean} enableDD true to enable drag and drop
35555 * @cfg {Boolean} enableDrag true to enable just drag
35556 * @cfg {Boolean} enableDrop true to enable just drop
35557 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35558 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35559 * @cfg {String} ddGroup The DD group this TreePanel belongs to
35560 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35561 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35562 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35563 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35564 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35565 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35566 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35567 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35568 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35569 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35570 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35571 * @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>
35572 * @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>
35575 * @param {String/HTMLElement/Element} el The container element
35576 * @param {Object} config
35578 Roo.tree.TreePanel = function(el, config){
35580 var loader = false;
35582 root = config.root;
35583 delete config.root;
35585 if (config.loader) {
35586 loader = config.loader;
35587 delete config.loader;
35590 Roo.apply(this, config);
35591 Roo.tree.TreePanel.superclass.constructor.call(this);
35592 this.el = Roo.get(el);
35593 this.el.addClass('x-tree');
35594 //console.log(root);
35596 this.setRootNode( Roo.factory(root, Roo.tree));
35599 this.loader = Roo.factory(loader, Roo.tree);
35602 * Read-only. The id of the container element becomes this TreePanel's id.
35604 this.id = this.el.id;
35607 * @event beforeload
35608 * Fires before a node is loaded, return false to cancel
35609 * @param {Node} node The node being loaded
35611 "beforeload" : true,
35614 * Fires when a node is loaded
35615 * @param {Node} node The node that was loaded
35619 * @event textchange
35620 * Fires when the text for a node is changed
35621 * @param {Node} node The node
35622 * @param {String} text The new text
35623 * @param {String} oldText The old text
35625 "textchange" : true,
35627 * @event beforeexpand
35628 * Fires before a node is expanded, return false to cancel.
35629 * @param {Node} node The node
35630 * @param {Boolean} deep
35631 * @param {Boolean} anim
35633 "beforeexpand" : true,
35635 * @event beforecollapse
35636 * Fires before a node is collapsed, return false to cancel.
35637 * @param {Node} node The node
35638 * @param {Boolean} deep
35639 * @param {Boolean} anim
35641 "beforecollapse" : true,
35644 * Fires when a node is expanded
35645 * @param {Node} node The node
35649 * @event disabledchange
35650 * Fires when the disabled status of a node changes
35651 * @param {Node} node The node
35652 * @param {Boolean} disabled
35654 "disabledchange" : true,
35657 * Fires when a node is collapsed
35658 * @param {Node} node The node
35662 * @event beforeclick
35663 * Fires before click processing on a node. Return false to cancel the default action.
35664 * @param {Node} node The node
35665 * @param {Roo.EventObject} e The event object
35667 "beforeclick":true,
35669 * @event checkchange
35670 * Fires when a node with a checkbox's checked property changes
35671 * @param {Node} this This node
35672 * @param {Boolean} checked
35674 "checkchange":true,
35677 * Fires when a node is clicked
35678 * @param {Node} node The node
35679 * @param {Roo.EventObject} e The event object
35684 * Fires when a node is double clicked
35685 * @param {Node} node The node
35686 * @param {Roo.EventObject} e The event object
35690 * @event contextmenu
35691 * Fires when a node is right clicked
35692 * @param {Node} node The node
35693 * @param {Roo.EventObject} e The event object
35695 "contextmenu":true,
35697 * @event beforechildrenrendered
35698 * Fires right before the child nodes for a node are rendered
35699 * @param {Node} node The node
35701 "beforechildrenrendered":true,
35704 * Fires when a node starts being dragged
35705 * @param {Roo.tree.TreePanel} this
35706 * @param {Roo.tree.TreeNode} node
35707 * @param {event} e The raw browser event
35709 "startdrag" : true,
35712 * Fires when a drag operation is complete
35713 * @param {Roo.tree.TreePanel} this
35714 * @param {Roo.tree.TreeNode} node
35715 * @param {event} e The raw browser event
35720 * Fires when a dragged node is dropped on a valid DD target
35721 * @param {Roo.tree.TreePanel} this
35722 * @param {Roo.tree.TreeNode} node
35723 * @param {DD} dd The dd it was dropped on
35724 * @param {event} e The raw browser event
35728 * @event beforenodedrop
35729 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35730 * passed to handlers has the following properties:<br />
35731 * <ul style="padding:5px;padding-left:16px;">
35732 * <li>tree - The TreePanel</li>
35733 * <li>target - The node being targeted for the drop</li>
35734 * <li>data - The drag data from the drag source</li>
35735 * <li>point - The point of the drop - append, above or below</li>
35736 * <li>source - The drag source</li>
35737 * <li>rawEvent - Raw mouse event</li>
35738 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35739 * to be inserted by setting them on this object.</li>
35740 * <li>cancel - Set this to true to cancel the drop.</li>
35742 * @param {Object} dropEvent
35744 "beforenodedrop" : true,
35747 * Fires after a DD object is dropped on a node in this tree. The dropEvent
35748 * passed to handlers has the following properties:<br />
35749 * <ul style="padding:5px;padding-left:16px;">
35750 * <li>tree - The TreePanel</li>
35751 * <li>target - The node being targeted for the drop</li>
35752 * <li>data - The drag data from the drag source</li>
35753 * <li>point - The point of the drop - append, above or below</li>
35754 * <li>source - The drag source</li>
35755 * <li>rawEvent - Raw mouse event</li>
35756 * <li>dropNode - Dropped node(s).</li>
35758 * @param {Object} dropEvent
35762 * @event nodedragover
35763 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35764 * passed to handlers has the following properties:<br />
35765 * <ul style="padding:5px;padding-left:16px;">
35766 * <li>tree - The TreePanel</li>
35767 * <li>target - The node being targeted for the drop</li>
35768 * <li>data - The drag data from the drag source</li>
35769 * <li>point - The point of the drop - append, above or below</li>
35770 * <li>source - The drag source</li>
35771 * <li>rawEvent - Raw mouse event</li>
35772 * <li>dropNode - Drop node(s) provided by the source.</li>
35773 * <li>cancel - Set this to true to signal drop not allowed.</li>
35775 * @param {Object} dragOverEvent
35777 "nodedragover" : true,
35779 * @event appendnode
35780 * Fires when append node to the tree
35781 * @param {Roo.tree.TreePanel} this
35782 * @param {Roo.tree.TreeNode} node
35783 * @param {Number} index The index of the newly appended node
35785 "appendnode" : true
35788 if(this.singleExpand){
35789 this.on("beforeexpand", this.restrictExpand, this);
35792 this.editor.tree = this;
35793 this.editor = Roo.factory(this.editor, Roo.tree);
35796 if (this.selModel) {
35797 this.selModel = Roo.factory(this.selModel, Roo.tree);
35801 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35802 rootVisible : true,
35803 animate: Roo.enableFx,
35806 hlDrop : Roo.enableFx,
35810 rendererTip: false,
35812 restrictExpand : function(node){
35813 var p = node.parentNode;
35815 if(p.expandedChild && p.expandedChild.parentNode == p){
35816 p.expandedChild.collapse();
35818 p.expandedChild = node;
35822 // private override
35823 setRootNode : function(node){
35824 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35825 if(!this.rootVisible){
35826 node.ui = new Roo.tree.RootTreeNodeUI(node);
35832 * Returns the container element for this TreePanel
35834 getEl : function(){
35839 * Returns the default TreeLoader for this TreePanel
35841 getLoader : function(){
35842 return this.loader;
35848 expandAll : function(){
35849 this.root.expand(true);
35853 * Collapse all nodes
35855 collapseAll : function(){
35856 this.root.collapse(true);
35860 * Returns the selection model used by this TreePanel
35862 getSelectionModel : function(){
35863 if(!this.selModel){
35864 this.selModel = new Roo.tree.DefaultSelectionModel();
35866 return this.selModel;
35870 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35871 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35872 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35875 getChecked : function(a, startNode){
35876 startNode = startNode || this.root;
35878 var f = function(){
35879 if(this.attributes.checked){
35880 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35883 startNode.cascade(f);
35888 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35889 * @param {String} path
35890 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35891 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35892 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35894 expandPath : function(path, attr, callback){
35895 attr = attr || "id";
35896 var keys = path.split(this.pathSeparator);
35897 var curNode = this.root;
35898 if(curNode.attributes[attr] != keys[1]){ // invalid root
35900 callback(false, null);
35905 var f = function(){
35906 if(++index == keys.length){
35908 callback(true, curNode);
35912 var c = curNode.findChild(attr, keys[index]);
35915 callback(false, curNode);
35920 c.expand(false, false, f);
35922 curNode.expand(false, false, f);
35926 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35927 * @param {String} path
35928 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35929 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35930 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35932 selectPath : function(path, attr, callback){
35933 attr = attr || "id";
35934 var keys = path.split(this.pathSeparator);
35935 var v = keys.pop();
35936 if(keys.length > 0){
35937 var f = function(success, node){
35938 if(success && node){
35939 var n = node.findChild(attr, v);
35945 }else if(callback){
35946 callback(false, n);
35950 callback(false, n);
35954 this.expandPath(keys.join(this.pathSeparator), attr, f);
35956 this.root.select();
35958 callback(true, this.root);
35963 getTreeEl : function(){
35968 * Trigger rendering of this TreePanel
35970 render : function(){
35971 if (this.innerCt) {
35972 return this; // stop it rendering more than once!!
35975 this.innerCt = this.el.createChild({tag:"ul",
35976 cls:"x-tree-root-ct " +
35977 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35979 if(this.containerScroll){
35980 Roo.dd.ScrollManager.register(this.el);
35982 if((this.enableDD || this.enableDrop) && !this.dropZone){
35984 * The dropZone used by this tree if drop is enabled
35985 * @type Roo.tree.TreeDropZone
35987 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35988 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35991 if((this.enableDD || this.enableDrag) && !this.dragZone){
35993 * The dragZone used by this tree if drag is enabled
35994 * @type Roo.tree.TreeDragZone
35996 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35997 ddGroup: this.ddGroup || "TreeDD",
35998 scroll: this.ddScroll
36001 this.getSelectionModel().init(this);
36003 Roo.log("ROOT not set in tree");
36006 this.root.render();
36007 if(!this.rootVisible){
36008 this.root.renderChildren();
36014 * Ext JS Library 1.1.1
36015 * Copyright(c) 2006-2007, Ext JS, LLC.
36017 * Originally Released Under LGPL - original licence link has changed is not relivant.
36020 * <script type="text/javascript">
36025 * @class Roo.tree.DefaultSelectionModel
36026 * @extends Roo.util.Observable
36027 * The default single selection for a TreePanel.
36028 * @param {Object} cfg Configuration
36030 Roo.tree.DefaultSelectionModel = function(cfg){
36031 this.selNode = null;
36037 * @event selectionchange
36038 * Fires when the selected node changes
36039 * @param {DefaultSelectionModel} this
36040 * @param {TreeNode} node the new selection
36042 "selectionchange" : true,
36045 * @event beforeselect
36046 * Fires before the selected node changes, return false to cancel the change
36047 * @param {DefaultSelectionModel} this
36048 * @param {TreeNode} node the new selection
36049 * @param {TreeNode} node the old selection
36051 "beforeselect" : true
36054 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36057 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36058 init : function(tree){
36060 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36061 tree.on("click", this.onNodeClick, this);
36064 onNodeClick : function(node, e){
36065 if (e.ctrlKey && this.selNode == node) {
36066 this.unselect(node);
36074 * @param {TreeNode} node The node to select
36075 * @return {TreeNode} The selected node
36077 select : function(node){
36078 var last = this.selNode;
36079 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36081 last.ui.onSelectedChange(false);
36083 this.selNode = node;
36084 node.ui.onSelectedChange(true);
36085 this.fireEvent("selectionchange", this, node, last);
36092 * @param {TreeNode} node The node to unselect
36094 unselect : function(node){
36095 if(this.selNode == node){
36096 this.clearSelections();
36101 * Clear all selections
36103 clearSelections : function(){
36104 var n = this.selNode;
36106 n.ui.onSelectedChange(false);
36107 this.selNode = null;
36108 this.fireEvent("selectionchange", this, null);
36114 * Get the selected node
36115 * @return {TreeNode} The selected node
36117 getSelectedNode : function(){
36118 return this.selNode;
36122 * Returns true if the node is selected
36123 * @param {TreeNode} node The node to check
36124 * @return {Boolean}
36126 isSelected : function(node){
36127 return this.selNode == node;
36131 * Selects the node above the selected node in the tree, intelligently walking the nodes
36132 * @return TreeNode The new selection
36134 selectPrevious : function(){
36135 var s = this.selNode || this.lastSelNode;
36139 var ps = s.previousSibling;
36141 if(!ps.isExpanded() || ps.childNodes.length < 1){
36142 return this.select(ps);
36144 var lc = ps.lastChild;
36145 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36148 return this.select(lc);
36150 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36151 return this.select(s.parentNode);
36157 * Selects the node above the selected node in the tree, intelligently walking the nodes
36158 * @return TreeNode The new selection
36160 selectNext : function(){
36161 var s = this.selNode || this.lastSelNode;
36165 if(s.firstChild && s.isExpanded()){
36166 return this.select(s.firstChild);
36167 }else if(s.nextSibling){
36168 return this.select(s.nextSibling);
36169 }else if(s.parentNode){
36171 s.parentNode.bubble(function(){
36172 if(this.nextSibling){
36173 newS = this.getOwnerTree().selModel.select(this.nextSibling);
36182 onKeyDown : function(e){
36183 var s = this.selNode || this.lastSelNode;
36184 // undesirable, but required
36189 var k = e.getKey();
36197 this.selectPrevious();
36200 e.preventDefault();
36201 if(s.hasChildNodes()){
36202 if(!s.isExpanded()){
36204 }else if(s.firstChild){
36205 this.select(s.firstChild, e);
36210 e.preventDefault();
36211 if(s.hasChildNodes() && s.isExpanded()){
36213 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36214 this.select(s.parentNode, e);
36222 * @class Roo.tree.MultiSelectionModel
36223 * @extends Roo.util.Observable
36224 * Multi selection for a TreePanel.
36225 * @param {Object} cfg Configuration
36227 Roo.tree.MultiSelectionModel = function(){
36228 this.selNodes = [];
36232 * @event selectionchange
36233 * Fires when the selected nodes change
36234 * @param {MultiSelectionModel} this
36235 * @param {Array} nodes Array of the selected nodes
36237 "selectionchange" : true
36239 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36243 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36244 init : function(tree){
36246 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36247 tree.on("click", this.onNodeClick, this);
36250 onNodeClick : function(node, e){
36251 this.select(node, e, e.ctrlKey);
36256 * @param {TreeNode} node The node to select
36257 * @param {EventObject} e (optional) An event associated with the selection
36258 * @param {Boolean} keepExisting True to retain existing selections
36259 * @return {TreeNode} The selected node
36261 select : function(node, e, keepExisting){
36262 if(keepExisting !== true){
36263 this.clearSelections(true);
36265 if(this.isSelected(node)){
36266 this.lastSelNode = node;
36269 this.selNodes.push(node);
36270 this.selMap[node.id] = node;
36271 this.lastSelNode = node;
36272 node.ui.onSelectedChange(true);
36273 this.fireEvent("selectionchange", this, this.selNodes);
36279 * @param {TreeNode} node The node to unselect
36281 unselect : function(node){
36282 if(this.selMap[node.id]){
36283 node.ui.onSelectedChange(false);
36284 var sn = this.selNodes;
36287 index = sn.indexOf(node);
36289 for(var i = 0, len = sn.length; i < len; i++){
36297 this.selNodes.splice(index, 1);
36299 delete this.selMap[node.id];
36300 this.fireEvent("selectionchange", this, this.selNodes);
36305 * Clear all selections
36307 clearSelections : function(suppressEvent){
36308 var sn = this.selNodes;
36310 for(var i = 0, len = sn.length; i < len; i++){
36311 sn[i].ui.onSelectedChange(false);
36313 this.selNodes = [];
36315 if(suppressEvent !== true){
36316 this.fireEvent("selectionchange", this, this.selNodes);
36322 * Returns true if the node is selected
36323 * @param {TreeNode} node The node to check
36324 * @return {Boolean}
36326 isSelected : function(node){
36327 return this.selMap[node.id] ? true : false;
36331 * Returns an array of the selected nodes
36334 getSelectedNodes : function(){
36335 return this.selNodes;
36338 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36340 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36342 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36345 * Ext JS Library 1.1.1
36346 * Copyright(c) 2006-2007, Ext JS, LLC.
36348 * Originally Released Under LGPL - original licence link has changed is not relivant.
36351 * <script type="text/javascript">
36355 * @class Roo.tree.TreeNode
36356 * @extends Roo.data.Node
36357 * @cfg {String} text The text for this node
36358 * @cfg {Boolean} expanded true to start the node expanded
36359 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36360 * @cfg {Boolean} allowDrop false if this node cannot be drop on
36361 * @cfg {Boolean} disabled true to start the node disabled
36362 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36363 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
36364 * @cfg {String} cls A css class to be added to the node
36365 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36366 * @cfg {String} href URL of the link used for the node (defaults to #)
36367 * @cfg {String} hrefTarget target frame for the link
36368 * @cfg {String} qtip An Ext QuickTip for the node
36369 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36370 * @cfg {Boolean} singleClickExpand True for single click expand on this node
36371 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36372 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36373 * (defaults to undefined with no checkbox rendered)
36375 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36377 Roo.tree.TreeNode = function(attributes){
36378 attributes = attributes || {};
36379 if(typeof attributes == "string"){
36380 attributes = {text: attributes};
36382 this.childrenRendered = false;
36383 this.rendered = false;
36384 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36385 this.expanded = attributes.expanded === true;
36386 this.isTarget = attributes.isTarget !== false;
36387 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36388 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36391 * Read-only. The text for this node. To change it use setText().
36394 this.text = attributes.text;
36396 * True if this node is disabled.
36399 this.disabled = attributes.disabled === true;
36403 * @event textchange
36404 * Fires when the text for this node is changed
36405 * @param {Node} this This node
36406 * @param {String} text The new text
36407 * @param {String} oldText The old text
36409 "textchange" : true,
36411 * @event beforeexpand
36412 * Fires before this node is expanded, return false to cancel.
36413 * @param {Node} this This node
36414 * @param {Boolean} deep
36415 * @param {Boolean} anim
36417 "beforeexpand" : true,
36419 * @event beforecollapse
36420 * Fires before this node is collapsed, return false to cancel.
36421 * @param {Node} this This node
36422 * @param {Boolean} deep
36423 * @param {Boolean} anim
36425 "beforecollapse" : true,
36428 * Fires when this node is expanded
36429 * @param {Node} this This node
36433 * @event disabledchange
36434 * Fires when the disabled status of this node changes
36435 * @param {Node} this This node
36436 * @param {Boolean} disabled
36438 "disabledchange" : true,
36441 * Fires when this node is collapsed
36442 * @param {Node} this This node
36446 * @event beforeclick
36447 * Fires before click processing. Return false to cancel the default action.
36448 * @param {Node} this This node
36449 * @param {Roo.EventObject} e The event object
36451 "beforeclick":true,
36453 * @event checkchange
36454 * Fires when a node with a checkbox's checked property changes
36455 * @param {Node} this This node
36456 * @param {Boolean} checked
36458 "checkchange":true,
36461 * Fires when this node is clicked
36462 * @param {Node} this This node
36463 * @param {Roo.EventObject} e The event object
36468 * Fires when this node is double clicked
36469 * @param {Node} this This node
36470 * @param {Roo.EventObject} e The event object
36474 * @event contextmenu
36475 * Fires when this node is right clicked
36476 * @param {Node} this This node
36477 * @param {Roo.EventObject} e The event object
36479 "contextmenu":true,
36481 * @event beforechildrenrendered
36482 * Fires right before the child nodes for this node are rendered
36483 * @param {Node} this This node
36485 "beforechildrenrendered":true
36488 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36491 * Read-only. The UI for this node
36494 this.ui = new uiClass(this);
36496 // finally support items[]
36497 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36502 Roo.each(this.attributes.items, function(c) {
36503 this.appendChild(Roo.factory(c,Roo.Tree));
36505 delete this.attributes.items;
36510 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36511 preventHScroll: true,
36513 * Returns true if this node is expanded
36514 * @return {Boolean}
36516 isExpanded : function(){
36517 return this.expanded;
36521 * Returns the UI object for this node
36522 * @return {TreeNodeUI}
36524 getUI : function(){
36528 // private override
36529 setFirstChild : function(node){
36530 var of = this.firstChild;
36531 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36532 if(this.childrenRendered && of && node != of){
36533 of.renderIndent(true, true);
36536 this.renderIndent(true, true);
36540 // private override
36541 setLastChild : function(node){
36542 var ol = this.lastChild;
36543 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36544 if(this.childrenRendered && ol && node != ol){
36545 ol.renderIndent(true, true);
36548 this.renderIndent(true, true);
36552 // these methods are overridden to provide lazy rendering support
36553 // private override
36554 appendChild : function()
36556 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36557 if(node && this.childrenRendered){
36560 this.ui.updateExpandIcon();
36564 // private override
36565 removeChild : function(node){
36566 this.ownerTree.getSelectionModel().unselect(node);
36567 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36568 // if it's been rendered remove dom node
36569 if(this.childrenRendered){
36572 if(this.childNodes.length < 1){
36573 this.collapse(false, false);
36575 this.ui.updateExpandIcon();
36577 if(!this.firstChild) {
36578 this.childrenRendered = false;
36583 // private override
36584 insertBefore : function(node, refNode){
36585 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36586 if(newNode && refNode && this.childrenRendered){
36589 this.ui.updateExpandIcon();
36594 * Sets the text for this node
36595 * @param {String} text
36597 setText : function(text){
36598 var oldText = this.text;
36600 this.attributes.text = text;
36601 if(this.rendered){ // event without subscribing
36602 this.ui.onTextChange(this, text, oldText);
36604 this.fireEvent("textchange", this, text, oldText);
36608 * Triggers selection of this node
36610 select : function(){
36611 this.getOwnerTree().getSelectionModel().select(this);
36615 * Triggers deselection of this node
36617 unselect : function(){
36618 this.getOwnerTree().getSelectionModel().unselect(this);
36622 * Returns true if this node is selected
36623 * @return {Boolean}
36625 isSelected : function(){
36626 return this.getOwnerTree().getSelectionModel().isSelected(this);
36630 * Expand this node.
36631 * @param {Boolean} deep (optional) True to expand all children as well
36632 * @param {Boolean} anim (optional) false to cancel the default animation
36633 * @param {Function} callback (optional) A callback to be called when
36634 * expanding this node completes (does not wait for deep expand to complete).
36635 * Called with 1 parameter, this node.
36637 expand : function(deep, anim, callback){
36638 if(!this.expanded){
36639 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36642 if(!this.childrenRendered){
36643 this.renderChildren();
36645 this.expanded = true;
36647 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36648 this.ui.animExpand(function(){
36649 this.fireEvent("expand", this);
36650 if(typeof callback == "function"){
36654 this.expandChildNodes(true);
36656 }.createDelegate(this));
36660 this.fireEvent("expand", this);
36661 if(typeof callback == "function"){
36666 if(typeof callback == "function"){
36671 this.expandChildNodes(true);
36675 isHiddenRoot : function(){
36676 return this.isRoot && !this.getOwnerTree().rootVisible;
36680 * Collapse this node.
36681 * @param {Boolean} deep (optional) True to collapse all children as well
36682 * @param {Boolean} anim (optional) false to cancel the default animation
36684 collapse : function(deep, anim){
36685 if(this.expanded && !this.isHiddenRoot()){
36686 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36689 this.expanded = false;
36690 if((this.getOwnerTree().animate && anim !== false) || anim){
36691 this.ui.animCollapse(function(){
36692 this.fireEvent("collapse", this);
36694 this.collapseChildNodes(true);
36696 }.createDelegate(this));
36699 this.ui.collapse();
36700 this.fireEvent("collapse", this);
36704 var cs = this.childNodes;
36705 for(var i = 0, len = cs.length; i < len; i++) {
36706 cs[i].collapse(true, false);
36712 delayedExpand : function(delay){
36713 if(!this.expandProcId){
36714 this.expandProcId = this.expand.defer(delay, this);
36719 cancelExpand : function(){
36720 if(this.expandProcId){
36721 clearTimeout(this.expandProcId);
36723 this.expandProcId = false;
36727 * Toggles expanded/collapsed state of the node
36729 toggle : function(){
36738 * Ensures all parent nodes are expanded
36740 ensureVisible : function(callback){
36741 var tree = this.getOwnerTree();
36742 tree.expandPath(this.parentNode.getPath(), false, function(){
36743 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36744 Roo.callback(callback);
36745 }.createDelegate(this));
36749 * Expand all child nodes
36750 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36752 expandChildNodes : function(deep){
36753 var cs = this.childNodes;
36754 for(var i = 0, len = cs.length; i < len; i++) {
36755 cs[i].expand(deep);
36760 * Collapse all child nodes
36761 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36763 collapseChildNodes : function(deep){
36764 var cs = this.childNodes;
36765 for(var i = 0, len = cs.length; i < len; i++) {
36766 cs[i].collapse(deep);
36771 * Disables this node
36773 disable : function(){
36774 this.disabled = true;
36776 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36777 this.ui.onDisableChange(this, true);
36779 this.fireEvent("disabledchange", this, true);
36783 * Enables this node
36785 enable : function(){
36786 this.disabled = false;
36787 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36788 this.ui.onDisableChange(this, false);
36790 this.fireEvent("disabledchange", this, false);
36794 renderChildren : function(suppressEvent){
36795 if(suppressEvent !== false){
36796 this.fireEvent("beforechildrenrendered", this);
36798 var cs = this.childNodes;
36799 for(var i = 0, len = cs.length; i < len; i++){
36800 cs[i].render(true);
36802 this.childrenRendered = true;
36806 sort : function(fn, scope){
36807 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36808 if(this.childrenRendered){
36809 var cs = this.childNodes;
36810 for(var i = 0, len = cs.length; i < len; i++){
36811 cs[i].render(true);
36817 render : function(bulkRender){
36818 this.ui.render(bulkRender);
36819 if(!this.rendered){
36820 this.rendered = true;
36822 this.expanded = false;
36823 this.expand(false, false);
36829 renderIndent : function(deep, refresh){
36831 this.ui.childIndent = null;
36833 this.ui.renderIndent();
36834 if(deep === true && this.childrenRendered){
36835 var cs = this.childNodes;
36836 for(var i = 0, len = cs.length; i < len; i++){
36837 cs[i].renderIndent(true, refresh);
36843 * Ext JS Library 1.1.1
36844 * Copyright(c) 2006-2007, Ext JS, LLC.
36846 * Originally Released Under LGPL - original licence link has changed is not relivant.
36849 * <script type="text/javascript">
36853 * @class Roo.tree.AsyncTreeNode
36854 * @extends Roo.tree.TreeNode
36855 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36857 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36859 Roo.tree.AsyncTreeNode = function(config){
36860 this.loaded = false;
36861 this.loading = false;
36862 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36864 * @event beforeload
36865 * Fires before this node is loaded, return false to cancel
36866 * @param {Node} this This node
36868 this.addEvents({'beforeload':true, 'load': true});
36871 * Fires when this node is loaded
36872 * @param {Node} this This node
36875 * The loader used by this node (defaults to using the tree's defined loader)
36880 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36881 expand : function(deep, anim, callback){
36882 if(this.loading){ // if an async load is already running, waiting til it's done
36884 var f = function(){
36885 if(!this.loading){ // done loading
36886 clearInterval(timer);
36887 this.expand(deep, anim, callback);
36889 }.createDelegate(this);
36890 timer = setInterval(f, 200);
36894 if(this.fireEvent("beforeload", this) === false){
36897 this.loading = true;
36898 this.ui.beforeLoad(this);
36899 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36901 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36905 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36909 * Returns true if this node is currently loading
36910 * @return {Boolean}
36912 isLoading : function(){
36913 return this.loading;
36916 loadComplete : function(deep, anim, callback){
36917 this.loading = false;
36918 this.loaded = true;
36919 this.ui.afterLoad(this);
36920 this.fireEvent("load", this);
36921 this.expand(deep, anim, callback);
36925 * Returns true if this node has been loaded
36926 * @return {Boolean}
36928 isLoaded : function(){
36929 return this.loaded;
36932 hasChildNodes : function(){
36933 if(!this.isLeaf() && !this.loaded){
36936 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36941 * Trigger a reload for this node
36942 * @param {Function} callback
36944 reload : function(callback){
36945 this.collapse(false, false);
36946 while(this.firstChild){
36947 this.removeChild(this.firstChild);
36949 this.childrenRendered = false;
36950 this.loaded = false;
36951 if(this.isHiddenRoot()){
36952 this.expanded = false;
36954 this.expand(false, false, callback);
36958 * Ext JS Library 1.1.1
36959 * Copyright(c) 2006-2007, Ext JS, LLC.
36961 * Originally Released Under LGPL - original licence link has changed is not relivant.
36964 * <script type="text/javascript">
36968 * @class Roo.tree.TreeNodeUI
36970 * @param {Object} node The node to render
36971 * The TreeNode UI implementation is separate from the
36972 * tree implementation. Unless you are customizing the tree UI,
36973 * you should never have to use this directly.
36975 Roo.tree.TreeNodeUI = function(node){
36977 this.rendered = false;
36978 this.animating = false;
36979 this.emptyIcon = Roo.BLANK_IMAGE_URL;
36982 Roo.tree.TreeNodeUI.prototype = {
36983 removeChild : function(node){
36985 this.ctNode.removeChild(node.ui.getEl());
36989 beforeLoad : function(){
36990 this.addClass("x-tree-node-loading");
36993 afterLoad : function(){
36994 this.removeClass("x-tree-node-loading");
36997 onTextChange : function(node, text, oldText){
36999 this.textNode.innerHTML = text;
37003 onDisableChange : function(node, state){
37004 this.disabled = state;
37006 this.addClass("x-tree-node-disabled");
37008 this.removeClass("x-tree-node-disabled");
37012 onSelectedChange : function(state){
37015 this.addClass("x-tree-selected");
37018 this.removeClass("x-tree-selected");
37022 onMove : function(tree, node, oldParent, newParent, index, refNode){
37023 this.childIndent = null;
37025 var targetNode = newParent.ui.getContainer();
37026 if(!targetNode){//target not rendered
37027 this.holder = document.createElement("div");
37028 this.holder.appendChild(this.wrap);
37031 var insertBefore = refNode ? refNode.ui.getEl() : null;
37033 targetNode.insertBefore(this.wrap, insertBefore);
37035 targetNode.appendChild(this.wrap);
37037 this.node.renderIndent(true);
37041 addClass : function(cls){
37043 Roo.fly(this.elNode).addClass(cls);
37047 removeClass : function(cls){
37049 Roo.fly(this.elNode).removeClass(cls);
37053 remove : function(){
37055 this.holder = document.createElement("div");
37056 this.holder.appendChild(this.wrap);
37060 fireEvent : function(){
37061 return this.node.fireEvent.apply(this.node, arguments);
37064 initEvents : function(){
37065 this.node.on("move", this.onMove, this);
37066 var E = Roo.EventManager;
37067 var a = this.anchor;
37069 var el = Roo.fly(a, '_treeui');
37071 if(Roo.isOpera){ // opera render bug ignores the CSS
37072 el.setStyle("text-decoration", "none");
37075 el.on("click", this.onClick, this);
37076 el.on("dblclick", this.onDblClick, this);
37079 Roo.EventManager.on(this.checkbox,
37080 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37083 el.on("contextmenu", this.onContextMenu, this);
37085 var icon = Roo.fly(this.iconNode);
37086 icon.on("click", this.onClick, this);
37087 icon.on("dblclick", this.onDblClick, this);
37088 icon.on("contextmenu", this.onContextMenu, this);
37089 E.on(this.ecNode, "click", this.ecClick, this, true);
37091 if(this.node.disabled){
37092 this.addClass("x-tree-node-disabled");
37094 if(this.node.hidden){
37095 this.addClass("x-tree-node-disabled");
37097 var ot = this.node.getOwnerTree();
37098 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37099 if(dd && (!this.node.isRoot || ot.rootVisible)){
37100 Roo.dd.Registry.register(this.elNode, {
37102 handles: this.getDDHandles(),
37108 getDDHandles : function(){
37109 return [this.iconNode, this.textNode];
37114 this.wrap.style.display = "none";
37120 this.wrap.style.display = "";
37124 onContextMenu : function(e){
37125 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37126 e.preventDefault();
37128 this.fireEvent("contextmenu", this.node, e);
37132 onClick : function(e){
37137 if(this.fireEvent("beforeclick", this.node, e) !== false){
37138 if(!this.disabled && this.node.attributes.href){
37139 this.fireEvent("click", this.node, e);
37142 e.preventDefault();
37147 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37148 this.node.toggle();
37151 this.fireEvent("click", this.node, e);
37157 onDblClick : function(e){
37158 e.preventDefault();
37163 this.toggleCheck();
37165 if(!this.animating && this.node.hasChildNodes()){
37166 this.node.toggle();
37168 this.fireEvent("dblclick", this.node, e);
37171 onCheckChange : function(){
37172 var checked = this.checkbox.checked;
37173 this.node.attributes.checked = checked;
37174 this.fireEvent('checkchange', this.node, checked);
37177 ecClick : function(e){
37178 if(!this.animating && this.node.hasChildNodes()){
37179 this.node.toggle();
37183 startDrop : function(){
37184 this.dropping = true;
37187 // delayed drop so the click event doesn't get fired on a drop
37188 endDrop : function(){
37189 setTimeout(function(){
37190 this.dropping = false;
37191 }.createDelegate(this), 50);
37194 expand : function(){
37195 this.updateExpandIcon();
37196 this.ctNode.style.display = "";
37199 focus : function(){
37200 if(!this.node.preventHScroll){
37201 try{this.anchor.focus();
37203 }else if(!Roo.isIE){
37205 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37206 var l = noscroll.scrollLeft;
37207 this.anchor.focus();
37208 noscroll.scrollLeft = l;
37213 toggleCheck : function(value){
37214 var cb = this.checkbox;
37216 cb.checked = (value === undefined ? !cb.checked : value);
37222 this.anchor.blur();
37226 animExpand : function(callback){
37227 var ct = Roo.get(this.ctNode);
37229 if(!this.node.hasChildNodes()){
37230 this.updateExpandIcon();
37231 this.ctNode.style.display = "";
37232 Roo.callback(callback);
37235 this.animating = true;
37236 this.updateExpandIcon();
37239 callback : function(){
37240 this.animating = false;
37241 Roo.callback(callback);
37244 duration: this.node.ownerTree.duration || .25
37248 highlight : function(){
37249 var tree = this.node.getOwnerTree();
37250 Roo.fly(this.wrap).highlight(
37251 tree.hlColor || "C3DAF9",
37252 {endColor: tree.hlBaseColor}
37256 collapse : function(){
37257 this.updateExpandIcon();
37258 this.ctNode.style.display = "none";
37261 animCollapse : function(callback){
37262 var ct = Roo.get(this.ctNode);
37263 ct.enableDisplayMode('block');
37266 this.animating = true;
37267 this.updateExpandIcon();
37270 callback : function(){
37271 this.animating = false;
37272 Roo.callback(callback);
37275 duration: this.node.ownerTree.duration || .25
37279 getContainer : function(){
37280 return this.ctNode;
37283 getEl : function(){
37287 appendDDGhost : function(ghostNode){
37288 ghostNode.appendChild(this.elNode.cloneNode(true));
37291 getDDRepairXY : function(){
37292 return Roo.lib.Dom.getXY(this.iconNode);
37295 onRender : function(){
37299 render : function(bulkRender){
37300 var n = this.node, a = n.attributes;
37301 var targetNode = n.parentNode ?
37302 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37304 if(!this.rendered){
37305 this.rendered = true;
37307 this.renderElements(n, a, targetNode, bulkRender);
37310 if(this.textNode.setAttributeNS){
37311 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37313 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37316 this.textNode.setAttribute("ext:qtip", a.qtip);
37318 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37321 }else if(a.qtipCfg){
37322 a.qtipCfg.target = Roo.id(this.textNode);
37323 Roo.QuickTips.register(a.qtipCfg);
37326 if(!this.node.expanded){
37327 this.updateExpandIcon();
37330 if(bulkRender === true) {
37331 targetNode.appendChild(this.wrap);
37336 renderElements : function(n, a, targetNode, bulkRender)
37338 // add some indent caching, this helps performance when rendering a large tree
37339 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37340 var t = n.getOwnerTree();
37341 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37342 if (typeof(n.attributes.html) != 'undefined') {
37343 txt = n.attributes.html;
37345 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37346 var cb = typeof a.checked == 'boolean';
37347 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37348 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37349 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37350 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37351 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37352 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37353 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37354 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
37355 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37356 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37359 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37360 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37361 n.nextSibling.ui.getEl(), buf.join(""));
37363 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37366 this.elNode = this.wrap.childNodes[0];
37367 this.ctNode = this.wrap.childNodes[1];
37368 var cs = this.elNode.childNodes;
37369 this.indentNode = cs[0];
37370 this.ecNode = cs[1];
37371 this.iconNode = cs[2];
37374 this.checkbox = cs[3];
37377 this.anchor = cs[index];
37378 this.textNode = cs[index].firstChild;
37381 getAnchor : function(){
37382 return this.anchor;
37385 getTextEl : function(){
37386 return this.textNode;
37389 getIconEl : function(){
37390 return this.iconNode;
37393 isChecked : function(){
37394 return this.checkbox ? this.checkbox.checked : false;
37397 updateExpandIcon : function(){
37399 var n = this.node, c1, c2;
37400 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37401 var hasChild = n.hasChildNodes();
37405 c1 = "x-tree-node-collapsed";
37406 c2 = "x-tree-node-expanded";
37409 c1 = "x-tree-node-expanded";
37410 c2 = "x-tree-node-collapsed";
37413 this.removeClass("x-tree-node-leaf");
37414 this.wasLeaf = false;
37416 if(this.c1 != c1 || this.c2 != c2){
37417 Roo.fly(this.elNode).replaceClass(c1, c2);
37418 this.c1 = c1; this.c2 = c2;
37421 // this changes non-leafs into leafs if they have no children.
37422 // it's not very rational behaviour..
37424 if(!this.wasLeaf && this.node.leaf){
37425 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37428 this.wasLeaf = true;
37431 var ecc = "x-tree-ec-icon "+cls;
37432 if(this.ecc != ecc){
37433 this.ecNode.className = ecc;
37439 getChildIndent : function(){
37440 if(!this.childIndent){
37444 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37446 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37448 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37453 this.childIndent = buf.join("");
37455 return this.childIndent;
37458 renderIndent : function(){
37461 var p = this.node.parentNode;
37463 indent = p.ui.getChildIndent();
37465 if(this.indentMarkup != indent){ // don't rerender if not required
37466 this.indentNode.innerHTML = indent;
37467 this.indentMarkup = indent;
37469 this.updateExpandIcon();
37474 Roo.tree.RootTreeNodeUI = function(){
37475 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37477 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37478 render : function(){
37479 if(!this.rendered){
37480 var targetNode = this.node.ownerTree.innerCt.dom;
37481 this.node.expanded = true;
37482 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37483 this.wrap = this.ctNode = targetNode.firstChild;
37486 collapse : function(){
37488 expand : function(){
37492 * Ext JS Library 1.1.1
37493 * Copyright(c) 2006-2007, Ext JS, LLC.
37495 * Originally Released Under LGPL - original licence link has changed is not relivant.
37498 * <script type="text/javascript">
37501 * @class Roo.tree.TreeLoader
37502 * @extends Roo.util.Observable
37503 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37504 * nodes from a specified URL. The response must be a javascript Array definition
37505 * who's elements are node definition objects. eg:
37510 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37511 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37518 * The old style respose with just an array is still supported, but not recommended.
37521 * A server request is sent, and child nodes are loaded only when a node is expanded.
37522 * The loading node's id is passed to the server under the parameter name "node" to
37523 * enable the server to produce the correct child nodes.
37525 * To pass extra parameters, an event handler may be attached to the "beforeload"
37526 * event, and the parameters specified in the TreeLoader's baseParams property:
37528 myTreeLoader.on("beforeload", function(treeLoader, node) {
37529 this.baseParams.category = node.attributes.category;
37534 * This would pass an HTTP parameter called "category" to the server containing
37535 * the value of the Node's "category" attribute.
37537 * Creates a new Treeloader.
37538 * @param {Object} config A config object containing config properties.
37540 Roo.tree.TreeLoader = function(config){
37541 this.baseParams = {};
37542 this.requestMethod = "POST";
37543 Roo.apply(this, config);
37548 * @event beforeload
37549 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37550 * @param {Object} This TreeLoader object.
37551 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37552 * @param {Object} callback The callback function specified in the {@link #load} call.
37557 * Fires when the node has been successfuly loaded.
37558 * @param {Object} This TreeLoader object.
37559 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37560 * @param {Object} response The response object containing the data from the server.
37564 * @event loadexception
37565 * Fires if the network request failed.
37566 * @param {Object} This TreeLoader object.
37567 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37568 * @param {Object} response The response object containing the data from the server.
37570 loadexception : true,
37573 * Fires before a node is created, enabling you to return custom Node types
37574 * @param {Object} This TreeLoader object.
37575 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37580 Roo.tree.TreeLoader.superclass.constructor.call(this);
37583 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37585 * @cfg {String} dataUrl The URL from which to request a Json string which
37586 * specifies an array of node definition object representing the child nodes
37590 * @cfg {String} requestMethod either GET or POST
37591 * defaults to POST (due to BC)
37595 * @cfg {Object} baseParams (optional) An object containing properties which
37596 * specify HTTP parameters to be passed to each request for child nodes.
37599 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37600 * created by this loader. If the attributes sent by the server have an attribute in this object,
37601 * they take priority.
37604 * @cfg {Object} uiProviders (optional) An object containing properties which
37606 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37607 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37608 * <i>uiProvider</i> attribute of a returned child node is a string rather
37609 * than a reference to a TreeNodeUI implementation, this that string value
37610 * is used as a property name in the uiProviders object. You can define the provider named
37611 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37616 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37617 * child nodes before loading.
37619 clearOnLoad : true,
37622 * @cfg {String} root (optional) Default to false. Use this to read data from an object
37623 * property on loading, rather than expecting an array. (eg. more compatible to a standard
37624 * Grid query { data : [ .....] }
37629 * @cfg {String} queryParam (optional)
37630 * Name of the query as it will be passed on the querystring (defaults to 'node')
37631 * eg. the request will be ?node=[id]
37638 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37639 * This is called automatically when a node is expanded, but may be used to reload
37640 * a node (or append new children if the {@link #clearOnLoad} option is false.)
37641 * @param {Roo.tree.TreeNode} node
37642 * @param {Function} callback
37644 load : function(node, callback){
37645 if(this.clearOnLoad){
37646 while(node.firstChild){
37647 node.removeChild(node.firstChild);
37650 if(node.attributes.children){ // preloaded json children
37651 var cs = node.attributes.children;
37652 for(var i = 0, len = cs.length; i < len; i++){
37653 node.appendChild(this.createNode(cs[i]));
37655 if(typeof callback == "function"){
37658 }else if(this.dataUrl){
37659 this.requestData(node, callback);
37663 getParams: function(node){
37664 var buf = [], bp = this.baseParams;
37665 for(var key in bp){
37666 if(typeof bp[key] != "function"){
37667 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37670 var n = this.queryParam === false ? 'node' : this.queryParam;
37671 buf.push(n + "=", encodeURIComponent(node.id));
37672 return buf.join("");
37675 requestData : function(node, callback){
37676 if(this.fireEvent("beforeload", this, node, callback) !== false){
37677 this.transId = Roo.Ajax.request({
37678 method:this.requestMethod,
37679 url: this.dataUrl||this.url,
37680 success: this.handleResponse,
37681 failure: this.handleFailure,
37683 argument: {callback: callback, node: node},
37684 params: this.getParams(node)
37687 // if the load is cancelled, make sure we notify
37688 // the node that we are done
37689 if(typeof callback == "function"){
37695 isLoading : function(){
37696 return this.transId ? true : false;
37699 abort : function(){
37700 if(this.isLoading()){
37701 Roo.Ajax.abort(this.transId);
37706 createNode : function(attr)
37708 // apply baseAttrs, nice idea Corey!
37709 if(this.baseAttrs){
37710 Roo.applyIf(attr, this.baseAttrs);
37712 if(this.applyLoader !== false){
37713 attr.loader = this;
37715 // uiProvider = depreciated..
37717 if(typeof(attr.uiProvider) == 'string'){
37718 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
37719 /** eval:var:attr */ eval(attr.uiProvider);
37721 if(typeof(this.uiProviders['default']) != 'undefined') {
37722 attr.uiProvider = this.uiProviders['default'];
37725 this.fireEvent('create', this, attr);
37727 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37729 new Roo.tree.TreeNode(attr) :
37730 new Roo.tree.AsyncTreeNode(attr));
37733 processResponse : function(response, node, callback)
37735 var json = response.responseText;
37738 var o = Roo.decode(json);
37740 if (this.root === false && typeof(o.success) != undefined) {
37741 this.root = 'data'; // the default behaviour for list like data..
37744 if (this.root !== false && !o.success) {
37745 // it's a failure condition.
37746 var a = response.argument;
37747 this.fireEvent("loadexception", this, a.node, response);
37748 Roo.log("Load failed - should have a handler really");
37754 if (this.root !== false) {
37758 for(var i = 0, len = o.length; i < len; i++){
37759 var n = this.createNode(o[i]);
37761 node.appendChild(n);
37764 if(typeof callback == "function"){
37765 callback(this, node);
37768 this.handleFailure(response);
37772 handleResponse : function(response){
37773 this.transId = false;
37774 var a = response.argument;
37775 this.processResponse(response, a.node, a.callback);
37776 this.fireEvent("load", this, a.node, response);
37779 handleFailure : function(response)
37781 // should handle failure better..
37782 this.transId = false;
37783 var a = response.argument;
37784 this.fireEvent("loadexception", this, a.node, response);
37785 if(typeof a.callback == "function"){
37786 a.callback(this, a.node);
37791 * Ext JS Library 1.1.1
37792 * Copyright(c) 2006-2007, Ext JS, LLC.
37794 * Originally Released Under LGPL - original licence link has changed is not relivant.
37797 * <script type="text/javascript">
37801 * @class Roo.tree.TreeFilter
37802 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37803 * @param {TreePanel} tree
37804 * @param {Object} config (optional)
37806 Roo.tree.TreeFilter = function(tree, config){
37808 this.filtered = {};
37809 Roo.apply(this, config);
37812 Roo.tree.TreeFilter.prototype = {
37819 * Filter the data by a specific attribute.
37820 * @param {String/RegExp} value Either string that the attribute value
37821 * should start with or a RegExp to test against the attribute
37822 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37823 * @param {TreeNode} startNode (optional) The node to start the filter at.
37825 filter : function(value, attr, startNode){
37826 attr = attr || "text";
37828 if(typeof value == "string"){
37829 var vlen = value.length;
37830 // auto clear empty filter
37831 if(vlen == 0 && this.clearBlank){
37835 value = value.toLowerCase();
37837 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37839 }else if(value.exec){ // regex?
37841 return value.test(n.attributes[attr]);
37844 throw 'Illegal filter type, must be string or regex';
37846 this.filterBy(f, null, startNode);
37850 * Filter by a function. The passed function will be called with each
37851 * node in the tree (or from the startNode). If the function returns true, the node is kept
37852 * otherwise it is filtered. If a node is filtered, its children are also filtered.
37853 * @param {Function} fn The filter function
37854 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37856 filterBy : function(fn, scope, startNode){
37857 startNode = startNode || this.tree.root;
37858 if(this.autoClear){
37861 var af = this.filtered, rv = this.reverse;
37862 var f = function(n){
37863 if(n == startNode){
37869 var m = fn.call(scope || n, n);
37877 startNode.cascade(f);
37880 if(typeof id != "function"){
37882 if(n && n.parentNode){
37883 n.parentNode.removeChild(n);
37891 * Clears the current filter. Note: with the "remove" option
37892 * set a filter cannot be cleared.
37894 clear : function(){
37896 var af = this.filtered;
37898 if(typeof id != "function"){
37905 this.filtered = {};
37910 * Ext JS Library 1.1.1
37911 * Copyright(c) 2006-2007, Ext JS, LLC.
37913 * Originally Released Under LGPL - original licence link has changed is not relivant.
37916 * <script type="text/javascript">
37921 * @class Roo.tree.TreeSorter
37922 * Provides sorting of nodes in a TreePanel
37924 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37925 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37926 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37927 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37928 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37929 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37931 * @param {TreePanel} tree
37932 * @param {Object} config
37934 Roo.tree.TreeSorter = function(tree, config){
37935 Roo.apply(this, config);
37936 tree.on("beforechildrenrendered", this.doSort, this);
37937 tree.on("append", this.updateSort, this);
37938 tree.on("insert", this.updateSort, this);
37940 var dsc = this.dir && this.dir.toLowerCase() == "desc";
37941 var p = this.property || "text";
37942 var sortType = this.sortType;
37943 var fs = this.folderSort;
37944 var cs = this.caseSensitive === true;
37945 var leafAttr = this.leafAttr || 'leaf';
37947 this.sortFn = function(n1, n2){
37949 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37952 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37956 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37957 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37959 return dsc ? +1 : -1;
37961 return dsc ? -1 : +1;
37968 Roo.tree.TreeSorter.prototype = {
37969 doSort : function(node){
37970 node.sort(this.sortFn);
37973 compareNodes : function(n1, n2){
37974 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37977 updateSort : function(tree, node){
37978 if(node.childrenRendered){
37979 this.doSort.defer(1, this, [node]);
37984 * Ext JS Library 1.1.1
37985 * Copyright(c) 2006-2007, Ext JS, LLC.
37987 * Originally Released Under LGPL - original licence link has changed is not relivant.
37990 * <script type="text/javascript">
37993 if(Roo.dd.DropZone){
37995 Roo.tree.TreeDropZone = function(tree, config){
37996 this.allowParentInsert = false;
37997 this.allowContainerDrop = false;
37998 this.appendOnly = false;
37999 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38001 this.lastInsertClass = "x-tree-no-status";
38002 this.dragOverData = {};
38005 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38006 ddGroup : "TreeDD",
38009 expandDelay : 1000,
38011 expandNode : function(node){
38012 if(node.hasChildNodes() && !node.isExpanded()){
38013 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38017 queueExpand : function(node){
38018 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38021 cancelExpand : function(){
38022 if(this.expandProcId){
38023 clearTimeout(this.expandProcId);
38024 this.expandProcId = false;
38028 isValidDropPoint : function(n, pt, dd, e, data){
38029 if(!n || !data){ return false; }
38030 var targetNode = n.node;
38031 var dropNode = data.node;
38032 // default drop rules
38033 if(!(targetNode && targetNode.isTarget && pt)){
38036 if(pt == "append" && targetNode.allowChildren === false){
38039 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38042 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38045 // reuse the object
38046 var overEvent = this.dragOverData;
38047 overEvent.tree = this.tree;
38048 overEvent.target = targetNode;
38049 overEvent.data = data;
38050 overEvent.point = pt;
38051 overEvent.source = dd;
38052 overEvent.rawEvent = e;
38053 overEvent.dropNode = dropNode;
38054 overEvent.cancel = false;
38055 var result = this.tree.fireEvent("nodedragover", overEvent);
38056 return overEvent.cancel === false && result !== false;
38059 getDropPoint : function(e, n, dd)
38063 return tn.allowChildren !== false ? "append" : false; // always append for root
38065 var dragEl = n.ddel;
38066 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38067 var y = Roo.lib.Event.getPageY(e);
38068 //var noAppend = tn.allowChildren === false || tn.isLeaf();
38070 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38071 var noAppend = tn.allowChildren === false;
38072 if(this.appendOnly || tn.parentNode.allowChildren === false){
38073 return noAppend ? false : "append";
38075 var noBelow = false;
38076 if(!this.allowParentInsert){
38077 noBelow = tn.hasChildNodes() && tn.isExpanded();
38079 var q = (b - t) / (noAppend ? 2 : 3);
38080 if(y >= t && y < (t + q)){
38082 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38089 onNodeEnter : function(n, dd, e, data)
38091 this.cancelExpand();
38094 onNodeOver : function(n, dd, e, data)
38097 var pt = this.getDropPoint(e, n, dd);
38100 // auto node expand check
38101 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38102 this.queueExpand(node);
38103 }else if(pt != "append"){
38104 this.cancelExpand();
38107 // set the insert point style on the target node
38108 var returnCls = this.dropNotAllowed;
38109 if(this.isValidDropPoint(n, pt, dd, e, data)){
38114 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38115 cls = "x-tree-drag-insert-above";
38116 }else if(pt == "below"){
38117 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38118 cls = "x-tree-drag-insert-below";
38120 returnCls = "x-tree-drop-ok-append";
38121 cls = "x-tree-drag-append";
38123 if(this.lastInsertClass != cls){
38124 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38125 this.lastInsertClass = cls;
38132 onNodeOut : function(n, dd, e, data){
38134 this.cancelExpand();
38135 this.removeDropIndicators(n);
38138 onNodeDrop : function(n, dd, e, data){
38139 var point = this.getDropPoint(e, n, dd);
38140 var targetNode = n.node;
38141 targetNode.ui.startDrop();
38142 if(!this.isValidDropPoint(n, point, dd, e, data)){
38143 targetNode.ui.endDrop();
38146 // first try to find the drop node
38147 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38150 target: targetNode,
38155 dropNode: dropNode,
38158 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38159 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38160 targetNode.ui.endDrop();
38163 // allow target changing
38164 targetNode = dropEvent.target;
38165 if(point == "append" && !targetNode.isExpanded()){
38166 targetNode.expand(false, null, function(){
38167 this.completeDrop(dropEvent);
38168 }.createDelegate(this));
38170 this.completeDrop(dropEvent);
38175 completeDrop : function(de){
38176 var ns = de.dropNode, p = de.point, t = de.target;
38177 if(!(ns instanceof Array)){
38181 for(var i = 0, len = ns.length; i < len; i++){
38184 t.parentNode.insertBefore(n, t);
38185 }else if(p == "below"){
38186 t.parentNode.insertBefore(n, t.nextSibling);
38192 if(this.tree.hlDrop){
38196 this.tree.fireEvent("nodedrop", de);
38199 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38200 if(this.tree.hlDrop){
38201 dropNode.ui.focus();
38202 dropNode.ui.highlight();
38204 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38207 getTree : function(){
38211 removeDropIndicators : function(n){
38214 Roo.fly(el).removeClass([
38215 "x-tree-drag-insert-above",
38216 "x-tree-drag-insert-below",
38217 "x-tree-drag-append"]);
38218 this.lastInsertClass = "_noclass";
38222 beforeDragDrop : function(target, e, id){
38223 this.cancelExpand();
38227 afterRepair : function(data){
38228 if(data && Roo.enableFx){
38229 data.node.ui.highlight();
38239 * Ext JS Library 1.1.1
38240 * Copyright(c) 2006-2007, Ext JS, LLC.
38242 * Originally Released Under LGPL - original licence link has changed is not relivant.
38245 * <script type="text/javascript">
38249 if(Roo.dd.DragZone){
38250 Roo.tree.TreeDragZone = function(tree, config){
38251 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38255 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38256 ddGroup : "TreeDD",
38258 onBeforeDrag : function(data, e){
38260 return n && n.draggable && !n.disabled;
38264 onInitDrag : function(e){
38265 var data = this.dragData;
38266 this.tree.getSelectionModel().select(data.node);
38267 this.proxy.update("");
38268 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38269 this.tree.fireEvent("startdrag", this.tree, data.node, e);
38272 getRepairXY : function(e, data){
38273 return data.node.ui.getDDRepairXY();
38276 onEndDrag : function(data, e){
38277 this.tree.fireEvent("enddrag", this.tree, data.node, e);
38282 onValidDrop : function(dd, e, id){
38283 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38287 beforeInvalidDrop : function(e, id){
38288 // this scrolls the original position back into view
38289 var sm = this.tree.getSelectionModel();
38290 sm.clearSelections();
38291 sm.select(this.dragData.node);
38296 * Ext JS Library 1.1.1
38297 * Copyright(c) 2006-2007, Ext JS, LLC.
38299 * Originally Released Under LGPL - original licence link has changed is not relivant.
38302 * <script type="text/javascript">
38305 * @class Roo.tree.TreeEditor
38306 * @extends Roo.Editor
38307 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
38308 * as the editor field.
38310 * @param {Object} config (used to be the tree panel.)
38311 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38313 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38314 * @cfg {Roo.form.TextField} field [required] The field configuration
38318 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38321 if (oldconfig) { // old style..
38322 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38325 tree = config.tree;
38326 config.field = config.field || {};
38327 config.field.xtype = 'TextField';
38328 field = Roo.factory(config.field, Roo.form);
38330 config = config || {};
38335 * @event beforenodeedit
38336 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
38337 * false from the handler of this event.
38338 * @param {Editor} this
38339 * @param {Roo.tree.Node} node
38341 "beforenodeedit" : true
38345 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38349 tree.on('beforeclick', this.beforeNodeClick, this);
38350 tree.getTreeEl().on('mousedown', this.hide, this);
38351 this.on('complete', this.updateNode, this);
38352 this.on('beforestartedit', this.fitToTree, this);
38353 this.on('startedit', this.bindScroll, this, {delay:10});
38354 this.on('specialkey', this.onSpecialKey, this);
38357 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38359 * @cfg {String} alignment
38360 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38366 * @cfg {Boolean} hideEl
38367 * True to hide the bound element while the editor is displayed (defaults to false)
38371 * @cfg {String} cls
38372 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38374 cls: "x-small-editor x-tree-editor",
38376 * @cfg {Boolean} shim
38377 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38383 * @cfg {Number} maxWidth
38384 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
38385 * the containing tree element's size, it will be automatically limited for you to the container width, taking
38386 * scroll and client offsets into account prior to each edit.
38393 fitToTree : function(ed, el){
38394 var td = this.tree.getTreeEl().dom, nd = el.dom;
38395 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
38396 td.scrollLeft = nd.offsetLeft;
38400 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38401 this.setSize(w, '');
38403 return this.fireEvent('beforenodeedit', this, this.editNode);
38408 triggerEdit : function(node){
38409 this.completeEdit();
38410 this.editNode = node;
38411 this.startEdit(node.ui.textNode, node.text);
38415 bindScroll : function(){
38416 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38420 beforeNodeClick : function(node, e){
38421 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38422 this.lastClick = new Date();
38423 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38425 this.triggerEdit(node);
38432 updateNode : function(ed, value){
38433 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38434 this.editNode.setText(value);
38438 onHide : function(){
38439 Roo.tree.TreeEditor.superclass.onHide.call(this);
38441 this.editNode.ui.focus();
38446 onSpecialKey : function(field, e){
38447 var k = e.getKey();
38451 }else if(k == e.ENTER && !e.hasModifier()){
38453 this.completeEdit();
38456 });//<Script type="text/javascript">
38459 * Ext JS Library 1.1.1
38460 * Copyright(c) 2006-2007, Ext JS, LLC.
38462 * Originally Released Under LGPL - original licence link has changed is not relivant.
38465 * <script type="text/javascript">
38469 * Not documented??? - probably should be...
38472 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38473 //focus: Roo.emptyFn, // prevent odd scrolling behavior
38475 renderElements : function(n, a, targetNode, bulkRender){
38476 //consel.log("renderElements?");
38477 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38479 var t = n.getOwnerTree();
38480 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38482 var cols = t.columns;
38483 var bw = t.borderWidth;
38485 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38486 var cb = typeof a.checked == "boolean";
38487 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38488 var colcls = 'x-t-' + tid + '-c0';
38490 '<li class="x-tree-node">',
38493 '<div class="x-tree-node-el ', a.cls,'">',
38495 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38498 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38499 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
38500 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38501 (a.icon ? ' x-tree-node-inline-icon' : ''),
38502 (a.iconCls ? ' '+a.iconCls : ''),
38503 '" unselectable="on" />',
38504 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
38505 (a.checked ? 'checked="checked" />' : ' />')) : ''),
38507 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38508 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38509 '<span unselectable="on" qtip="' + tx + '">',
38513 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38514 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38516 for(var i = 1, len = cols.length; i < len; i++){
38518 colcls = 'x-t-' + tid + '-c' +i;
38519 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38520 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38521 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38527 '<div class="x-clear"></div></div>',
38528 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38531 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38532 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38533 n.nextSibling.ui.getEl(), buf.join(""));
38535 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38537 var el = this.wrap.firstChild;
38539 this.elNode = el.firstChild;
38540 this.ranchor = el.childNodes[1];
38541 this.ctNode = this.wrap.childNodes[1];
38542 var cs = el.firstChild.childNodes;
38543 this.indentNode = cs[0];
38544 this.ecNode = cs[1];
38545 this.iconNode = cs[2];
38548 this.checkbox = cs[3];
38551 this.anchor = cs[index];
38553 this.textNode = cs[index].firstChild;
38555 //el.on("click", this.onClick, this);
38556 //el.on("dblclick", this.onDblClick, this);
38559 // console.log(this);
38561 initEvents : function(){
38562 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38565 var a = this.ranchor;
38567 var el = Roo.get(a);
38569 if(Roo.isOpera){ // opera render bug ignores the CSS
38570 el.setStyle("text-decoration", "none");
38573 el.on("click", this.onClick, this);
38574 el.on("dblclick", this.onDblClick, this);
38575 el.on("contextmenu", this.onContextMenu, this);
38579 /*onSelectedChange : function(state){
38582 this.addClass("x-tree-selected");
38585 this.removeClass("x-tree-selected");
38588 addClass : function(cls){
38590 Roo.fly(this.elRow).addClass(cls);
38596 removeClass : function(cls){
38598 Roo.fly(this.elRow).removeClass(cls);
38604 });//<Script type="text/javascript">
38608 * Ext JS Library 1.1.1
38609 * Copyright(c) 2006-2007, Ext JS, LLC.
38611 * Originally Released Under LGPL - original licence link has changed is not relivant.
38614 * <script type="text/javascript">
38619 * @class Roo.tree.ColumnTree
38620 * @extends Roo.tree.TreePanel
38621 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
38622 * @cfg {int} borderWidth compined right/left border allowance
38624 * @param {String/HTMLElement/Element} el The container element
38625 * @param {Object} config
38627 Roo.tree.ColumnTree = function(el, config)
38629 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38633 * Fire this event on a container when it resizes
38634 * @param {int} w Width
38635 * @param {int} h Height
38639 this.on('resize', this.onResize, this);
38642 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38646 borderWidth: Roo.isBorderBox ? 0 : 2,
38649 render : function(){
38650 // add the header.....
38652 Roo.tree.ColumnTree.superclass.render.apply(this);
38654 this.el.addClass('x-column-tree');
38656 this.headers = this.el.createChild(
38657 {cls:'x-tree-headers'},this.innerCt.dom);
38659 var cols = this.columns, c;
38660 var totalWidth = 0;
38662 var len = cols.length;
38663 for(var i = 0; i < len; i++){
38665 totalWidth += c.width;
38666 this.headEls.push(this.headers.createChild({
38667 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38669 cls:'x-tree-hd-text',
38672 style:'width:'+(c.width-this.borderWidth)+'px;'
38675 this.headers.createChild({cls:'x-clear'});
38676 // prevent floats from wrapping when clipped
38677 this.headers.setWidth(totalWidth);
38678 //this.innerCt.setWidth(totalWidth);
38679 this.innerCt.setStyle({ overflow: 'auto' });
38680 this.onResize(this.width, this.height);
38684 onResize : function(w,h)
38689 this.innerCt.setWidth(this.width);
38690 this.innerCt.setHeight(this.height-20);
38693 var cols = this.columns, c;
38694 var totalWidth = 0;
38696 var len = cols.length;
38697 for(var i = 0; i < len; i++){
38699 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38700 // it's the expander..
38701 expEl = this.headEls[i];
38704 totalWidth += c.width;
38708 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
38710 this.headers.setWidth(w-20);
38719 * Ext JS Library 1.1.1
38720 * Copyright(c) 2006-2007, Ext JS, LLC.
38722 * Originally Released Under LGPL - original licence link has changed is not relivant.
38725 * <script type="text/javascript">
38729 * @class Roo.menu.Menu
38730 * @extends Roo.util.Observable
38731 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38732 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
38733 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38735 * Creates a new Menu
38736 * @param {Object} config Configuration options
38738 Roo.menu.Menu = function(config){
38740 Roo.menu.Menu.superclass.constructor.call(this, config);
38742 this.id = this.id || Roo.id();
38745 * @event beforeshow
38746 * Fires before this menu is displayed
38747 * @param {Roo.menu.Menu} this
38751 * @event beforehide
38752 * Fires before this menu is hidden
38753 * @param {Roo.menu.Menu} this
38758 * Fires after this menu is displayed
38759 * @param {Roo.menu.Menu} this
38764 * Fires after this menu is hidden
38765 * @param {Roo.menu.Menu} this
38770 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38771 * @param {Roo.menu.Menu} this
38772 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38773 * @param {Roo.EventObject} e
38778 * Fires when the mouse is hovering over this menu
38779 * @param {Roo.menu.Menu} this
38780 * @param {Roo.EventObject} e
38781 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38786 * Fires when the mouse exits this menu
38787 * @param {Roo.menu.Menu} this
38788 * @param {Roo.EventObject} e
38789 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38794 * Fires when a menu item contained in this menu is clicked
38795 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38796 * @param {Roo.EventObject} e
38800 if (this.registerMenu) {
38801 Roo.menu.MenuMgr.register(this);
38804 var mis = this.items;
38805 this.items = new Roo.util.MixedCollection();
38807 this.add.apply(this, mis);
38811 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38813 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38817 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38818 * for bottom-right shadow (defaults to "sides")
38822 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38823 * this menu (defaults to "tl-tr?")
38825 subMenuAlign : "tl-tr?",
38827 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38828 * relative to its element of origin (defaults to "tl-bl?")
38830 defaultAlign : "tl-bl?",
38832 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38834 allowOtherMenus : false,
38836 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38838 registerMenu : true,
38843 render : function(){
38847 var el = this.el = new Roo.Layer({
38849 shadow:this.shadow,
38851 parentEl: this.parentEl || document.body,
38855 this.keyNav = new Roo.menu.MenuNav(this);
38858 el.addClass("x-menu-plain");
38861 el.addClass(this.cls);
38863 // generic focus element
38864 this.focusEl = el.createChild({
38865 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38867 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38868 //disabling touch- as it's causing issues ..
38869 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
38870 ul.on('click' , this.onClick, this);
38873 ul.on("mouseover", this.onMouseOver, this);
38874 ul.on("mouseout", this.onMouseOut, this);
38875 this.items.each(function(item){
38880 var li = document.createElement("li");
38881 li.className = "x-menu-list-item";
38882 ul.dom.appendChild(li);
38883 item.render(li, this);
38890 autoWidth : function(){
38891 var el = this.el, ul = this.ul;
38895 var w = this.width;
38898 }else if(Roo.isIE){
38899 el.setWidth(this.minWidth);
38900 var t = el.dom.offsetWidth; // force recalc
38901 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38906 delayAutoWidth : function(){
38909 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38911 this.awTask.delay(20);
38916 findTargetItem : function(e){
38917 var t = e.getTarget(".x-menu-list-item", this.ul, true);
38918 if(t && t.menuItemId){
38919 return this.items.get(t.menuItemId);
38924 onClick : function(e){
38925 Roo.log("menu.onClick");
38926 var t = this.findTargetItem(e);
38931 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
38932 if(t == this.activeItem && t.shouldDeactivate(e)){
38933 this.activeItem.deactivate();
38934 delete this.activeItem;
38938 this.setActiveItem(t, true);
38946 this.fireEvent("click", this, t, e);
38950 setActiveItem : function(item, autoExpand){
38951 if(item != this.activeItem){
38952 if(this.activeItem){
38953 this.activeItem.deactivate();
38955 this.activeItem = item;
38956 item.activate(autoExpand);
38957 }else if(autoExpand){
38963 tryActivate : function(start, step){
38964 var items = this.items;
38965 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38966 var item = items.get(i);
38967 if(!item.disabled && item.canActivate){
38968 this.setActiveItem(item, false);
38976 onMouseOver : function(e){
38978 if(t = this.findTargetItem(e)){
38979 if(t.canActivate && !t.disabled){
38980 this.setActiveItem(t, true);
38983 this.fireEvent("mouseover", this, e, t);
38987 onMouseOut : function(e){
38989 if(t = this.findTargetItem(e)){
38990 if(t == this.activeItem && t.shouldDeactivate(e)){
38991 this.activeItem.deactivate();
38992 delete this.activeItem;
38995 this.fireEvent("mouseout", this, e, t);
38999 * Read-only. Returns true if the menu is currently displayed, else false.
39002 isVisible : function(){
39003 return this.el && !this.hidden;
39007 * Displays this menu relative to another element
39008 * @param {String/HTMLElement/Roo.Element} element The element to align to
39009 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39010 * the element (defaults to this.defaultAlign)
39011 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39013 show : function(el, pos, parentMenu){
39014 this.parentMenu = parentMenu;
39018 this.fireEvent("beforeshow", this);
39019 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39023 * Displays this menu at a specific xy position
39024 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39025 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39027 showAt : function(xy, parentMenu, /* private: */_e){
39028 this.parentMenu = parentMenu;
39033 this.fireEvent("beforeshow", this);
39034 xy = this.el.adjustForConstraints(xy);
39038 this.hidden = false;
39040 this.fireEvent("show", this);
39043 focus : function(){
39045 this.doFocus.defer(50, this);
39049 doFocus : function(){
39051 this.focusEl.focus();
39056 * Hides this menu and optionally all parent menus
39057 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39059 hide : function(deep){
39060 if(this.el && this.isVisible()){
39061 this.fireEvent("beforehide", this);
39062 if(this.activeItem){
39063 this.activeItem.deactivate();
39064 this.activeItem = null;
39067 this.hidden = true;
39068 this.fireEvent("hide", this);
39070 if(deep === true && this.parentMenu){
39071 this.parentMenu.hide(true);
39076 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39077 * Any of the following are valid:
39079 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39080 * <li>An HTMLElement object which will be converted to a menu item</li>
39081 * <li>A menu item config object that will be created as a new menu item</li>
39082 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39083 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39088 var menu = new Roo.menu.Menu();
39090 // Create a menu item to add by reference
39091 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39093 // Add a bunch of items at once using different methods.
39094 // Only the last item added will be returned.
39095 var item = menu.add(
39096 menuItem, // add existing item by ref
39097 'Dynamic Item', // new TextItem
39098 '-', // new separator
39099 { text: 'Config Item' } // new item by config
39102 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39103 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39106 var a = arguments, l = a.length, item;
39107 for(var i = 0; i < l; i++){
39109 if ((typeof(el) == "object") && el.xtype && el.xns) {
39110 el = Roo.factory(el, Roo.menu);
39113 if(el.render){ // some kind of Item
39114 item = this.addItem(el);
39115 }else if(typeof el == "string"){ // string
39116 if(el == "separator" || el == "-"){
39117 item = this.addSeparator();
39119 item = this.addText(el);
39121 }else if(el.tagName || el.el){ // element
39122 item = this.addElement(el);
39123 }else if(typeof el == "object"){ // must be menu item config?
39124 item = this.addMenuItem(el);
39131 * Returns this menu's underlying {@link Roo.Element} object
39132 * @return {Roo.Element} The element
39134 getEl : function(){
39142 * Adds a separator bar to the menu
39143 * @return {Roo.menu.Item} The menu item that was added
39145 addSeparator : function(){
39146 return this.addItem(new Roo.menu.Separator());
39150 * Adds an {@link Roo.Element} object to the menu
39151 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39152 * @return {Roo.menu.Item} The menu item that was added
39154 addElement : function(el){
39155 return this.addItem(new Roo.menu.BaseItem(el));
39159 * Adds an existing object based on {@link Roo.menu.Item} to the menu
39160 * @param {Roo.menu.Item} item The menu item to add
39161 * @return {Roo.menu.Item} The menu item that was added
39163 addItem : function(item){
39164 this.items.add(item);
39166 var li = document.createElement("li");
39167 li.className = "x-menu-list-item";
39168 this.ul.dom.appendChild(li);
39169 item.render(li, this);
39170 this.delayAutoWidth();
39176 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39177 * @param {Object} config A MenuItem config object
39178 * @return {Roo.menu.Item} The menu item that was added
39180 addMenuItem : function(config){
39181 if(!(config instanceof Roo.menu.Item)){
39182 if(typeof config.checked == "boolean"){ // must be check menu item config?
39183 config = new Roo.menu.CheckItem(config);
39185 config = new Roo.menu.Item(config);
39188 return this.addItem(config);
39192 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39193 * @param {String} text The text to display in the menu item
39194 * @return {Roo.menu.Item} The menu item that was added
39196 addText : function(text){
39197 return this.addItem(new Roo.menu.TextItem({ text : text }));
39201 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39202 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39203 * @param {Roo.menu.Item} item The menu item to add
39204 * @return {Roo.menu.Item} The menu item that was added
39206 insert : function(index, item){
39207 this.items.insert(index, item);
39209 var li = document.createElement("li");
39210 li.className = "x-menu-list-item";
39211 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39212 item.render(li, this);
39213 this.delayAutoWidth();
39219 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39220 * @param {Roo.menu.Item} item The menu item to remove
39222 remove : function(item){
39223 this.items.removeKey(item.id);
39228 * Removes and destroys all items in the menu
39230 removeAll : function(){
39232 while(f = this.items.first()){
39238 // MenuNav is a private utility class used internally by the Menu
39239 Roo.menu.MenuNav = function(menu){
39240 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39241 this.scope = this.menu = menu;
39244 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39245 doRelay : function(e, h){
39246 var k = e.getKey();
39247 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39248 this.menu.tryActivate(0, 1);
39251 return h.call(this.scope || this, e, this.menu);
39254 up : function(e, m){
39255 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39256 m.tryActivate(m.items.length-1, -1);
39260 down : function(e, m){
39261 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39262 m.tryActivate(0, 1);
39266 right : function(e, m){
39268 m.activeItem.expandMenu(true);
39272 left : function(e, m){
39274 if(m.parentMenu && m.parentMenu.activeItem){
39275 m.parentMenu.activeItem.activate();
39279 enter : function(e, m){
39281 e.stopPropagation();
39282 m.activeItem.onClick(e);
39283 m.fireEvent("click", this, m.activeItem);
39289 * Ext JS Library 1.1.1
39290 * Copyright(c) 2006-2007, Ext JS, LLC.
39292 * Originally Released Under LGPL - original licence link has changed is not relivant.
39295 * <script type="text/javascript">
39299 * @class Roo.menu.MenuMgr
39300 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39303 Roo.menu.MenuMgr = function(){
39304 var menus, active, groups = {}, attached = false, lastShow = new Date();
39306 // private - called when first menu is created
39309 active = new Roo.util.MixedCollection();
39310 Roo.get(document).addKeyListener(27, function(){
39311 if(active.length > 0){
39318 function hideAll(){
39319 if(active && active.length > 0){
39320 var c = active.clone();
39321 c.each(function(m){
39328 function onHide(m){
39330 if(active.length < 1){
39331 Roo.get(document).un("mousedown", onMouseDown);
39337 function onShow(m){
39338 var last = active.last();
39339 lastShow = new Date();
39342 Roo.get(document).on("mousedown", onMouseDown);
39346 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39347 m.parentMenu.activeChild = m;
39348 }else if(last && last.isVisible()){
39349 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39354 function onBeforeHide(m){
39356 m.activeChild.hide();
39358 if(m.autoHideTimer){
39359 clearTimeout(m.autoHideTimer);
39360 delete m.autoHideTimer;
39365 function onBeforeShow(m){
39366 var pm = m.parentMenu;
39367 if(!pm && !m.allowOtherMenus){
39369 }else if(pm && pm.activeChild && active != m){
39370 pm.activeChild.hide();
39375 function onMouseDown(e){
39376 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39382 function onBeforeCheck(mi, state){
39384 var g = groups[mi.group];
39385 for(var i = 0, l = g.length; i < l; i++){
39387 g[i].setChecked(false);
39396 * Hides all menus that are currently visible
39398 hideAll : function(){
39403 register : function(menu){
39407 menus[menu.id] = menu;
39408 menu.on("beforehide", onBeforeHide);
39409 menu.on("hide", onHide);
39410 menu.on("beforeshow", onBeforeShow);
39411 menu.on("show", onShow);
39412 var g = menu.group;
39413 if(g && menu.events["checkchange"]){
39417 groups[g].push(menu);
39418 menu.on("checkchange", onCheck);
39423 * Returns a {@link Roo.menu.Menu} object
39424 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39425 * be used to generate and return a new Menu instance.
39427 get : function(menu){
39428 if(typeof menu == "string"){ // menu id
39429 return menus[menu];
39430 }else if(menu.events){ // menu instance
39432 }else if(typeof menu.length == 'number'){ // array of menu items?
39433 return new Roo.menu.Menu({items:menu});
39434 }else{ // otherwise, must be a config
39435 return new Roo.menu.Menu(menu);
39440 unregister : function(menu){
39441 delete menus[menu.id];
39442 menu.un("beforehide", onBeforeHide);
39443 menu.un("hide", onHide);
39444 menu.un("beforeshow", onBeforeShow);
39445 menu.un("show", onShow);
39446 var g = menu.group;
39447 if(g && menu.events["checkchange"]){
39448 groups[g].remove(menu);
39449 menu.un("checkchange", onCheck);
39454 registerCheckable : function(menuItem){
39455 var g = menuItem.group;
39460 groups[g].push(menuItem);
39461 menuItem.on("beforecheckchange", onBeforeCheck);
39466 unregisterCheckable : function(menuItem){
39467 var g = menuItem.group;
39469 groups[g].remove(menuItem);
39470 menuItem.un("beforecheckchange", onBeforeCheck);
39476 * Ext JS Library 1.1.1
39477 * Copyright(c) 2006-2007, Ext JS, LLC.
39479 * Originally Released Under LGPL - original licence link has changed is not relivant.
39482 * <script type="text/javascript">
39487 * @class Roo.menu.BaseItem
39488 * @extends Roo.Component
39490 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
39491 * management and base configuration options shared by all menu components.
39493 * Creates a new BaseItem
39494 * @param {Object} config Configuration options
39496 Roo.menu.BaseItem = function(config){
39497 Roo.menu.BaseItem.superclass.constructor.call(this, config);
39502 * Fires when this item is clicked
39503 * @param {Roo.menu.BaseItem} this
39504 * @param {Roo.EventObject} e
39509 * Fires when this item is activated
39510 * @param {Roo.menu.BaseItem} this
39514 * @event deactivate
39515 * Fires when this item is deactivated
39516 * @param {Roo.menu.BaseItem} this
39522 this.on("click", this.handler, this.scope, true);
39526 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39528 * @cfg {Function} handler
39529 * A function that will handle the click event of this menu item (defaults to undefined)
39532 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39534 canActivate : false,
39537 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39542 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39544 activeClass : "x-menu-item-active",
39546 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39548 hideOnClick : true,
39550 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39555 ctype: "Roo.menu.BaseItem",
39558 actionMode : "container",
39561 render : function(container, parentMenu){
39562 this.parentMenu = parentMenu;
39563 Roo.menu.BaseItem.superclass.render.call(this, container);
39564 this.container.menuItemId = this.id;
39568 onRender : function(container, position){
39569 this.el = Roo.get(this.el);
39570 container.dom.appendChild(this.el.dom);
39574 onClick : function(e){
39575 if(!this.disabled && this.fireEvent("click", this, e) !== false
39576 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39577 this.handleClick(e);
39584 activate : function(){
39588 var li = this.container;
39589 li.addClass(this.activeClass);
39590 this.region = li.getRegion().adjust(2, 2, -2, -2);
39591 this.fireEvent("activate", this);
39596 deactivate : function(){
39597 this.container.removeClass(this.activeClass);
39598 this.fireEvent("deactivate", this);
39602 shouldDeactivate : function(e){
39603 return !this.region || !this.region.contains(e.getPoint());
39607 handleClick : function(e){
39608 if(this.hideOnClick){
39609 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39614 expandMenu : function(autoActivate){
39619 hideMenu : function(){
39624 * Ext JS Library 1.1.1
39625 * Copyright(c) 2006-2007, Ext JS, LLC.
39627 * Originally Released Under LGPL - original licence link has changed is not relivant.
39630 * <script type="text/javascript">
39634 * @class Roo.menu.Adapter
39635 * @extends Roo.menu.BaseItem
39637 * 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.
39638 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39640 * Creates a new Adapter
39641 * @param {Object} config Configuration options
39643 Roo.menu.Adapter = function(component, config){
39644 Roo.menu.Adapter.superclass.constructor.call(this, config);
39645 this.component = component;
39647 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39649 canActivate : true,
39652 onRender : function(container, position){
39653 this.component.render(container);
39654 this.el = this.component.getEl();
39658 activate : function(){
39662 this.component.focus();
39663 this.fireEvent("activate", this);
39668 deactivate : function(){
39669 this.fireEvent("deactivate", this);
39673 disable : function(){
39674 this.component.disable();
39675 Roo.menu.Adapter.superclass.disable.call(this);
39679 enable : function(){
39680 this.component.enable();
39681 Roo.menu.Adapter.superclass.enable.call(this);
39685 * Ext JS Library 1.1.1
39686 * Copyright(c) 2006-2007, Ext JS, LLC.
39688 * Originally Released Under LGPL - original licence link has changed is not relivant.
39691 * <script type="text/javascript">
39695 * @class Roo.menu.TextItem
39696 * @extends Roo.menu.BaseItem
39697 * Adds a static text string to a menu, usually used as either a heading or group separator.
39698 * Note: old style constructor with text is still supported.
39701 * Creates a new TextItem
39702 * @param {Object} cfg Configuration
39704 Roo.menu.TextItem = function(cfg){
39705 if (typeof(cfg) == 'string') {
39708 Roo.apply(this,cfg);
39711 Roo.menu.TextItem.superclass.constructor.call(this);
39714 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39716 * @cfg {String} text Text to show on item.
39721 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39723 hideOnClick : false,
39725 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39727 itemCls : "x-menu-text",
39730 onRender : function(){
39731 var s = document.createElement("span");
39732 s.className = this.itemCls;
39733 s.innerHTML = this.text;
39735 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39739 * Ext JS Library 1.1.1
39740 * Copyright(c) 2006-2007, Ext JS, LLC.
39742 * Originally Released Under LGPL - original licence link has changed is not relivant.
39745 * <script type="text/javascript">
39749 * @class Roo.menu.Separator
39750 * @extends Roo.menu.BaseItem
39751 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39752 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39754 * @param {Object} config Configuration options
39756 Roo.menu.Separator = function(config){
39757 Roo.menu.Separator.superclass.constructor.call(this, config);
39760 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39762 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39764 itemCls : "x-menu-sep",
39766 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39768 hideOnClick : false,
39771 onRender : function(li){
39772 var s = document.createElement("span");
39773 s.className = this.itemCls;
39774 s.innerHTML = " ";
39776 li.addClass("x-menu-sep-li");
39777 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39781 * Ext JS Library 1.1.1
39782 * Copyright(c) 2006-2007, Ext JS, LLC.
39784 * Originally Released Under LGPL - original licence link has changed is not relivant.
39787 * <script type="text/javascript">
39790 * @class Roo.menu.Item
39791 * @extends Roo.menu.BaseItem
39792 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39793 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39794 * activation and click handling.
39796 * Creates a new Item
39797 * @param {Object} config Configuration options
39799 Roo.menu.Item = function(config){
39800 Roo.menu.Item.superclass.constructor.call(this, config);
39802 this.menu = Roo.menu.MenuMgr.get(this.menu);
39805 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39807 * @cfg {Roo.menu.Menu} menu
39811 * @cfg {String} text
39812 * The text to show on the menu item.
39816 * @cfg {String} HTML to render in menu
39817 * The text to show on the menu item (HTML version).
39821 * @cfg {String} icon
39822 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39826 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39828 itemCls : "x-menu-item",
39830 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39832 canActivate : true,
39834 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39837 // doc'd in BaseItem
39841 ctype: "Roo.menu.Item",
39844 onRender : function(container, position){
39845 var el = document.createElement("a");
39846 el.hideFocus = true;
39847 el.unselectable = "on";
39848 el.href = this.href || "#";
39849 if(this.hrefTarget){
39850 el.target = this.hrefTarget;
39852 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
39854 var html = this.html.length ? this.html : String.format('{0}',this.text);
39856 el.innerHTML = String.format(
39857 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39858 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39860 Roo.menu.Item.superclass.onRender.call(this, container, position);
39864 * Sets the text to display in this menu item
39865 * @param {String} text The text to display
39866 * @param {Boolean} isHTML true to indicate text is pure html.
39868 setText : function(text, isHTML){
39876 var html = this.html.length ? this.html : String.format('{0}',this.text);
39878 this.el.update(String.format(
39879 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39880 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39881 this.parentMenu.autoWidth();
39886 handleClick : function(e){
39887 if(!this.href){ // if no link defined, stop the event automatically
39890 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39894 activate : function(autoExpand){
39895 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39905 shouldDeactivate : function(e){
39906 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39907 if(this.menu && this.menu.isVisible()){
39908 return !this.menu.getEl().getRegion().contains(e.getPoint());
39916 deactivate : function(){
39917 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39922 expandMenu : function(autoActivate){
39923 if(!this.disabled && this.menu){
39924 clearTimeout(this.hideTimer);
39925 delete this.hideTimer;
39926 if(!this.menu.isVisible() && !this.showTimer){
39927 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39928 }else if (this.menu.isVisible() && autoActivate){
39929 this.menu.tryActivate(0, 1);
39935 deferExpand : function(autoActivate){
39936 delete this.showTimer;
39937 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39939 this.menu.tryActivate(0, 1);
39944 hideMenu : function(){
39945 clearTimeout(this.showTimer);
39946 delete this.showTimer;
39947 if(!this.hideTimer && this.menu && this.menu.isVisible()){
39948 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39953 deferHide : function(){
39954 delete this.hideTimer;
39959 * Ext JS Library 1.1.1
39960 * Copyright(c) 2006-2007, Ext JS, LLC.
39962 * Originally Released Under LGPL - original licence link has changed is not relivant.
39965 * <script type="text/javascript">
39969 * @class Roo.menu.CheckItem
39970 * @extends Roo.menu.Item
39971 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39973 * Creates a new CheckItem
39974 * @param {Object} config Configuration options
39976 Roo.menu.CheckItem = function(config){
39977 Roo.menu.CheckItem.superclass.constructor.call(this, config);
39980 * @event beforecheckchange
39981 * Fires before the checked value is set, providing an opportunity to cancel if needed
39982 * @param {Roo.menu.CheckItem} this
39983 * @param {Boolean} checked The new checked value that will be set
39985 "beforecheckchange" : true,
39987 * @event checkchange
39988 * Fires after the checked value has been set
39989 * @param {Roo.menu.CheckItem} this
39990 * @param {Boolean} checked The checked value that was set
39992 "checkchange" : true
39994 if(this.checkHandler){
39995 this.on('checkchange', this.checkHandler, this.scope);
39998 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40000 * @cfg {String} group
40001 * All check items with the same group name will automatically be grouped into a single-select
40002 * radio button group (defaults to '')
40005 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40007 itemCls : "x-menu-item x-menu-check-item",
40009 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40011 groupClass : "x-menu-group-item",
40014 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
40015 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40016 * initialized with checked = true will be rendered as checked.
40021 ctype: "Roo.menu.CheckItem",
40024 onRender : function(c){
40025 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40027 this.el.addClass(this.groupClass);
40029 Roo.menu.MenuMgr.registerCheckable(this);
40031 this.checked = false;
40032 this.setChecked(true, true);
40037 destroy : function(){
40039 Roo.menu.MenuMgr.unregisterCheckable(this);
40041 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40045 * Set the checked state of this item
40046 * @param {Boolean} checked The new checked value
40047 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40049 setChecked : function(state, suppressEvent){
40050 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40051 if(this.container){
40052 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40054 this.checked = state;
40055 if(suppressEvent !== true){
40056 this.fireEvent("checkchange", this, state);
40062 handleClick : function(e){
40063 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40064 this.setChecked(!this.checked);
40066 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40070 * Ext JS Library 1.1.1
40071 * Copyright(c) 2006-2007, Ext JS, LLC.
40073 * Originally Released Under LGPL - original licence link has changed is not relivant.
40076 * <script type="text/javascript">
40080 * @class Roo.menu.DateItem
40081 * @extends Roo.menu.Adapter
40082 * A menu item that wraps the {@link Roo.DatPicker} component.
40084 * Creates a new DateItem
40085 * @param {Object} config Configuration options
40087 Roo.menu.DateItem = function(config){
40088 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40089 /** The Roo.DatePicker object @type Roo.DatePicker */
40090 this.picker = this.component;
40091 this.addEvents({select: true});
40093 this.picker.on("render", function(picker){
40094 picker.getEl().swallowEvent("click");
40095 picker.container.addClass("x-menu-date-item");
40098 this.picker.on("select", this.onSelect, this);
40101 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40103 onSelect : function(picker, date){
40104 this.fireEvent("select", this, date, picker);
40105 Roo.menu.DateItem.superclass.handleClick.call(this);
40109 * Ext JS Library 1.1.1
40110 * Copyright(c) 2006-2007, Ext JS, LLC.
40112 * Originally Released Under LGPL - original licence link has changed is not relivant.
40115 * <script type="text/javascript">
40119 * @class Roo.menu.ColorItem
40120 * @extends Roo.menu.Adapter
40121 * A menu item that wraps the {@link Roo.ColorPalette} component.
40123 * Creates a new ColorItem
40124 * @param {Object} config Configuration options
40126 Roo.menu.ColorItem = function(config){
40127 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40128 /** The Roo.ColorPalette object @type Roo.ColorPalette */
40129 this.palette = this.component;
40130 this.relayEvents(this.palette, ["select"]);
40131 if(this.selectHandler){
40132 this.on('select', this.selectHandler, this.scope);
40135 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40137 * Ext JS Library 1.1.1
40138 * Copyright(c) 2006-2007, Ext JS, LLC.
40140 * Originally Released Under LGPL - original licence link has changed is not relivant.
40143 * <script type="text/javascript">
40148 * @class Roo.menu.DateMenu
40149 * @extends Roo.menu.Menu
40150 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40152 * Creates a new DateMenu
40153 * @param {Object} config Configuration options
40155 Roo.menu.DateMenu = function(config){
40156 Roo.menu.DateMenu.superclass.constructor.call(this, config);
40158 var di = new Roo.menu.DateItem(config);
40161 * The {@link Roo.DatePicker} instance for this DateMenu
40164 this.picker = di.picker;
40167 * @param {DatePicker} picker
40168 * @param {Date} date
40170 this.relayEvents(di, ["select"]);
40171 this.on('beforeshow', function(){
40173 this.picker.hideMonthPicker(false);
40177 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40181 * Ext JS Library 1.1.1
40182 * Copyright(c) 2006-2007, Ext JS, LLC.
40184 * Originally Released Under LGPL - original licence link has changed is not relivant.
40187 * <script type="text/javascript">
40192 * @class Roo.menu.ColorMenu
40193 * @extends Roo.menu.Menu
40194 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40196 * Creates a new ColorMenu
40197 * @param {Object} config Configuration options
40199 Roo.menu.ColorMenu = function(config){
40200 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40202 var ci = new Roo.menu.ColorItem(config);
40205 * The {@link Roo.ColorPalette} instance for this ColorMenu
40206 * @type ColorPalette
40208 this.palette = ci.palette;
40211 * @param {ColorPalette} palette
40212 * @param {String} color
40214 this.relayEvents(ci, ["select"]);
40216 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40218 * Ext JS Library 1.1.1
40219 * Copyright(c) 2006-2007, Ext JS, LLC.
40221 * Originally Released Under LGPL - original licence link has changed is not relivant.
40224 * <script type="text/javascript">
40228 * @class Roo.form.TextItem
40229 * @extends Roo.BoxComponent
40230 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40232 * Creates a new TextItem
40233 * @param {Object} config Configuration options
40235 Roo.form.TextItem = function(config){
40236 Roo.form.TextItem.superclass.constructor.call(this, config);
40239 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
40242 * @cfg {String} tag the tag for this item (default div)
40246 * @cfg {String} html the content for this item
40250 getAutoCreate : function()
40263 onRender : function(ct, position)
40265 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40268 var cfg = this.getAutoCreate();
40270 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40272 if (!cfg.name.length) {
40275 this.el = ct.createChild(cfg, position);
40280 * @param {String} html update the Contents of the element.
40282 setHTML : function(html)
40284 this.fieldEl.dom.innerHTML = html;
40289 * Ext JS Library 1.1.1
40290 * Copyright(c) 2006-2007, Ext JS, LLC.
40292 * Originally Released Under LGPL - original licence link has changed is not relivant.
40295 * <script type="text/javascript">
40299 * @class Roo.form.Field
40300 * @extends Roo.BoxComponent
40301 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40303 * Creates a new Field
40304 * @param {Object} config Configuration options
40306 Roo.form.Field = function(config){
40307 Roo.form.Field.superclass.constructor.call(this, config);
40310 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
40312 * @cfg {String} fieldLabel Label to use when rendering a form.
40315 * @cfg {String} qtip Mouse over tip
40319 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40321 invalidClass : "x-form-invalid",
40323 * @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")
40325 invalidText : "The value in this field is invalid",
40327 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40329 focusClass : "x-form-focus",
40331 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40332 automatic validation (defaults to "keyup").
40334 validationEvent : "keyup",
40336 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40338 validateOnBlur : true,
40340 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40342 validationDelay : 250,
40344 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40345 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40347 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40349 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40351 fieldClass : "x-form-field",
40353 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
40356 ----------- ----------------------------------------------------------------------
40357 qtip Display a quick tip when the user hovers over the field
40358 title Display a default browser title attribute popup
40359 under Add a block div beneath the field containing the error text
40360 side Add an error icon to the right of the field with a popup on hover
40361 [element id] Add the error text directly to the innerHTML of the specified element
40364 msgTarget : 'qtip',
40366 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40371 * @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.
40376 * @cfg {Boolean} disabled True to disable the field (defaults to false).
40381 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40383 inputType : undefined,
40386 * @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).
40388 tabIndex : undefined,
40391 isFormField : true,
40396 * @property {Roo.Element} fieldEl
40397 * Element Containing the rendered Field (with label etc.)
40400 * @cfg {Mixed} value A value to initialize this field with.
40405 * @cfg {String} name The field's HTML name attribute.
40408 * @cfg {String} cls A CSS class to apply to the field's underlying element.
40411 loadedValue : false,
40415 initComponent : function(){
40416 Roo.form.Field.superclass.initComponent.call(this);
40420 * Fires when this field receives input focus.
40421 * @param {Roo.form.Field} this
40426 * Fires when this field loses input focus.
40427 * @param {Roo.form.Field} this
40431 * @event specialkey
40432 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
40433 * {@link Roo.EventObject#getKey} to determine which key was pressed.
40434 * @param {Roo.form.Field} this
40435 * @param {Roo.EventObject} e The event object
40440 * Fires just before the field blurs if the field value has changed.
40441 * @param {Roo.form.Field} this
40442 * @param {Mixed} newValue The new value
40443 * @param {Mixed} oldValue The original value
40448 * Fires after the field has been marked as invalid.
40449 * @param {Roo.form.Field} this
40450 * @param {String} msg The validation message
40455 * Fires after the field has been validated with no errors.
40456 * @param {Roo.form.Field} this
40461 * Fires after the key up
40462 * @param {Roo.form.Field} this
40463 * @param {Roo.EventObject} e The event Object
40470 * Returns the name attribute of the field if available
40471 * @return {String} name The field name
40473 getName: function(){
40474 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40478 onRender : function(ct, position){
40479 Roo.form.Field.superclass.onRender.call(this, ct, position);
40481 var cfg = this.getAutoCreate();
40483 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40485 if (!cfg.name.length) {
40488 if(this.inputType){
40489 cfg.type = this.inputType;
40491 this.el = ct.createChild(cfg, position);
40493 var type = this.el.dom.type;
40495 if(type == 'password'){
40498 this.el.addClass('x-form-'+type);
40501 this.el.dom.readOnly = true;
40503 if(this.tabIndex !== undefined){
40504 this.el.dom.setAttribute('tabIndex', this.tabIndex);
40507 this.el.addClass([this.fieldClass, this.cls]);
40512 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40513 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40514 * @return {Roo.form.Field} this
40516 applyTo : function(target){
40517 this.allowDomMove = false;
40518 this.el = Roo.get(target);
40519 this.render(this.el.dom.parentNode);
40524 initValue : function(){
40525 if(this.value !== undefined){
40526 this.setValue(this.value);
40527 }else if(this.el.dom.value.length > 0){
40528 this.setValue(this.el.dom.value);
40533 * Returns true if this field has been changed since it was originally loaded and is not disabled.
40534 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
40536 isDirty : function() {
40537 if(this.disabled) {
40540 return String(this.getValue()) !== String(this.originalValue);
40544 * stores the current value in loadedValue
40546 resetHasChanged : function()
40548 this.loadedValue = String(this.getValue());
40551 * checks the current value against the 'loaded' value.
40552 * Note - will return false if 'resetHasChanged' has not been called first.
40554 hasChanged : function()
40556 if(this.disabled || this.readOnly) {
40559 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40565 afterRender : function(){
40566 Roo.form.Field.superclass.afterRender.call(this);
40571 fireKey : function(e){
40572 //Roo.log('field ' + e.getKey());
40573 if(e.isNavKeyPress()){
40574 this.fireEvent("specialkey", this, e);
40579 * Resets the current field value to the originally loaded value and clears any validation messages
40581 reset : function(){
40582 this.setValue(this.resetValue);
40583 this.originalValue = this.getValue();
40584 this.clearInvalid();
40588 initEvents : function(){
40589 // safari killled keypress - so keydown is now used..
40590 this.el.on("keydown" , this.fireKey, this);
40591 this.el.on("focus", this.onFocus, this);
40592 this.el.on("blur", this.onBlur, this);
40593 this.el.relayEvent('keyup', this);
40595 // reference to original value for reset
40596 this.originalValue = this.getValue();
40597 this.resetValue = this.getValue();
40601 onFocus : function(){
40602 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40603 this.el.addClass(this.focusClass);
40605 if(!this.hasFocus){
40606 this.hasFocus = true;
40607 this.startValue = this.getValue();
40608 this.fireEvent("focus", this);
40612 beforeBlur : Roo.emptyFn,
40615 onBlur : function(){
40617 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40618 this.el.removeClass(this.focusClass);
40620 this.hasFocus = false;
40621 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40624 var v = this.getValue();
40625 if(String(v) !== String(this.startValue)){
40626 this.fireEvent('change', this, v, this.startValue);
40628 this.fireEvent("blur", this);
40632 * Returns whether or not the field value is currently valid
40633 * @param {Boolean} preventMark True to disable marking the field invalid
40634 * @return {Boolean} True if the value is valid, else false
40636 isValid : function(preventMark){
40640 var restore = this.preventMark;
40641 this.preventMark = preventMark === true;
40642 var v = this.validateValue(this.processValue(this.getRawValue()));
40643 this.preventMark = restore;
40648 * Validates the field value
40649 * @return {Boolean} True if the value is valid, else false
40651 validate : function(){
40652 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40653 this.clearInvalid();
40659 processValue : function(value){
40664 // Subclasses should provide the validation implementation by overriding this
40665 validateValue : function(value){
40670 * Mark this field as invalid
40671 * @param {String} msg The validation message
40673 markInvalid : function(msg){
40674 if(!this.rendered || this.preventMark){ // not rendered
40678 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40680 obj.el.addClass(this.invalidClass);
40681 msg = msg || this.invalidText;
40682 switch(this.msgTarget){
40684 obj.el.dom.qtip = msg;
40685 obj.el.dom.qclass = 'x-form-invalid-tip';
40686 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40687 Roo.QuickTips.enable();
40691 this.el.dom.title = msg;
40695 var elp = this.el.findParent('.x-form-element', 5, true);
40696 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40697 this.errorEl.setWidth(elp.getWidth(true)-20);
40699 this.errorEl.update(msg);
40700 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40703 if(!this.errorIcon){
40704 var elp = this.el.findParent('.x-form-element', 5, true);
40705 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40707 this.alignErrorIcon();
40708 this.errorIcon.dom.qtip = msg;
40709 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40710 this.errorIcon.show();
40711 this.on('resize', this.alignErrorIcon, this);
40714 var t = Roo.getDom(this.msgTarget);
40716 t.style.display = this.msgDisplay;
40719 this.fireEvent('invalid', this, msg);
40723 alignErrorIcon : function(){
40724 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40728 * Clear any invalid styles/messages for this field
40730 clearInvalid : function(){
40731 if(!this.rendered || this.preventMark){ // not rendered
40734 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40736 obj.el.removeClass(this.invalidClass);
40737 switch(this.msgTarget){
40739 obj.el.dom.qtip = '';
40742 this.el.dom.title = '';
40746 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40750 if(this.errorIcon){
40751 this.errorIcon.dom.qtip = '';
40752 this.errorIcon.hide();
40753 this.un('resize', this.alignErrorIcon, this);
40757 var t = Roo.getDom(this.msgTarget);
40759 t.style.display = 'none';
40762 this.fireEvent('valid', this);
40766 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
40767 * @return {Mixed} value The field value
40769 getRawValue : function(){
40770 var v = this.el.getValue();
40776 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
40777 * @return {Mixed} value The field value
40779 getValue : function(){
40780 var v = this.el.getValue();
40786 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
40787 * @param {Mixed} value The value to set
40789 setRawValue : function(v){
40790 return this.el.dom.value = (v === null || v === undefined ? '' : v);
40794 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
40795 * @param {Mixed} value The value to set
40797 setValue : function(v){
40800 this.el.dom.value = (v === null || v === undefined ? '' : v);
40805 adjustSize : function(w, h){
40806 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40807 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40811 adjustWidth : function(tag, w){
40812 tag = tag.toLowerCase();
40813 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40814 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40815 if(tag == 'input'){
40818 if(tag == 'textarea'){
40821 }else if(Roo.isOpera){
40822 if(tag == 'input'){
40825 if(tag == 'textarea'){
40835 // anything other than normal should be considered experimental
40836 Roo.form.Field.msgFx = {
40838 show: function(msgEl, f){
40839 msgEl.setDisplayed('block');
40842 hide : function(msgEl, f){
40843 msgEl.setDisplayed(false).update('');
40848 show: function(msgEl, f){
40849 msgEl.slideIn('t', {stopFx:true});
40852 hide : function(msgEl, f){
40853 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40858 show: function(msgEl, f){
40859 msgEl.fixDisplay();
40860 msgEl.alignTo(f.el, 'tl-tr');
40861 msgEl.slideIn('l', {stopFx:true});
40864 hide : function(msgEl, f){
40865 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40870 * Ext JS Library 1.1.1
40871 * Copyright(c) 2006-2007, Ext JS, LLC.
40873 * Originally Released Under LGPL - original licence link has changed is not relivant.
40876 * <script type="text/javascript">
40881 * @class Roo.form.TextField
40882 * @extends Roo.form.Field
40883 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
40884 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40886 * Creates a new TextField
40887 * @param {Object} config Configuration options
40889 Roo.form.TextField = function(config){
40890 Roo.form.TextField.superclass.constructor.call(this, config);
40894 * Fires when the autosize function is triggered. The field may or may not have actually changed size
40895 * according to the default logic, but this event provides a hook for the developer to apply additional
40896 * logic at runtime to resize the field if needed.
40897 * @param {Roo.form.Field} this This text field
40898 * @param {Number} width The new field width
40904 Roo.extend(Roo.form.TextField, Roo.form.Field, {
40906 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40910 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40914 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40918 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40922 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40926 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40928 disableKeyFilter : false,
40930 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40934 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40938 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40940 maxLength : Number.MAX_VALUE,
40942 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40944 minLengthText : "The minimum length for this field is {0}",
40946 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40948 maxLengthText : "The maximum length for this field is {0}",
40950 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40952 selectOnFocus : false,
40954 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
40956 allowLeadingSpace : false,
40958 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40960 blankText : "This field is required",
40962 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40963 * If available, this function will be called only after the basic validators all return true, and will be passed the
40964 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40968 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40969 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40970 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
40974 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40978 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40984 initEvents : function()
40986 if (this.emptyText) {
40987 this.el.attr('placeholder', this.emptyText);
40990 Roo.form.TextField.superclass.initEvents.call(this);
40991 if(this.validationEvent == 'keyup'){
40992 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40993 this.el.on('keyup', this.filterValidation, this);
40995 else if(this.validationEvent !== false){
40996 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40999 if(this.selectOnFocus){
41000 this.on("focus", this.preFocus, this);
41002 if (!this.allowLeadingSpace) {
41003 this.on('blur', this.cleanLeadingSpace, this);
41006 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41007 this.el.on("keypress", this.filterKeys, this);
41010 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
41011 this.el.on("click", this.autoSize, this);
41013 if(this.el.is('input[type=password]') && Roo.isSafari){
41014 this.el.on('keydown', this.SafariOnKeyDown, this);
41018 processValue : function(value){
41019 if(this.stripCharsRe){
41020 var newValue = value.replace(this.stripCharsRe, '');
41021 if(newValue !== value){
41022 this.setRawValue(newValue);
41029 filterValidation : function(e){
41030 if(!e.isNavKeyPress()){
41031 this.validationTask.delay(this.validationDelay);
41036 onKeyUp : function(e){
41037 if(!e.isNavKeyPress()){
41041 // private - clean the leading white space
41042 cleanLeadingSpace : function(e)
41044 if ( this.inputType == 'file') {
41048 this.setValue((this.getValue() + '').replace(/^\s+/,''));
41051 * Resets the current field value to the originally-loaded value and clears any validation messages.
41054 reset : function(){
41055 Roo.form.TextField.superclass.reset.call(this);
41059 preFocus : function(){
41061 if(this.selectOnFocus){
41062 this.el.dom.select();
41068 filterKeys : function(e){
41069 var k = e.getKey();
41070 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41073 var c = e.getCharCode(), cc = String.fromCharCode(c);
41074 if(Roo.isIE && (e.isSpecialKey() || !cc)){
41077 if(!this.maskRe.test(cc)){
41082 setValue : function(v){
41084 Roo.form.TextField.superclass.setValue.apply(this, arguments);
41090 * Validates a value according to the field's validation rules and marks the field as invalid
41091 * if the validation fails
41092 * @param {Mixed} value The value to validate
41093 * @return {Boolean} True if the value is valid, else false
41095 validateValue : function(value){
41096 if(value.length < 1) { // if it's blank
41097 if(this.allowBlank){
41098 this.clearInvalid();
41101 this.markInvalid(this.blankText);
41105 if(value.length < this.minLength){
41106 this.markInvalid(String.format(this.minLengthText, this.minLength));
41109 if(value.length > this.maxLength){
41110 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41114 var vt = Roo.form.VTypes;
41115 if(!vt[this.vtype](value, this)){
41116 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41120 if(typeof this.validator == "function"){
41121 var msg = this.validator(value);
41123 this.markInvalid(msg);
41127 if(this.regex && !this.regex.test(value)){
41128 this.markInvalid(this.regexText);
41135 * Selects text in this field
41136 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41137 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41139 selectText : function(start, end){
41140 var v = this.getRawValue();
41142 start = start === undefined ? 0 : start;
41143 end = end === undefined ? v.length : end;
41144 var d = this.el.dom;
41145 if(d.setSelectionRange){
41146 d.setSelectionRange(start, end);
41147 }else if(d.createTextRange){
41148 var range = d.createTextRange();
41149 range.moveStart("character", start);
41150 range.moveEnd("character", v.length-end);
41157 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41158 * This only takes effect if grow = true, and fires the autosize event.
41160 autoSize : function(){
41161 if(!this.grow || !this.rendered){
41165 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41168 var v = el.dom.value;
41169 var d = document.createElement('div');
41170 d.appendChild(document.createTextNode(v));
41174 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41175 this.el.setWidth(w);
41176 this.fireEvent("autosize", this, w);
41180 SafariOnKeyDown : function(event)
41182 // this is a workaround for a password hang bug on chrome/ webkit.
41184 var isSelectAll = false;
41186 if(this.el.dom.selectionEnd > 0){
41187 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41189 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41190 event.preventDefault();
41195 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41197 event.preventDefault();
41198 // this is very hacky as keydown always get's upper case.
41200 var cc = String.fromCharCode(event.getCharCode());
41203 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
41211 * Ext JS Library 1.1.1
41212 * Copyright(c) 2006-2007, Ext JS, LLC.
41214 * Originally Released Under LGPL - original licence link has changed is not relivant.
41217 * <script type="text/javascript">
41221 * @class Roo.form.Hidden
41222 * @extends Roo.form.TextField
41223 * Simple Hidden element used on forms
41225 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41228 * Creates a new Hidden form element.
41229 * @param {Object} config Configuration options
41234 // easy hidden field...
41235 Roo.form.Hidden = function(config){
41236 Roo.form.Hidden.superclass.constructor.call(this, config);
41239 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41241 inputType: 'hidden',
41244 labelSeparator: '',
41246 itemCls : 'x-form-item-display-none'
41254 * Ext JS Library 1.1.1
41255 * Copyright(c) 2006-2007, Ext JS, LLC.
41257 * Originally Released Under LGPL - original licence link has changed is not relivant.
41260 * <script type="text/javascript">
41264 * @class Roo.form.TriggerField
41265 * @extends Roo.form.TextField
41266 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41267 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41268 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41269 * for which you can provide a custom implementation. For example:
41271 var trigger = new Roo.form.TriggerField();
41272 trigger.onTriggerClick = myTriggerFn;
41273 trigger.applyTo('my-field');
41276 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41277 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41278 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
41279 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41281 * Create a new TriggerField.
41282 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41283 * to the base TextField)
41285 Roo.form.TriggerField = function(config){
41286 this.mimicing = false;
41287 Roo.form.TriggerField.superclass.constructor.call(this, config);
41290 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
41292 * @cfg {String} triggerClass A CSS class to apply to the trigger
41295 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41296 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41298 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41300 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41304 /** @cfg {Boolean} grow @hide */
41305 /** @cfg {Number} growMin @hide */
41306 /** @cfg {Number} growMax @hide */
41312 autoSize: Roo.emptyFn,
41316 deferHeight : true,
41319 actionMode : 'wrap',
41321 onResize : function(w, h){
41322 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41323 if(typeof w == 'number'){
41324 var x = w - this.trigger.getWidth();
41325 this.el.setWidth(this.adjustWidth('input', x));
41326 this.trigger.setStyle('left', x+'px');
41331 adjustSize : Roo.BoxComponent.prototype.adjustSize,
41334 getResizeEl : function(){
41339 getPositionEl : function(){
41344 alignErrorIcon : function(){
41345 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41349 onRender : function(ct, position){
41350 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41351 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41352 this.trigger = this.wrap.createChild(this.triggerConfig ||
41353 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41354 if(this.hideTrigger){
41355 this.trigger.setDisplayed(false);
41357 this.initTrigger();
41359 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41364 initTrigger : function(){
41365 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41366 this.trigger.addClassOnOver('x-form-trigger-over');
41367 this.trigger.addClassOnClick('x-form-trigger-click');
41371 onDestroy : function(){
41373 this.trigger.removeAllListeners();
41374 this.trigger.remove();
41377 this.wrap.remove();
41379 Roo.form.TriggerField.superclass.onDestroy.call(this);
41383 onFocus : function(){
41384 Roo.form.TriggerField.superclass.onFocus.call(this);
41385 if(!this.mimicing){
41386 this.wrap.addClass('x-trigger-wrap-focus');
41387 this.mimicing = true;
41388 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41389 if(this.monitorTab){
41390 this.el.on("keydown", this.checkTab, this);
41396 checkTab : function(e){
41397 if(e.getKey() == e.TAB){
41398 this.triggerBlur();
41403 onBlur : function(){
41408 mimicBlur : function(e, t){
41409 if(!this.wrap.contains(t) && this.validateBlur()){
41410 this.triggerBlur();
41415 triggerBlur : function(){
41416 this.mimicing = false;
41417 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41418 if(this.monitorTab){
41419 this.el.un("keydown", this.checkTab, this);
41421 this.wrap.removeClass('x-trigger-wrap-focus');
41422 Roo.form.TriggerField.superclass.onBlur.call(this);
41426 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41427 validateBlur : function(e, t){
41432 onDisable : function(){
41433 Roo.form.TriggerField.superclass.onDisable.call(this);
41435 this.wrap.addClass('x-item-disabled');
41440 onEnable : function(){
41441 Roo.form.TriggerField.superclass.onEnable.call(this);
41443 this.wrap.removeClass('x-item-disabled');
41448 onShow : function(){
41449 var ae = this.getActionEl();
41452 ae.dom.style.display = '';
41453 ae.dom.style.visibility = 'visible';
41459 onHide : function(){
41460 var ae = this.getActionEl();
41461 ae.dom.style.display = 'none';
41465 * The function that should handle the trigger's click event. This method does nothing by default until overridden
41466 * by an implementing function.
41468 * @param {EventObject} e
41470 onTriggerClick : Roo.emptyFn
41473 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
41474 // to be extended by an implementing class. For an example of implementing this class, see the custom
41475 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41476 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41477 initComponent : function(){
41478 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41480 this.triggerConfig = {
41481 tag:'span', cls:'x-form-twin-triggers', cn:[
41482 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41483 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41487 getTrigger : function(index){
41488 return this.triggers[index];
41491 initTrigger : function(){
41492 var ts = this.trigger.select('.x-form-trigger', true);
41493 this.wrap.setStyle('overflow', 'hidden');
41494 var triggerField = this;
41495 ts.each(function(t, all, index){
41496 t.hide = function(){
41497 var w = triggerField.wrap.getWidth();
41498 this.dom.style.display = 'none';
41499 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41501 t.show = function(){
41502 var w = triggerField.wrap.getWidth();
41503 this.dom.style.display = '';
41504 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41506 var triggerIndex = 'Trigger'+(index+1);
41508 if(this['hide'+triggerIndex]){
41509 t.dom.style.display = 'none';
41511 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41512 t.addClassOnOver('x-form-trigger-over');
41513 t.addClassOnClick('x-form-trigger-click');
41515 this.triggers = ts.elements;
41518 onTrigger1Click : Roo.emptyFn,
41519 onTrigger2Click : Roo.emptyFn
41522 * Ext JS Library 1.1.1
41523 * Copyright(c) 2006-2007, Ext JS, LLC.
41525 * Originally Released Under LGPL - original licence link has changed is not relivant.
41528 * <script type="text/javascript">
41532 * @class Roo.form.TextArea
41533 * @extends Roo.form.TextField
41534 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
41535 * support for auto-sizing.
41537 * Creates a new TextArea
41538 * @param {Object} config Configuration options
41540 Roo.form.TextArea = function(config){
41541 Roo.form.TextArea.superclass.constructor.call(this, config);
41542 // these are provided exchanges for backwards compat
41543 // minHeight/maxHeight were replaced by growMin/growMax to be
41544 // compatible with TextField growing config values
41545 if(this.minHeight !== undefined){
41546 this.growMin = this.minHeight;
41548 if(this.maxHeight !== undefined){
41549 this.growMax = this.maxHeight;
41553 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
41555 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41559 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41563 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41564 * in the field (equivalent to setting overflow: hidden, defaults to false)
41566 preventScrollbars: false,
41568 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41569 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41573 onRender : function(ct, position){
41575 this.defaultAutoCreate = {
41577 style:"width:300px;height:60px;",
41578 autocomplete: "new-password"
41581 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41583 this.textSizeEl = Roo.DomHelper.append(document.body, {
41584 tag: "pre", cls: "x-form-grow-sizer"
41586 if(this.preventScrollbars){
41587 this.el.setStyle("overflow", "hidden");
41589 this.el.setHeight(this.growMin);
41593 onDestroy : function(){
41594 if(this.textSizeEl){
41595 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41597 Roo.form.TextArea.superclass.onDestroy.call(this);
41601 onKeyUp : function(e){
41602 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41608 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41609 * This only takes effect if grow = true, and fires the autosize event if the height changes.
41611 autoSize : function(){
41612 if(!this.grow || !this.textSizeEl){
41616 var v = el.dom.value;
41617 var ts = this.textSizeEl;
41620 ts.appendChild(document.createTextNode(v));
41623 Roo.fly(ts).setWidth(this.el.getWidth());
41625 v = "  ";
41628 v = v.replace(/\n/g, '<p> </p>');
41630 v += " \n ";
41633 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41634 if(h != this.lastHeight){
41635 this.lastHeight = h;
41636 this.el.setHeight(h);
41637 this.fireEvent("autosize", this, h);
41642 * Ext JS Library 1.1.1
41643 * Copyright(c) 2006-2007, Ext JS, LLC.
41645 * Originally Released Under LGPL - original licence link has changed is not relivant.
41648 * <script type="text/javascript">
41653 * @class Roo.form.NumberField
41654 * @extends Roo.form.TextField
41655 * Numeric text field that provides automatic keystroke filtering and numeric validation.
41657 * Creates a new NumberField
41658 * @param {Object} config Configuration options
41660 Roo.form.NumberField = function(config){
41661 Roo.form.NumberField.superclass.constructor.call(this, config);
41664 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
41666 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41668 fieldClass: "x-form-field x-form-num-field",
41670 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41672 allowDecimals : true,
41674 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41676 decimalSeparator : ".",
41678 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41680 decimalPrecision : 2,
41682 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41684 allowNegative : true,
41686 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41688 minValue : Number.NEGATIVE_INFINITY,
41690 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41692 maxValue : Number.MAX_VALUE,
41694 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41696 minText : "The minimum value for this field is {0}",
41698 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41700 maxText : "The maximum value for this field is {0}",
41702 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41703 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41705 nanText : "{0} is not a valid number",
41708 initEvents : function(){
41709 Roo.form.NumberField.superclass.initEvents.call(this);
41710 var allowed = "0123456789";
41711 if(this.allowDecimals){
41712 allowed += this.decimalSeparator;
41714 if(this.allowNegative){
41717 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41718 var keyPress = function(e){
41719 var k = e.getKey();
41720 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41723 var c = e.getCharCode();
41724 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41728 this.el.on("keypress", keyPress, this);
41732 validateValue : function(value){
41733 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41736 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41739 var num = this.parseValue(value);
41741 this.markInvalid(String.format(this.nanText, value));
41744 if(num < this.minValue){
41745 this.markInvalid(String.format(this.minText, this.minValue));
41748 if(num > this.maxValue){
41749 this.markInvalid(String.format(this.maxText, this.maxValue));
41755 getValue : function(){
41756 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41760 parseValue : function(value){
41761 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41762 return isNaN(value) ? '' : value;
41766 fixPrecision : function(value){
41767 var nan = isNaN(value);
41768 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41769 return nan ? '' : value;
41771 return parseFloat(value).toFixed(this.decimalPrecision);
41774 setValue : function(v){
41775 v = this.fixPrecision(v);
41776 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41780 decimalPrecisionFcn : function(v){
41781 return Math.floor(v);
41784 beforeBlur : function(){
41785 var v = this.parseValue(this.getRawValue());
41792 * Ext JS Library 1.1.1
41793 * Copyright(c) 2006-2007, Ext JS, LLC.
41795 * Originally Released Under LGPL - original licence link has changed is not relivant.
41798 * <script type="text/javascript">
41802 * @class Roo.form.DateField
41803 * @extends Roo.form.TriggerField
41804 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41806 * Create a new DateField
41807 * @param {Object} config
41809 Roo.form.DateField = function(config)
41811 Roo.form.DateField.superclass.constructor.call(this, config);
41817 * Fires when a date is selected
41818 * @param {Roo.form.DateField} combo This combo box
41819 * @param {Date} date The date selected
41826 if(typeof this.minValue == "string") {
41827 this.minValue = this.parseDate(this.minValue);
41829 if(typeof this.maxValue == "string") {
41830 this.maxValue = this.parseDate(this.maxValue);
41832 this.ddMatch = null;
41833 if(this.disabledDates){
41834 var dd = this.disabledDates;
41836 for(var i = 0; i < dd.length; i++){
41838 if(i != dd.length-1) {
41842 this.ddMatch = new RegExp(re + ")");
41846 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
41848 * @cfg {String} format
41849 * The default date format string which can be overriden for localization support. The format must be
41850 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41854 * @cfg {String} altFormats
41855 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41856 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41858 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41860 * @cfg {Array} disabledDays
41861 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41863 disabledDays : null,
41865 * @cfg {String} disabledDaysText
41866 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41868 disabledDaysText : "Disabled",
41870 * @cfg {Array} disabledDates
41871 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41872 * expression so they are very powerful. Some examples:
41874 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41875 * <li>["03/08", "09/16"] would disable those days for every year</li>
41876 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41877 * <li>["03/../2006"] would disable every day in March 2006</li>
41878 * <li>["^03"] would disable every day in every March</li>
41880 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41881 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41883 disabledDates : null,
41885 * @cfg {String} disabledDatesText
41886 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41888 disabledDatesText : "Disabled",
41892 * @cfg {Date/String} zeroValue
41893 * if the date is less that this number, then the field is rendered as empty
41896 zeroValue : '1800-01-01',
41900 * @cfg {Date/String} minValue
41901 * The minimum allowed date. Can be either a Javascript date object or a string date in a
41902 * valid format (defaults to null).
41906 * @cfg {Date/String} maxValue
41907 * The maximum allowed date. Can be either a Javascript date object or a string date in a
41908 * valid format (defaults to null).
41912 * @cfg {String} minText
41913 * The error text to display when the date in the cell is before minValue (defaults to
41914 * 'The date in this field must be after {minValue}').
41916 minText : "The date in this field must be equal to or after {0}",
41918 * @cfg {String} maxText
41919 * The error text to display when the date in the cell is after maxValue (defaults to
41920 * 'The date in this field must be before {maxValue}').
41922 maxText : "The date in this field must be equal to or before {0}",
41924 * @cfg {String} invalidText
41925 * The error text to display when the date in the field is invalid (defaults to
41926 * '{value} is not a valid date - it must be in the format {format}').
41928 invalidText : "{0} is not a valid date - it must be in the format {1}",
41930 * @cfg {String} triggerClass
41931 * An additional CSS class used to style the trigger button. The trigger will always get the
41932 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41933 * which displays a calendar icon).
41935 triggerClass : 'x-form-date-trigger',
41939 * @cfg {Boolean} useIso
41940 * if enabled, then the date field will use a hidden field to store the
41941 * real value as iso formated date. default (false)
41945 * @cfg {String/Object} autoCreate
41946 * A DomHelper element spec, or true for a default element spec (defaults to
41947 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41950 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41953 hiddenField: false,
41955 onRender : function(ct, position)
41957 Roo.form.DateField.superclass.onRender.call(this, ct, position);
41959 //this.el.dom.removeAttribute('name');
41960 Roo.log("Changing name?");
41961 this.el.dom.setAttribute('name', this.name + '____hidden___' );
41962 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41964 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41965 // prevent input submission
41966 this.hiddenName = this.name;
41973 validateValue : function(value)
41975 value = this.formatDate(value);
41976 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41977 Roo.log('super failed');
41980 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41983 var svalue = value;
41984 value = this.parseDate(value);
41986 Roo.log('parse date failed' + svalue);
41987 this.markInvalid(String.format(this.invalidText, svalue, this.format));
41990 var time = value.getTime();
41991 if(this.minValue && time < this.minValue.getTime()){
41992 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41995 if(this.maxValue && time > this.maxValue.getTime()){
41996 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41999 if(this.disabledDays){
42000 var day = value.getDay();
42001 for(var i = 0; i < this.disabledDays.length; i++) {
42002 if(day === this.disabledDays[i]){
42003 this.markInvalid(this.disabledDaysText);
42008 var fvalue = this.formatDate(value);
42009 if(this.ddMatch && this.ddMatch.test(fvalue)){
42010 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42017 // Provides logic to override the default TriggerField.validateBlur which just returns true
42018 validateBlur : function(){
42019 return !this.menu || !this.menu.isVisible();
42022 getName: function()
42024 // returns hidden if it's set..
42025 if (!this.rendered) {return ''};
42026 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42031 * Returns the current date value of the date field.
42032 * @return {Date} The date value
42034 getValue : function(){
42036 return this.hiddenField ?
42037 this.hiddenField.value :
42038 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42042 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42043 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42044 * (the default format used is "m/d/y").
42047 //All of these calls set the same date value (May 4, 2006)
42049 //Pass a date object:
42050 var dt = new Date('5/4/06');
42051 dateField.setValue(dt);
42053 //Pass a date string (default format):
42054 dateField.setValue('5/4/06');
42056 //Pass a date string (custom format):
42057 dateField.format = 'Y-m-d';
42058 dateField.setValue('2006-5-4');
42060 * @param {String/Date} date The date or valid date string
42062 setValue : function(date){
42063 if (this.hiddenField) {
42064 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42066 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42067 // make sure the value field is always stored as a date..
42068 this.value = this.parseDate(date);
42074 parseDate : function(value){
42076 if (value instanceof Date) {
42077 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42084 if(!value || value instanceof Date){
42087 var v = Date.parseDate(value, this.format);
42088 if (!v && this.useIso) {
42089 v = Date.parseDate(value, 'Y-m-d');
42091 if(!v && this.altFormats){
42092 if(!this.altFormatsArray){
42093 this.altFormatsArray = this.altFormats.split("|");
42095 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42096 v = Date.parseDate(value, this.altFormatsArray[i]);
42099 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42106 formatDate : function(date, fmt){
42107 return (!date || !(date instanceof Date)) ?
42108 date : date.dateFormat(fmt || this.format);
42113 select: function(m, d){
42116 this.fireEvent('select', this, d);
42118 show : function(){ // retain focus styling
42122 this.focus.defer(10, this);
42123 var ml = this.menuListeners;
42124 this.menu.un("select", ml.select, this);
42125 this.menu.un("show", ml.show, this);
42126 this.menu.un("hide", ml.hide, this);
42131 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42132 onTriggerClick : function(){
42136 if(this.menu == null){
42137 this.menu = new Roo.menu.DateMenu();
42139 Roo.apply(this.menu.picker, {
42140 showClear: this.allowBlank,
42141 minDate : this.minValue,
42142 maxDate : this.maxValue,
42143 disabledDatesRE : this.ddMatch,
42144 disabledDatesText : this.disabledDatesText,
42145 disabledDays : this.disabledDays,
42146 disabledDaysText : this.disabledDaysText,
42147 format : this.useIso ? 'Y-m-d' : this.format,
42148 minText : String.format(this.minText, this.formatDate(this.minValue)),
42149 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42151 this.menu.on(Roo.apply({}, this.menuListeners, {
42154 this.menu.picker.setValue(this.getValue() || new Date());
42155 this.menu.show(this.el, "tl-bl?");
42158 beforeBlur : function(){
42159 var v = this.parseDate(this.getRawValue());
42169 isDirty : function() {
42170 if(this.disabled) {
42174 if(typeof(this.startValue) === 'undefined'){
42178 return String(this.getValue()) !== String(this.startValue);
42182 cleanLeadingSpace : function(e)
42189 * Ext JS Library 1.1.1
42190 * Copyright(c) 2006-2007, Ext JS, LLC.
42192 * Originally Released Under LGPL - original licence link has changed is not relivant.
42195 * <script type="text/javascript">
42199 * @class Roo.form.MonthField
42200 * @extends Roo.form.TriggerField
42201 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42203 * Create a new MonthField
42204 * @param {Object} config
42206 Roo.form.MonthField = function(config){
42208 Roo.form.MonthField.superclass.constructor.call(this, config);
42214 * Fires when a date is selected
42215 * @param {Roo.form.MonthFieeld} combo This combo box
42216 * @param {Date} date The date selected
42223 if(typeof this.minValue == "string") {
42224 this.minValue = this.parseDate(this.minValue);
42226 if(typeof this.maxValue == "string") {
42227 this.maxValue = this.parseDate(this.maxValue);
42229 this.ddMatch = null;
42230 if(this.disabledDates){
42231 var dd = this.disabledDates;
42233 for(var i = 0; i < dd.length; i++){
42235 if(i != dd.length-1) {
42239 this.ddMatch = new RegExp(re + ")");
42243 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
42245 * @cfg {String} format
42246 * The default date format string which can be overriden for localization support. The format must be
42247 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42251 * @cfg {String} altFormats
42252 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42253 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42255 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42257 * @cfg {Array} disabledDays
42258 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42260 disabledDays : [0,1,2,3,4,5,6],
42262 * @cfg {String} disabledDaysText
42263 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42265 disabledDaysText : "Disabled",
42267 * @cfg {Array} disabledDates
42268 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42269 * expression so they are very powerful. Some examples:
42271 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42272 * <li>["03/08", "09/16"] would disable those days for every year</li>
42273 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42274 * <li>["03/../2006"] would disable every day in March 2006</li>
42275 * <li>["^03"] would disable every day in every March</li>
42277 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42278 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42280 disabledDates : null,
42282 * @cfg {String} disabledDatesText
42283 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42285 disabledDatesText : "Disabled",
42287 * @cfg {Date/String} minValue
42288 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42289 * valid format (defaults to null).
42293 * @cfg {Date/String} maxValue
42294 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42295 * valid format (defaults to null).
42299 * @cfg {String} minText
42300 * The error text to display when the date in the cell is before minValue (defaults to
42301 * 'The date in this field must be after {minValue}').
42303 minText : "The date in this field must be equal to or after {0}",
42305 * @cfg {String} maxTextf
42306 * The error text to display when the date in the cell is after maxValue (defaults to
42307 * 'The date in this field must be before {maxValue}').
42309 maxText : "The date in this field must be equal to or before {0}",
42311 * @cfg {String} invalidText
42312 * The error text to display when the date in the field is invalid (defaults to
42313 * '{value} is not a valid date - it must be in the format {format}').
42315 invalidText : "{0} is not a valid date - it must be in the format {1}",
42317 * @cfg {String} triggerClass
42318 * An additional CSS class used to style the trigger button. The trigger will always get the
42319 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42320 * which displays a calendar icon).
42322 triggerClass : 'x-form-date-trigger',
42326 * @cfg {Boolean} useIso
42327 * if enabled, then the date field will use a hidden field to store the
42328 * real value as iso formated date. default (true)
42332 * @cfg {String/Object} autoCreate
42333 * A DomHelper element spec, or true for a default element spec (defaults to
42334 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42337 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42340 hiddenField: false,
42342 hideMonthPicker : false,
42344 onRender : function(ct, position)
42346 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42348 this.el.dom.removeAttribute('name');
42349 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42351 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42352 // prevent input submission
42353 this.hiddenName = this.name;
42360 validateValue : function(value)
42362 value = this.formatDate(value);
42363 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42366 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42369 var svalue = value;
42370 value = this.parseDate(value);
42372 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42375 var time = value.getTime();
42376 if(this.minValue && time < this.minValue.getTime()){
42377 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42380 if(this.maxValue && time > this.maxValue.getTime()){
42381 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42384 /*if(this.disabledDays){
42385 var day = value.getDay();
42386 for(var i = 0; i < this.disabledDays.length; i++) {
42387 if(day === this.disabledDays[i]){
42388 this.markInvalid(this.disabledDaysText);
42394 var fvalue = this.formatDate(value);
42395 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42396 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42404 // Provides logic to override the default TriggerField.validateBlur which just returns true
42405 validateBlur : function(){
42406 return !this.menu || !this.menu.isVisible();
42410 * Returns the current date value of the date field.
42411 * @return {Date} The date value
42413 getValue : function(){
42417 return this.hiddenField ?
42418 this.hiddenField.value :
42419 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42423 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42424 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42425 * (the default format used is "m/d/y").
42428 //All of these calls set the same date value (May 4, 2006)
42430 //Pass a date object:
42431 var dt = new Date('5/4/06');
42432 monthField.setValue(dt);
42434 //Pass a date string (default format):
42435 monthField.setValue('5/4/06');
42437 //Pass a date string (custom format):
42438 monthField.format = 'Y-m-d';
42439 monthField.setValue('2006-5-4');
42441 * @param {String/Date} date The date or valid date string
42443 setValue : function(date){
42444 Roo.log('month setValue' + date);
42445 // can only be first of month..
42447 var val = this.parseDate(date);
42449 if (this.hiddenField) {
42450 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42452 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42453 this.value = this.parseDate(date);
42457 parseDate : function(value){
42458 if(!value || value instanceof Date){
42459 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42462 var v = Date.parseDate(value, this.format);
42463 if (!v && this.useIso) {
42464 v = Date.parseDate(value, 'Y-m-d');
42468 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42472 if(!v && this.altFormats){
42473 if(!this.altFormatsArray){
42474 this.altFormatsArray = this.altFormats.split("|");
42476 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42477 v = Date.parseDate(value, this.altFormatsArray[i]);
42484 formatDate : function(date, fmt){
42485 return (!date || !(date instanceof Date)) ?
42486 date : date.dateFormat(fmt || this.format);
42491 select: function(m, d){
42493 this.fireEvent('select', this, d);
42495 show : function(){ // retain focus styling
42499 this.focus.defer(10, this);
42500 var ml = this.menuListeners;
42501 this.menu.un("select", ml.select, this);
42502 this.menu.un("show", ml.show, this);
42503 this.menu.un("hide", ml.hide, this);
42507 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42508 onTriggerClick : function(){
42512 if(this.menu == null){
42513 this.menu = new Roo.menu.DateMenu();
42517 Roo.apply(this.menu.picker, {
42519 showClear: this.allowBlank,
42520 minDate : this.minValue,
42521 maxDate : this.maxValue,
42522 disabledDatesRE : this.ddMatch,
42523 disabledDatesText : this.disabledDatesText,
42525 format : this.useIso ? 'Y-m-d' : this.format,
42526 minText : String.format(this.minText, this.formatDate(this.minValue)),
42527 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42530 this.menu.on(Roo.apply({}, this.menuListeners, {
42538 // hide month picker get's called when we called by 'before hide';
42540 var ignorehide = true;
42541 p.hideMonthPicker = function(disableAnim){
42545 if(this.monthPicker){
42546 Roo.log("hideMonthPicker called");
42547 if(disableAnim === true){
42548 this.monthPicker.hide();
42550 this.monthPicker.slideOut('t', {duration:.2});
42551 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42552 p.fireEvent("select", this, this.value);
42558 Roo.log('picker set value');
42559 Roo.log(this.getValue());
42560 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42561 m.show(this.el, 'tl-bl?');
42562 ignorehide = false;
42563 // this will trigger hideMonthPicker..
42566 // hidden the day picker
42567 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42573 p.showMonthPicker.defer(100, p);
42579 beforeBlur : function(){
42580 var v = this.parseDate(this.getRawValue());
42586 /** @cfg {Boolean} grow @hide */
42587 /** @cfg {Number} growMin @hide */
42588 /** @cfg {Number} growMax @hide */
42595 * Ext JS Library 1.1.1
42596 * Copyright(c) 2006-2007, Ext JS, LLC.
42598 * Originally Released Under LGPL - original licence link has changed is not relivant.
42601 * <script type="text/javascript">
42606 * @class Roo.form.ComboBox
42607 * @extends Roo.form.TriggerField
42608 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42610 * Create a new ComboBox.
42611 * @param {Object} config Configuration options
42613 Roo.form.ComboBox = function(config){
42614 Roo.form.ComboBox.superclass.constructor.call(this, config);
42618 * Fires when the dropdown list is expanded
42619 * @param {Roo.form.ComboBox} combo This combo box
42624 * Fires when the dropdown list is collapsed
42625 * @param {Roo.form.ComboBox} combo This combo box
42629 * @event beforeselect
42630 * Fires before a list item is selected. Return false to cancel the selection.
42631 * @param {Roo.form.ComboBox} combo This combo box
42632 * @param {Roo.data.Record} record The data record returned from the underlying store
42633 * @param {Number} index The index of the selected item in the dropdown list
42635 'beforeselect' : true,
42638 * Fires when a list item is selected
42639 * @param {Roo.form.ComboBox} combo This combo box
42640 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42641 * @param {Number} index The index of the selected item in the dropdown list
42645 * @event beforequery
42646 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42647 * The event object passed has these properties:
42648 * @param {Roo.form.ComboBox} combo This combo box
42649 * @param {String} query The query
42650 * @param {Boolean} forceAll true to force "all" query
42651 * @param {Boolean} cancel true to cancel the query
42652 * @param {Object} e The query event object
42654 'beforequery': true,
42657 * Fires when the 'add' icon is pressed (add a listener to enable add button)
42658 * @param {Roo.form.ComboBox} combo This combo box
42663 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42664 * @param {Roo.form.ComboBox} combo This combo box
42665 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42671 if(this.transform){
42672 this.allowDomMove = false;
42673 var s = Roo.getDom(this.transform);
42674 if(!this.hiddenName){
42675 this.hiddenName = s.name;
42678 this.mode = 'local';
42679 var d = [], opts = s.options;
42680 for(var i = 0, len = opts.length;i < len; i++){
42682 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42684 this.value = value;
42686 d.push([value, o.text]);
42688 this.store = new Roo.data.SimpleStore({
42690 fields: ['value', 'text'],
42693 this.valueField = 'value';
42694 this.displayField = 'text';
42696 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42697 if(!this.lazyRender){
42698 this.target = true;
42699 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42700 s.parentNode.removeChild(s); // remove it
42701 this.render(this.el.parentNode);
42703 s.parentNode.removeChild(s); // remove it
42708 this.store = Roo.factory(this.store, Roo.data);
42711 this.selectedIndex = -1;
42712 if(this.mode == 'local'){
42713 if(config.queryDelay === undefined){
42714 this.queryDelay = 10;
42716 if(config.minChars === undefined){
42722 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42724 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42727 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42728 * rendering into an Roo.Editor, defaults to false)
42731 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42732 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42735 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42738 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42739 * the dropdown list (defaults to undefined, with no header element)
42743 * @cfg {String/Roo.Template} tpl The template to use to render the output
42747 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42749 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42751 listWidth: undefined,
42753 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42754 * mode = 'remote' or 'text' if mode = 'local')
42756 displayField: undefined,
42758 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42759 * mode = 'remote' or 'value' if mode = 'local').
42760 * Note: use of a valueField requires the user make a selection
42761 * in order for a value to be mapped.
42763 valueField: undefined,
42767 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42768 * field's data value (defaults to the underlying DOM element's name)
42770 hiddenName: undefined,
42772 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42776 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42778 selectedClass: 'x-combo-selected',
42780 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
42781 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42782 * which displays a downward arrow icon).
42784 triggerClass : 'x-form-arrow-trigger',
42786 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42790 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42791 * anchor positions (defaults to 'tl-bl')
42793 listAlign: 'tl-bl?',
42795 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42799 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
42800 * query specified by the allQuery config option (defaults to 'query')
42802 triggerAction: 'query',
42804 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42805 * (defaults to 4, does not apply if editable = false)
42809 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42810 * delay (typeAheadDelay) if it matches a known value (defaults to false)
42814 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42815 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42819 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42820 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
42824 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
42825 * when editable = true (defaults to false)
42827 selectOnFocus:false,
42829 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42831 queryParam: 'query',
42833 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
42834 * when mode = 'remote' (defaults to 'Loading...')
42836 loadingText: 'Loading...',
42838 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42842 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42846 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42847 * traditional select (defaults to true)
42851 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42855 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42859 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42860 * listWidth has a higher value)
42864 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42865 * allow the user to set arbitrary text into the field (defaults to false)
42867 forceSelection:false,
42869 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42870 * if typeAhead = true (defaults to 250)
42872 typeAheadDelay : 250,
42874 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42875 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42877 valueNotFoundText : undefined,
42879 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42881 blockFocus : false,
42884 * @cfg {Boolean} disableClear Disable showing of clear button.
42886 disableClear : false,
42888 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
42890 alwaysQuery : false,
42896 // element that contains real text value.. (when hidden is used..)
42899 onRender : function(ct, position)
42901 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42903 if(this.hiddenName){
42904 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
42906 this.hiddenField.value =
42907 this.hiddenValue !== undefined ? this.hiddenValue :
42908 this.value !== undefined ? this.value : '';
42910 // prevent input submission
42911 this.el.dom.removeAttribute('name');
42917 this.el.dom.setAttribute('autocomplete', 'off');
42920 var cls = 'x-combo-list';
42922 this.list = new Roo.Layer({
42923 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42926 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42927 this.list.setWidth(lw);
42928 this.list.swallowEvent('mousewheel');
42929 this.assetHeight = 0;
42932 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42933 this.assetHeight += this.header.getHeight();
42936 this.innerList = this.list.createChild({cls:cls+'-inner'});
42937 this.innerList.on('mouseover', this.onViewOver, this);
42938 this.innerList.on('mousemove', this.onViewMove, this);
42939 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42941 if(this.allowBlank && !this.pageSize && !this.disableClear){
42942 this.footer = this.list.createChild({cls:cls+'-ft'});
42943 this.pageTb = new Roo.Toolbar(this.footer);
42947 this.footer = this.list.createChild({cls:cls+'-ft'});
42948 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42949 {pageSize: this.pageSize});
42953 if (this.pageTb && this.allowBlank && !this.disableClear) {
42955 this.pageTb.add(new Roo.Toolbar.Fill(), {
42956 cls: 'x-btn-icon x-btn-clear',
42958 handler: function()
42961 _this.clearValue();
42962 _this.onSelect(false, -1);
42967 this.assetHeight += this.footer.getHeight();
42972 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42975 this.view = new Roo.View(this.innerList, this.tpl, {
42978 selectedClass: this.selectedClass
42981 this.view.on('click', this.onViewClick, this);
42983 this.store.on('beforeload', this.onBeforeLoad, this);
42984 this.store.on('load', this.onLoad, this);
42985 this.store.on('loadexception', this.onLoadException, this);
42987 if(this.resizable){
42988 this.resizer = new Roo.Resizable(this.list, {
42989 pinned:true, handles:'se'
42991 this.resizer.on('resize', function(r, w, h){
42992 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42993 this.listWidth = w;
42994 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42995 this.restrictHeight();
42997 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42999 if(!this.editable){
43000 this.editable = true;
43001 this.setEditable(false);
43005 if (typeof(this.events.add.listeners) != 'undefined') {
43007 this.addicon = this.wrap.createChild(
43008 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
43010 this.addicon.on('click', function(e) {
43011 this.fireEvent('add', this);
43014 if (typeof(this.events.edit.listeners) != 'undefined') {
43016 this.editicon = this.wrap.createChild(
43017 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
43018 if (this.addicon) {
43019 this.editicon.setStyle('margin-left', '40px');
43021 this.editicon.on('click', function(e) {
43023 // we fire even if inothing is selected..
43024 this.fireEvent('edit', this, this.lastData );
43034 initEvents : function(){
43035 Roo.form.ComboBox.superclass.initEvents.call(this);
43037 this.keyNav = new Roo.KeyNav(this.el, {
43038 "up" : function(e){
43039 this.inKeyMode = true;
43043 "down" : function(e){
43044 if(!this.isExpanded()){
43045 this.onTriggerClick();
43047 this.inKeyMode = true;
43052 "enter" : function(e){
43053 this.onViewClick();
43057 "esc" : function(e){
43061 "tab" : function(e){
43062 this.onViewClick(false);
43063 this.fireEvent("specialkey", this, e);
43069 doRelay : function(foo, bar, hname){
43070 if(hname == 'down' || this.scope.isExpanded()){
43071 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43078 this.queryDelay = Math.max(this.queryDelay || 10,
43079 this.mode == 'local' ? 10 : 250);
43080 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43081 if(this.typeAhead){
43082 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43084 if(this.editable !== false){
43085 this.el.on("keyup", this.onKeyUp, this);
43087 if(this.forceSelection){
43088 this.on('blur', this.doForce, this);
43092 onDestroy : function(){
43094 this.view.setStore(null);
43095 this.view.el.removeAllListeners();
43096 this.view.el.remove();
43097 this.view.purgeListeners();
43100 this.list.destroy();
43103 this.store.un('beforeload', this.onBeforeLoad, this);
43104 this.store.un('load', this.onLoad, this);
43105 this.store.un('loadexception', this.onLoadException, this);
43107 Roo.form.ComboBox.superclass.onDestroy.call(this);
43111 fireKey : function(e){
43112 if(e.isNavKeyPress() && !this.list.isVisible()){
43113 this.fireEvent("specialkey", this, e);
43118 onResize: function(w, h){
43119 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43121 if(typeof w != 'number'){
43122 // we do not handle it!?!?
43125 var tw = this.trigger.getWidth();
43126 tw += this.addicon ? this.addicon.getWidth() : 0;
43127 tw += this.editicon ? this.editicon.getWidth() : 0;
43129 this.el.setWidth( this.adjustWidth('input', x));
43131 this.trigger.setStyle('left', x+'px');
43133 if(this.list && this.listWidth === undefined){
43134 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43135 this.list.setWidth(lw);
43136 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43144 * Allow or prevent the user from directly editing the field text. If false is passed,
43145 * the user will only be able to select from the items defined in the dropdown list. This method
43146 * is the runtime equivalent of setting the 'editable' config option at config time.
43147 * @param {Boolean} value True to allow the user to directly edit the field text
43149 setEditable : function(value){
43150 if(value == this.editable){
43153 this.editable = value;
43155 this.el.dom.setAttribute('readOnly', true);
43156 this.el.on('mousedown', this.onTriggerClick, this);
43157 this.el.addClass('x-combo-noedit');
43159 this.el.dom.setAttribute('readOnly', false);
43160 this.el.un('mousedown', this.onTriggerClick, this);
43161 this.el.removeClass('x-combo-noedit');
43166 onBeforeLoad : function(){
43167 if(!this.hasFocus){
43170 this.innerList.update(this.loadingText ?
43171 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43172 this.restrictHeight();
43173 this.selectedIndex = -1;
43177 onLoad : function(){
43178 if(!this.hasFocus){
43181 if(this.store.getCount() > 0){
43183 this.restrictHeight();
43184 if(this.lastQuery == this.allQuery){
43186 this.el.dom.select();
43188 if(!this.selectByValue(this.value, true)){
43189 this.select(0, true);
43193 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43194 this.taTask.delay(this.typeAheadDelay);
43198 this.onEmptyResults();
43203 onLoadException : function()
43206 Roo.log(this.store.reader.jsonData);
43207 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43208 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43214 onTypeAhead : function(){
43215 if(this.store.getCount() > 0){
43216 var r = this.store.getAt(0);
43217 var newValue = r.data[this.displayField];
43218 var len = newValue.length;
43219 var selStart = this.getRawValue().length;
43220 if(selStart != len){
43221 this.setRawValue(newValue);
43222 this.selectText(selStart, newValue.length);
43228 onSelect : function(record, index){
43229 if(this.fireEvent('beforeselect', this, record, index) !== false){
43230 this.setFromData(index > -1 ? record.data : false);
43232 this.fireEvent('select', this, record, index);
43237 * Returns the currently selected field value or empty string if no value is set.
43238 * @return {String} value The selected value
43240 getValue : function(){
43241 if(this.valueField){
43242 return typeof this.value != 'undefined' ? this.value : '';
43244 return Roo.form.ComboBox.superclass.getValue.call(this);
43248 * Clears any text/value currently set in the field
43250 clearValue : function(){
43251 if(this.hiddenField){
43252 this.hiddenField.value = '';
43255 this.setRawValue('');
43256 this.lastSelectionText = '';
43261 * Sets the specified value into the field. If the value finds a match, the corresponding record text
43262 * will be displayed in the field. If the value does not match the data value of an existing item,
43263 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43264 * Otherwise the field will be blank (although the value will still be set).
43265 * @param {String} value The value to match
43267 setValue : function(v){
43269 if(this.valueField){
43270 var r = this.findRecord(this.valueField, v);
43272 text = r.data[this.displayField];
43273 }else if(this.valueNotFoundText !== undefined){
43274 text = this.valueNotFoundText;
43277 this.lastSelectionText = text;
43278 if(this.hiddenField){
43279 this.hiddenField.value = v;
43281 Roo.form.ComboBox.superclass.setValue.call(this, text);
43285 * @property {Object} the last set data for the element
43290 * Sets the value of the field based on a object which is related to the record format for the store.
43291 * @param {Object} value the value to set as. or false on reset?
43293 setFromData : function(o){
43294 var dv = ''; // display value
43295 var vv = ''; // value value..
43297 if (this.displayField) {
43298 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43300 // this is an error condition!!!
43301 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
43304 if(this.valueField){
43305 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43307 if(this.hiddenField){
43308 this.hiddenField.value = vv;
43310 this.lastSelectionText = dv;
43311 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43315 // no hidden field.. - we store the value in 'value', but still display
43316 // display field!!!!
43317 this.lastSelectionText = dv;
43318 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43324 reset : function(){
43325 // overridden so that last data is reset..
43326 this.setValue(this.resetValue);
43327 this.originalValue = this.getValue();
43328 this.clearInvalid();
43329 this.lastData = false;
43331 this.view.clearSelections();
43335 findRecord : function(prop, value){
43337 if(this.store.getCount() > 0){
43338 this.store.each(function(r){
43339 if(r.data[prop] == value){
43349 getName: function()
43351 // returns hidden if it's set..
43352 if (!this.rendered) {return ''};
43353 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
43357 onViewMove : function(e, t){
43358 this.inKeyMode = false;
43362 onViewOver : function(e, t){
43363 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43366 var item = this.view.findItemFromChild(t);
43368 var index = this.view.indexOf(item);
43369 this.select(index, false);
43374 onViewClick : function(doFocus)
43376 var index = this.view.getSelectedIndexes()[0];
43377 var r = this.store.getAt(index);
43379 this.onSelect(r, index);
43381 if(doFocus !== false && !this.blockFocus){
43387 restrictHeight : function(){
43388 this.innerList.dom.style.height = '';
43389 var inner = this.innerList.dom;
43390 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43391 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43392 this.list.beginUpdate();
43393 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43394 this.list.alignTo(this.el, this.listAlign);
43395 this.list.endUpdate();
43399 onEmptyResults : function(){
43404 * Returns true if the dropdown list is expanded, else false.
43406 isExpanded : function(){
43407 return this.list.isVisible();
43411 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43412 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43413 * @param {String} value The data value of the item to select
43414 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43415 * selected item if it is not currently in view (defaults to true)
43416 * @return {Boolean} True if the value matched an item in the list, else false
43418 selectByValue : function(v, scrollIntoView){
43419 if(v !== undefined && v !== null){
43420 var r = this.findRecord(this.valueField || this.displayField, v);
43422 this.select(this.store.indexOf(r), scrollIntoView);
43430 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43431 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43432 * @param {Number} index The zero-based index of the list item to select
43433 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43434 * selected item if it is not currently in view (defaults to true)
43436 select : function(index, scrollIntoView){
43437 this.selectedIndex = index;
43438 this.view.select(index);
43439 if(scrollIntoView !== false){
43440 var el = this.view.getNode(index);
43442 this.innerList.scrollChildIntoView(el, false);
43448 selectNext : function(){
43449 var ct = this.store.getCount();
43451 if(this.selectedIndex == -1){
43453 }else if(this.selectedIndex < ct-1){
43454 this.select(this.selectedIndex+1);
43460 selectPrev : function(){
43461 var ct = this.store.getCount();
43463 if(this.selectedIndex == -1){
43465 }else if(this.selectedIndex != 0){
43466 this.select(this.selectedIndex-1);
43472 onKeyUp : function(e){
43473 if(this.editable !== false && !e.isSpecialKey()){
43474 this.lastKey = e.getKey();
43475 this.dqTask.delay(this.queryDelay);
43480 validateBlur : function(){
43481 return !this.list || !this.list.isVisible();
43485 initQuery : function(){
43486 this.doQuery(this.getRawValue());
43490 doForce : function(){
43491 if(this.el.dom.value.length > 0){
43492 this.el.dom.value =
43493 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43499 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
43500 * query allowing the query action to be canceled if needed.
43501 * @param {String} query The SQL query to execute
43502 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43503 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
43504 * saved in the current store (defaults to false)
43506 doQuery : function(q, forceAll){
43507 if(q === undefined || q === null){
43512 forceAll: forceAll,
43516 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43520 forceAll = qe.forceAll;
43521 if(forceAll === true || (q.length >= this.minChars)){
43522 if(this.lastQuery != q || this.alwaysQuery){
43523 this.lastQuery = q;
43524 if(this.mode == 'local'){
43525 this.selectedIndex = -1;
43527 this.store.clearFilter();
43529 this.store.filter(this.displayField, q);
43533 this.store.baseParams[this.queryParam] = q;
43535 params: this.getParams(q)
43540 this.selectedIndex = -1;
43547 getParams : function(q){
43549 //p[this.queryParam] = q;
43552 p.limit = this.pageSize;
43558 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43560 collapse : function(){
43561 if(!this.isExpanded()){
43565 Roo.get(document).un('mousedown', this.collapseIf, this);
43566 Roo.get(document).un('mousewheel', this.collapseIf, this);
43567 if (!this.editable) {
43568 Roo.get(document).un('keydown', this.listKeyPress, this);
43570 this.fireEvent('collapse', this);
43574 collapseIf : function(e){
43575 if(!e.within(this.wrap) && !e.within(this.list)){
43581 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43583 expand : function(){
43584 if(this.isExpanded() || !this.hasFocus){
43587 this.list.alignTo(this.el, this.listAlign);
43589 Roo.get(document).on('mousedown', this.collapseIf, this);
43590 Roo.get(document).on('mousewheel', this.collapseIf, this);
43591 if (!this.editable) {
43592 Roo.get(document).on('keydown', this.listKeyPress, this);
43595 this.fireEvent('expand', this);
43599 // Implements the default empty TriggerField.onTriggerClick function
43600 onTriggerClick : function(){
43604 if(this.isExpanded()){
43606 if (!this.blockFocus) {
43611 this.hasFocus = true;
43612 if(this.triggerAction == 'all') {
43613 this.doQuery(this.allQuery, true);
43615 this.doQuery(this.getRawValue());
43617 if (!this.blockFocus) {
43622 listKeyPress : function(e)
43624 //Roo.log('listkeypress');
43625 // scroll to first matching element based on key pres..
43626 if (e.isSpecialKey()) {
43629 var k = String.fromCharCode(e.getKey()).toUpperCase();
43632 var csel = this.view.getSelectedNodes();
43633 var cselitem = false;
43635 var ix = this.view.indexOf(csel[0]);
43636 cselitem = this.store.getAt(ix);
43637 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43643 this.store.each(function(v) {
43645 // start at existing selection.
43646 if (cselitem.id == v.id) {
43652 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43653 match = this.store.indexOf(v);
43658 if (match === false) {
43659 return true; // no more action?
43662 this.view.select(match);
43663 var sn = Roo.get(this.view.getSelectedNodes()[0]);
43664 sn.scrollIntoView(sn.dom.parentNode, false);
43668 * @cfg {Boolean} grow
43672 * @cfg {Number} growMin
43676 * @cfg {Number} growMax
43684 * Copyright(c) 2010-2012, Roo J Solutions Limited
43691 * @class Roo.form.ComboBoxArray
43692 * @extends Roo.form.TextField
43693 * A facebook style adder... for lists of email / people / countries etc...
43694 * pick multiple items from a combo box, and shows each one.
43696 * Fred [x] Brian [x] [Pick another |v]
43699 * For this to work: it needs various extra information
43700 * - normal combo problay has
43702 * + displayField, valueField
43704 * For our purpose...
43707 * If we change from 'extends' to wrapping...
43714 * Create a new ComboBoxArray.
43715 * @param {Object} config Configuration options
43719 Roo.form.ComboBoxArray = function(config)
43723 * @event beforeremove
43724 * Fires before remove the value from the list
43725 * @param {Roo.form.ComboBoxArray} _self This combo box array
43726 * @param {Roo.form.ComboBoxArray.Item} item removed item
43728 'beforeremove' : true,
43731 * Fires when remove the value from the list
43732 * @param {Roo.form.ComboBoxArray} _self This combo box array
43733 * @param {Roo.form.ComboBoxArray.Item} item removed item
43740 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43742 this.items = new Roo.util.MixedCollection(false);
43744 // construct the child combo...
43754 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43757 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43762 // behavies liek a hiddne field
43763 inputType: 'hidden',
43765 * @cfg {Number} width The width of the box that displays the selected element
43772 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
43776 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
43778 hiddenName : false,
43780 * @cfg {String} seperator The value seperator normally ','
43784 // private the array of items that are displayed..
43786 // private - the hidden field el.
43788 // private - the filed el..
43791 //validateValue : function() { return true; }, // all values are ok!
43792 //onAddClick: function() { },
43794 onRender : function(ct, position)
43797 // create the standard hidden element
43798 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43801 // give fake names to child combo;
43802 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43803 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43805 this.combo = Roo.factory(this.combo, Roo.form);
43806 this.combo.onRender(ct, position);
43807 if (typeof(this.combo.width) != 'undefined') {
43808 this.combo.onResize(this.combo.width,0);
43811 this.combo.initEvents();
43813 // assigned so form know we need to do this..
43814 this.store = this.combo.store;
43815 this.valueField = this.combo.valueField;
43816 this.displayField = this.combo.displayField ;
43819 this.combo.wrap.addClass('x-cbarray-grp');
43821 var cbwrap = this.combo.wrap.createChild(
43822 {tag: 'div', cls: 'x-cbarray-cb'},
43827 this.hiddenEl = this.combo.wrap.createChild({
43828 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
43830 this.el = this.combo.wrap.createChild({
43831 tag: 'input', type:'hidden' , name: this.name, value : ''
43833 // this.el.dom.removeAttribute("name");
43836 this.outerWrap = this.combo.wrap;
43837 this.wrap = cbwrap;
43839 this.outerWrap.setWidth(this.width);
43840 this.outerWrap.dom.removeChild(this.el.dom);
43842 this.wrap.dom.appendChild(this.el.dom);
43843 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43844 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43846 this.combo.trigger.setStyle('position','relative');
43847 this.combo.trigger.setStyle('left', '0px');
43848 this.combo.trigger.setStyle('top', '2px');
43850 this.combo.el.setStyle('vertical-align', 'text-bottom');
43852 //this.trigger.setStyle('vertical-align', 'top');
43854 // this should use the code from combo really... on('add' ....)
43858 this.adder = this.outerWrap.createChild(
43859 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
43861 this.adder.on('click', function(e) {
43862 _t.fireEvent('adderclick', this, e);
43866 //this.adder.on('click', this.onAddClick, _t);
43869 this.combo.on('select', function(cb, rec, ix) {
43870 this.addItem(rec.data);
43873 cb.el.dom.value = '';
43874 //cb.lastData = rec.data;
43883 getName: function()
43885 // returns hidden if it's set..
43886 if (!this.rendered) {return ''};
43887 return this.hiddenName ? this.hiddenName : this.name;
43892 onResize: function(w, h){
43895 // not sure if this is needed..
43896 //this.combo.onResize(w,h);
43898 if(typeof w != 'number'){
43899 // we do not handle it!?!?
43902 var tw = this.combo.trigger.getWidth();
43903 tw += this.addicon ? this.addicon.getWidth() : 0;
43904 tw += this.editicon ? this.editicon.getWidth() : 0;
43906 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43908 this.combo.trigger.setStyle('left', '0px');
43910 if(this.list && this.listWidth === undefined){
43911 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43912 this.list.setWidth(lw);
43913 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43920 addItem: function(rec)
43922 var valueField = this.combo.valueField;
43923 var displayField = this.combo.displayField;
43925 if (this.items.indexOfKey(rec[valueField]) > -1) {
43926 //console.log("GOT " + rec.data.id);
43930 var x = new Roo.form.ComboBoxArray.Item({
43931 //id : rec[this.idField],
43933 displayField : displayField ,
43934 tipField : displayField ,
43938 this.items.add(rec[valueField],x);
43939 // add it before the element..
43940 this.updateHiddenEl();
43941 x.render(this.outerWrap, this.wrap.dom);
43942 // add the image handler..
43945 updateHiddenEl : function()
43948 if (!this.hiddenEl) {
43952 var idField = this.combo.valueField;
43954 this.items.each(function(f) {
43955 ar.push(f.data[idField]);
43957 this.hiddenEl.dom.value = ar.join(this.seperator);
43963 this.items.clear();
43965 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43969 this.el.dom.value = '';
43970 if (this.hiddenEl) {
43971 this.hiddenEl.dom.value = '';
43975 getValue: function()
43977 return this.hiddenEl ? this.hiddenEl.dom.value : '';
43979 setValue: function(v) // not a valid action - must use addItems..
43984 if (this.store.isLocal && (typeof(v) == 'string')) {
43985 // then we can use the store to find the values..
43986 // comma seperated at present.. this needs to allow JSON based encoding..
43987 this.hiddenEl.value = v;
43989 Roo.each(v.split(this.seperator), function(k) {
43990 Roo.log("CHECK " + this.valueField + ',' + k);
43991 var li = this.store.query(this.valueField, k);
43996 add[this.valueField] = k;
43997 add[this.displayField] = li.item(0).data[this.displayField];
44003 if (typeof(v) == 'object' ) {
44004 // then let's assume it's an array of objects..
44005 Roo.each(v, function(l) {
44007 if (typeof(l) == 'string') {
44009 add[this.valueField] = l;
44010 add[this.displayField] = l
44019 setFromData: function(v)
44021 // this recieves an object, if setValues is called.
44023 this.el.dom.value = v[this.displayField];
44024 this.hiddenEl.dom.value = v[this.valueField];
44025 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44028 var kv = v[this.valueField];
44029 var dv = v[this.displayField];
44030 kv = typeof(kv) != 'string' ? '' : kv;
44031 dv = typeof(dv) != 'string' ? '' : dv;
44034 var keys = kv.split(this.seperator);
44035 var display = dv.split(this.seperator);
44036 for (var i = 0 ; i < keys.length; i++) {
44038 add[this.valueField] = keys[i];
44039 add[this.displayField] = display[i];
44047 * Validates the combox array value
44048 * @return {Boolean} True if the value is valid, else false
44050 validate : function(){
44051 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44052 this.clearInvalid();
44058 validateValue : function(value){
44059 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44067 isDirty : function() {
44068 if(this.disabled) {
44073 var d = Roo.decode(String(this.originalValue));
44075 return String(this.getValue()) !== String(this.originalValue);
44078 var originalValue = [];
44080 for (var i = 0; i < d.length; i++){
44081 originalValue.push(d[i][this.valueField]);
44084 return String(this.getValue()) !== String(originalValue.join(this.seperator));
44093 * @class Roo.form.ComboBoxArray.Item
44094 * @extends Roo.BoxComponent
44095 * A selected item in the list
44096 * Fred [x] Brian [x] [Pick another |v]
44099 * Create a new item.
44100 * @param {Object} config Configuration options
44103 Roo.form.ComboBoxArray.Item = function(config) {
44104 config.id = Roo.id();
44105 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44108 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44111 displayField : false,
44115 defaultAutoCreate : {
44117 cls: 'x-cbarray-item',
44124 src : Roo.BLANK_IMAGE_URL ,
44132 onRender : function(ct, position)
44134 Roo.form.Field.superclass.onRender.call(this, ct, position);
44137 var cfg = this.getAutoCreate();
44138 this.el = ct.createChild(cfg, position);
44141 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44143 this.el.child('div').dom.innerHTML = this.cb.renderer ?
44144 this.cb.renderer(this.data) :
44145 String.format('{0}',this.data[this.displayField]);
44148 this.el.child('div').dom.setAttribute('qtip',
44149 String.format('{0}',this.data[this.tipField])
44152 this.el.child('img').on('click', this.remove, this);
44156 remove : function()
44158 if(this.cb.disabled){
44162 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44163 this.cb.items.remove(this);
44164 this.el.child('img').un('click', this.remove, this);
44166 this.cb.updateHiddenEl();
44168 this.cb.fireEvent('remove', this.cb, this);
44173 * RooJS Library 1.1.1
44174 * Copyright(c) 2008-2011 Alan Knowles
44181 * @class Roo.form.ComboNested
44182 * @extends Roo.form.ComboBox
44183 * A combobox for that allows selection of nested items in a list,
44198 * Create a new ComboNested
44199 * @param {Object} config Configuration options
44201 Roo.form.ComboNested = function(config){
44202 Roo.form.ComboCheck.superclass.constructor.call(this, config);
44203 // should verify some data...
44205 // hiddenName = required..
44206 // displayField = required
44207 // valudField == required
44208 var req= [ 'hiddenName', 'displayField', 'valueField' ];
44210 Roo.each(req, function(e) {
44211 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44212 throw "Roo.form.ComboNested : missing value for: " + e;
44219 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44222 * @config {Number} max Number of columns to show
44227 list : null, // the outermost div..
44228 innerLists : null, // the
44232 loadingChildren : false,
44234 onRender : function(ct, position)
44236 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44238 if(this.hiddenName){
44239 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
44241 this.hiddenField.value =
44242 this.hiddenValue !== undefined ? this.hiddenValue :
44243 this.value !== undefined ? this.value : '';
44245 // prevent input submission
44246 this.el.dom.removeAttribute('name');
44252 this.el.dom.setAttribute('autocomplete', 'off');
44255 var cls = 'x-combo-list';
44257 this.list = new Roo.Layer({
44258 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44261 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44262 this.list.setWidth(lw);
44263 this.list.swallowEvent('mousewheel');
44264 this.assetHeight = 0;
44267 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44268 this.assetHeight += this.header.getHeight();
44270 this.innerLists = [];
44273 for (var i =0 ; i < this.maxColumns; i++) {
44274 this.onRenderList( cls, i);
44277 // always needs footer, as we are going to have an 'OK' button.
44278 this.footer = this.list.createChild({cls:cls+'-ft'});
44279 this.pageTb = new Roo.Toolbar(this.footer);
44284 handler: function()
44290 if ( this.allowBlank && !this.disableClear) {
44292 this.pageTb.add(new Roo.Toolbar.Fill(), {
44293 cls: 'x-btn-icon x-btn-clear',
44295 handler: function()
44298 _this.clearValue();
44299 _this.onSelect(false, -1);
44304 this.assetHeight += this.footer.getHeight();
44308 onRenderList : function ( cls, i)
44311 var lw = Math.floor(
44312 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44315 this.list.setWidth(lw); // default to '1'
44317 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44318 //il.on('mouseover', this.onViewOver, this, { list: i });
44319 //il.on('mousemove', this.onViewMove, this, { list: i });
44321 il.setStyle({ 'overflow-x' : 'hidden'});
44324 this.tpl = new Roo.Template({
44325 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44326 isEmpty: function (value, allValues) {
44328 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44329 return dl ? 'has-children' : 'no-children'
44334 var store = this.store;
44336 store = new Roo.data.SimpleStore({
44337 //fields : this.store.reader.meta.fields,
44338 reader : this.store.reader,
44342 this.stores[i] = store;
44344 var view = this.views[i] = new Roo.View(
44350 selectedClass: this.selectedClass
44353 view.getEl().setWidth(lw);
44354 view.getEl().setStyle({
44355 position: i < 1 ? 'relative' : 'absolute',
44357 left: (i * lw ) + 'px',
44358 display : i > 0 ? 'none' : 'block'
44360 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44361 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44362 //view.on('click', this.onViewClick, this, { list : i });
44364 store.on('beforeload', this.onBeforeLoad, this);
44365 store.on('load', this.onLoad, this, { list : i});
44366 store.on('loadexception', this.onLoadException, this);
44368 // hide the other vies..
44374 restrictHeight : function()
44377 Roo.each(this.innerLists, function(il,i) {
44378 var el = this.views[i].getEl();
44379 el.dom.style.height = '';
44380 var inner = el.dom;
44381 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44382 // only adjust heights on other ones..
44383 mh = Math.max(h, mh);
44386 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44387 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44394 this.list.beginUpdate();
44395 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44396 this.list.alignTo(this.el, this.listAlign);
44397 this.list.endUpdate();
44402 // -- store handlers..
44404 onBeforeLoad : function()
44406 if(!this.hasFocus){
44409 this.innerLists[0].update(this.loadingText ?
44410 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44411 this.restrictHeight();
44412 this.selectedIndex = -1;
44415 onLoad : function(a,b,c,d)
44417 if (!this.loadingChildren) {
44418 // then we are loading the top level. - hide the children
44419 for (var i = 1;i < this.views.length; i++) {
44420 this.views[i].getEl().setStyle({ display : 'none' });
44422 var lw = Math.floor(
44423 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44426 this.list.setWidth(lw); // default to '1'
44430 if(!this.hasFocus){
44434 if(this.store.getCount() > 0) {
44436 this.restrictHeight();
44438 this.onEmptyResults();
44441 if (!this.loadingChildren) {
44442 this.selectActive();
44445 this.stores[1].loadData([]);
44446 this.stores[2].loadData([]);
44455 onLoadException : function()
44458 Roo.log(this.store.reader.jsonData);
44459 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44460 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44465 // no cleaning of leading spaces on blur here.
44466 cleanLeadingSpace : function(e) { },
44469 onSelectChange : function (view, sels, opts )
44471 var ix = view.getSelectedIndexes();
44473 if (opts.list > this.maxColumns - 2) {
44474 if (view.store.getCount()< 1) {
44475 this.views[opts.list ].getEl().setStyle({ display : 'none' });
44479 // used to clear ?? but if we are loading unselected
44480 this.setFromData(view.store.getAt(ix[0]).data);
44489 // this get's fired when trigger opens..
44490 // this.setFromData({});
44491 var str = this.stores[opts.list+1];
44492 str.data.clear(); // removeall wihtout the fire events..
44496 var rec = view.store.getAt(ix[0]);
44498 this.setFromData(rec.data);
44499 this.fireEvent('select', this, rec, ix[0]);
44501 var lw = Math.floor(
44503 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44504 ) / this.maxColumns
44506 this.loadingChildren = true;
44507 this.stores[opts.list+1].loadDataFromChildren( rec );
44508 this.loadingChildren = false;
44509 var dl = this.stores[opts.list+1]. getTotalCount();
44511 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44513 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44514 for (var i = opts.list+2; i < this.views.length;i++) {
44515 this.views[i].getEl().setStyle({ display : 'none' });
44518 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44519 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44521 if (this.isLoading) {
44522 // this.selectActive(opts.list);
44530 onDoubleClick : function()
44532 this.collapse(); //??
44540 recordToStack : function(store, prop, value, stack)
44542 var cstore = new Roo.data.SimpleStore({
44543 //fields : this.store.reader.meta.fields, // we need array reader.. for
44544 reader : this.store.reader,
44548 var record = false;
44550 if(store.getCount() < 1){
44553 store.each(function(r){
44554 if(r.data[prop] == value){
44559 if (r.data.cn && r.data.cn.length) {
44560 cstore.loadDataFromChildren( r);
44561 var cret = _this.recordToStack(cstore, prop, value, stack);
44562 if (cret !== false) {
44571 if (record == false) {
44574 stack.unshift(srec);
44579 * find the stack of stores that match our value.
44584 selectActive : function ()
44586 // if store is not loaded, then we will need to wait for that to happen first.
44588 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44589 for (var i = 0; i < stack.length; i++ ) {
44590 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44602 * Ext JS Library 1.1.1
44603 * Copyright(c) 2006-2007, Ext JS, LLC.
44605 * Originally Released Under LGPL - original licence link has changed is not relivant.
44608 * <script type="text/javascript">
44611 * @class Roo.form.Checkbox
44612 * @extends Roo.form.Field
44613 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
44615 * Creates a new Checkbox
44616 * @param {Object} config Configuration options
44618 Roo.form.Checkbox = function(config){
44619 Roo.form.Checkbox.superclass.constructor.call(this, config);
44623 * Fires when the checkbox is checked or unchecked.
44624 * @param {Roo.form.Checkbox} this This checkbox
44625 * @param {Boolean} checked The new checked value
44631 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
44633 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44635 focusClass : undefined,
44637 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44639 fieldClass: "x-form-field",
44641 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44645 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44646 * {tag: "input", type: "checkbox", autocomplete: "off"})
44648 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44650 * @cfg {String} boxLabel The text that appears beside the checkbox
44654 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44658 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44660 valueOff: '0', // value when not checked..
44662 actionMode : 'viewEl',
44665 itemCls : 'x-menu-check-item x-form-item',
44666 groupClass : 'x-menu-group-item',
44667 inputType : 'hidden',
44670 inSetChecked: false, // check that we are not calling self...
44672 inputElement: false, // real input element?
44673 basedOn: false, // ????
44675 isFormField: true, // not sure where this is needed!!!!
44677 onResize : function(){
44678 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44679 if(!this.boxLabel){
44680 this.el.alignTo(this.wrap, 'c-c');
44684 initEvents : function(){
44685 Roo.form.Checkbox.superclass.initEvents.call(this);
44686 this.el.on("click", this.onClick, this);
44687 this.el.on("change", this.onClick, this);
44691 getResizeEl : function(){
44695 getPositionEl : function(){
44700 onRender : function(ct, position){
44701 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44703 if(this.inputValue !== undefined){
44704 this.el.dom.value = this.inputValue;
44707 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44708 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44709 var viewEl = this.wrap.createChild({
44710 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44711 this.viewEl = viewEl;
44712 this.wrap.on('click', this.onClick, this);
44714 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
44715 this.el.on('propertychange', this.setFromHidden, this); //ie
44720 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44721 // viewEl.on('click', this.onClick, this);
44723 //if(this.checked){
44724 this.setChecked(this.checked);
44726 //this.checked = this.el.dom;
44732 initValue : Roo.emptyFn,
44735 * Returns the checked state of the checkbox.
44736 * @return {Boolean} True if checked, else false
44738 getValue : function(){
44740 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44742 return this.valueOff;
44747 onClick : function(){
44748 if (this.disabled) {
44751 this.setChecked(!this.checked);
44753 //if(this.el.dom.checked != this.checked){
44754 // this.setValue(this.el.dom.checked);
44759 * Sets the checked state of the checkbox.
44760 * On is always based on a string comparison between inputValue and the param.
44761 * @param {Boolean/String} value - the value to set
44762 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44764 setValue : function(v,suppressEvent){
44767 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44768 //if(this.el && this.el.dom){
44769 // this.el.dom.checked = this.checked;
44770 // this.el.dom.defaultChecked = this.checked;
44772 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44773 //this.fireEvent("check", this, this.checked);
44776 setChecked : function(state,suppressEvent)
44778 if (this.inSetChecked) {
44779 this.checked = state;
44785 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44787 this.checked = state;
44788 if(suppressEvent !== true){
44789 this.fireEvent('check', this, state);
44791 this.inSetChecked = true;
44792 this.el.dom.value = state ? this.inputValue : this.valueOff;
44793 this.inSetChecked = false;
44796 // handle setting of hidden value by some other method!!?!?
44797 setFromHidden: function()
44802 //console.log("SET FROM HIDDEN");
44803 //alert('setFrom hidden');
44804 this.setValue(this.el.dom.value);
44807 onDestroy : function()
44810 Roo.get(this.viewEl).remove();
44813 Roo.form.Checkbox.superclass.onDestroy.call(this);
44816 setBoxLabel : function(str)
44818 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44823 * Ext JS Library 1.1.1
44824 * Copyright(c) 2006-2007, Ext JS, LLC.
44826 * Originally Released Under LGPL - original licence link has changed is not relivant.
44829 * <script type="text/javascript">
44833 * @class Roo.form.Radio
44834 * @extends Roo.form.Checkbox
44835 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
44836 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44838 * Creates a new Radio
44839 * @param {Object} config Configuration options
44841 Roo.form.Radio = function(){
44842 Roo.form.Radio.superclass.constructor.apply(this, arguments);
44844 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44845 inputType: 'radio',
44848 * If this radio is part of a group, it will return the selected value
44851 getGroupValue : function(){
44852 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44856 onRender : function(ct, position){
44857 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44859 if(this.inputValue !== undefined){
44860 this.el.dom.value = this.inputValue;
44863 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44864 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44865 //var viewEl = this.wrap.createChild({
44866 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44867 //this.viewEl = viewEl;
44868 //this.wrap.on('click', this.onClick, this);
44870 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
44871 //this.el.on('propertychange', this.setFromHidden, this); //ie
44876 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44877 // viewEl.on('click', this.onClick, this);
44880 this.el.dom.checked = 'checked' ;
44886 });Roo.rtf = {}; // namespace
44887 Roo.rtf.Hex = function(hex)
44891 Roo.rtf.Paragraph = function(opts)
44893 this.content = []; ///??? is that used?
44894 };Roo.rtf.Span = function(opts)
44896 this.value = opts.value;
44899 Roo.rtf.Group = function(parent)
44901 // we dont want to acutally store parent - it will make debug a nightmare..
44909 Roo.rtf.Group.prototype = {
44913 addContent : function(node) {
44914 // could set styles...
44915 this.content.push(node);
44917 addChild : function(cn)
44921 // only for images really...
44922 toDataURL : function()
44924 var mimetype = false;
44926 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
44927 mimetype = "image/png";
44929 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44930 mimetype = "image/jpeg";
44933 return 'about:blank'; // ?? error?
44937 var hexstring = this.content[this.content.length-1].value;
44939 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44940 return String.fromCharCode(parseInt(a, 16));
44945 // this looks like it's normally the {rtf{ .... }}
44946 Roo.rtf.Document = function()
44948 // we dont want to acutally store parent - it will make debug a nightmare..
44954 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
44955 addChild : function(cn)
44959 case 'rtlch': // most content seems to be inside this??
44962 this.rtlch.push(cn);
44965 this[cn.type] = cn;
44970 getElementsByType : function(type)
44973 this._getElementsByType(type, ret, this.cn, 'rtf');
44976 _getElementsByType : function (type, ret, search_array, path)
44978 search_array.forEach(function(n,i) {
44979 if (n.type == type) {
44980 n.path = path + '/' + n.type + ':' + i;
44983 if (n.cn.length > 0) {
44984 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44991 Roo.rtf.Ctrl = function(opts)
44993 this.value = opts.value;
44994 this.param = opts.param;
44999 * based on this https://github.com/iarna/rtf-parser
45000 * it's really only designed to extract pict from pasted RTF
45004 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45013 Roo.rtf.Parser = function(text) {
45014 //super({objectMode: true})
45016 this.parserState = this.parseText;
45018 // these are for interpeter...
45020 ///this.parserState = this.parseTop
45021 this.groupStack = [];
45022 this.hexStore = [];
45025 this.groups = []; // where we put the return.
45027 for (var ii = 0; ii < text.length; ++ii) {
45030 if (text[ii] === '\n') {
45036 this.parserState(text[ii]);
45042 Roo.rtf.Parser.prototype = {
45043 text : '', // string being parsed..
45045 controlWordParam : '',
45049 groupStack : false,
45054 row : 1, // reportin?
45058 push : function (el)
45060 var m = 'cmd'+ el.type;
45061 if (typeof(this[m]) == 'undefined') {
45062 Roo.log('invalid cmd:' + el.type);
45068 flushHexStore : function()
45070 if (this.hexStore.length < 1) {
45073 var hexstr = this.hexStore.map(
45078 this.group.addContent( new Roo.rtf.Hex( hexstr ));
45081 this.hexStore.splice(0)
45085 cmdgroupstart : function()
45087 this.flushHexStore();
45089 this.groupStack.push(this.group);
45092 if (this.doc === false) {
45093 this.group = this.doc = new Roo.rtf.Document();
45097 this.group = new Roo.rtf.Group(this.group);
45099 cmdignorable : function()
45101 this.flushHexStore();
45102 this.group.ignorable = true;
45104 cmdendparagraph : function()
45106 this.flushHexStore();
45107 this.group.addContent(new Roo.rtf.Paragraph());
45109 cmdgroupend : function ()
45111 this.flushHexStore();
45112 var endingGroup = this.group;
45115 this.group = this.groupStack.pop();
45117 this.group.addChild(endingGroup);
45122 var doc = this.group || this.doc;
45123 //if (endingGroup instanceof FontTable) {
45124 // doc.fonts = endingGroup.table
45125 //} else if (endingGroup instanceof ColorTable) {
45126 // doc.colors = endingGroup.table
45127 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45128 if (endingGroup.ignorable === false) {
45130 this.groups.push(endingGroup);
45131 // Roo.log( endingGroup );
45133 //Roo.each(endingGroup.content, function(item)) {
45134 // doc.addContent(item);
45136 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45139 cmdtext : function (cmd)
45141 this.flushHexStore();
45142 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45143 //this.group = this.doc
45145 this.group.addContent(new Roo.rtf.Span(cmd));
45147 cmdcontrolword : function (cmd)
45149 this.flushHexStore();
45150 if (!this.group.type) {
45151 this.group.type = cmd.value;
45154 this.group.addContent(new Roo.rtf.Ctrl(cmd));
45155 // we actually don't care about ctrl words...
45158 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45159 if (this[method]) {
45160 this[method](cmd.param)
45162 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45166 cmdhexchar : function(cmd) {
45167 this.hexStore.push(cmd);
45169 cmderror : function(cmd) {
45170 throw new Exception (cmd.value);
45175 if (this.text !== '\u0000') this.emitText()
45181 parseText : function(c)
45184 this.parserState = this.parseEscapes;
45185 } else if (c === '{') {
45186 this.emitStartGroup();
45187 } else if (c === '}') {
45188 this.emitEndGroup();
45189 } else if (c === '\x0A' || c === '\x0D') {
45190 // cr/lf are noise chars
45196 parseEscapes: function (c)
45198 if (c === '\\' || c === '{' || c === '}') {
45200 this.parserState = this.parseText;
45202 this.parserState = this.parseControlSymbol;
45203 this.parseControlSymbol(c);
45206 parseControlSymbol: function(c)
45209 this.text += '\u00a0'; // nbsp
45210 this.parserState = this.parseText
45211 } else if (c === '-') {
45212 this.text += '\u00ad'; // soft hyphen
45213 } else if (c === '_') {
45214 this.text += '\u2011'; // non-breaking hyphen
45215 } else if (c === '*') {
45216 this.emitIgnorable();
45217 this.parserState = this.parseText;
45218 } else if (c === "'") {
45219 this.parserState = this.parseHexChar;
45220 } else if (c === '|') { // formula cacter
45221 this.emitFormula();
45222 this.parserState = this.parseText;
45223 } else if (c === ':') { // subentry in an index entry
45224 this.emitIndexSubEntry();
45225 this.parserState = this.parseText;
45226 } else if (c === '\x0a') {
45227 this.emitEndParagraph();
45228 this.parserState = this.parseText;
45229 } else if (c === '\x0d') {
45230 this.emitEndParagraph();
45231 this.parserState = this.parseText;
45233 this.parserState = this.parseControlWord;
45234 this.parseControlWord(c);
45237 parseHexChar: function (c)
45239 if (/^[A-Fa-f0-9]$/.test(c)) {
45241 if (this.hexChar.length >= 2) {
45242 this.emitHexChar();
45243 this.parserState = this.parseText;
45247 this.emitError("Invalid character \"" + c + "\" in hex literal.");
45248 this.parserState = this.parseText;
45251 parseControlWord : function(c)
45254 this.emitControlWord();
45255 this.parserState = this.parseText;
45256 } else if (/^[-\d]$/.test(c)) {
45257 this.parserState = this.parseControlWordParam;
45258 this.controlWordParam += c;
45259 } else if (/^[A-Za-z]$/.test(c)) {
45260 this.controlWord += c;
45262 this.emitControlWord();
45263 this.parserState = this.parseText;
45267 parseControlWordParam : function (c) {
45268 if (/^\d$/.test(c)) {
45269 this.controlWordParam += c;
45270 } else if (c === ' ') {
45271 this.emitControlWord();
45272 this.parserState = this.parseText;
45274 this.emitControlWord();
45275 this.parserState = this.parseText;
45283 emitText : function () {
45284 if (this.text === '') {
45296 emitControlWord : function ()
45299 if (this.controlWord === '') {
45300 this.emitError('empty control word');
45303 type: 'controlword',
45304 value: this.controlWord,
45305 param: this.controlWordParam !== '' && Number(this.controlWordParam),
45311 this.controlWord = '';
45312 this.controlWordParam = '';
45314 emitStartGroup : function ()
45318 type: 'groupstart',
45324 emitEndGroup : function ()
45334 emitIgnorable : function ()
45344 emitHexChar : function ()
45349 value: this.hexChar,
45356 emitError : function (message)
45364 char: this.cpos //,
45365 //stack: new Error().stack
45368 emitEndParagraph : function () {
45371 type: 'endparagraph',
45379 Roo.htmleditor = {};
45382 * @class Roo.htmleditor.Filter
45383 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45384 * @cfg {DomElement} node The node to iterate and filter
45385 * @cfg {boolean|String|Array} tag Tags to replace
45387 * Create a new Filter.
45388 * @param {Object} config Configuration options
45393 Roo.htmleditor.Filter = function(cfg) {
45394 Roo.apply(this.cfg);
45395 // this does not actually call walk as it's really just a abstract class
45399 Roo.htmleditor.Filter.prototype = {
45405 // overrride to do replace comments.
45406 replaceComment : false,
45408 // overrride to do replace or do stuff with tags..
45409 replaceTag : false,
45411 walk : function(dom)
45413 Roo.each( Array.from(dom.childNodes), function( e ) {
45416 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45417 this.replaceComment(e);
45420 case e.nodeType != 1: //not a node.
45423 case this.tag === true: // everything
45424 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45425 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45426 if (this.replaceTag && false === this.replaceTag(e)) {
45429 if (e.hasChildNodes()) {
45434 default: // tags .. that do not match.
45435 if (e.hasChildNodes()) {
45446 * @class Roo.htmleditor.FilterAttributes
45447 * clean attributes and styles including http:// etc.. in attribute
45449 * Run a new Attribute Filter
45450 * @param {Object} config Configuration options
45452 Roo.htmleditor.FilterAttributes = function(cfg)
45454 Roo.apply(this, cfg);
45455 this.attrib_black = this.attrib_black || [];
45456 this.attrib_white = this.attrib_white || [];
45458 this.attrib_clean = this.attrib_clean || [];
45459 this.style_white = this.style_white || [];
45460 this.style_black = this.style_black || [];
45461 this.walk(cfg.node);
45464 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45466 tag: true, // all tags
45468 attrib_black : false, // array
45469 attrib_clean : false,
45470 attrib_white : false,
45472 style_white : false,
45473 style_black : false,
45476 replaceTag : function(node)
45478 if (!node.attributes || !node.attributes.length) {
45482 for (var i = node.attributes.length-1; i > -1 ; i--) {
45483 var a = node.attributes[i];
45485 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45486 node.removeAttribute(a.name);
45492 if (a.name.toLowerCase().substr(0,2)=='on') {
45493 node.removeAttribute(a.name);
45498 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45499 node.removeAttribute(a.name);
45502 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45503 this.cleanAttr(node,a.name,a.value); // fixme..
45506 if (a.name == 'style') {
45507 this.cleanStyle(node,a.name,a.value);
45510 /// clean up MS crap..
45511 // tecnically this should be a list of valid class'es..
45514 if (a.name == 'class') {
45515 if (a.value.match(/^Mso/)) {
45516 node.removeAttribute('class');
45519 if (a.value.match(/^body$/)) {
45520 node.removeAttribute('class');
45530 return true; // clean children
45533 cleanAttr: function(node, n,v)
45536 if (v.match(/^\./) || v.match(/^\//)) {
45539 if (v.match(/^(http|https):\/\//)
45540 || v.match(/^mailto:/)
45541 || v.match(/^ftp:/)
45542 || v.match(/^data:/)
45546 if (v.match(/^#/)) {
45549 if (v.match(/^\{/)) { // allow template editing.
45552 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45553 node.removeAttribute(n);
45556 cleanStyle : function(node, n,v)
45558 if (v.match(/expression/)) { //XSS?? should we even bother..
45559 node.removeAttribute(n);
45563 var parts = v.split(/;/);
45566 Roo.each(parts, function(p) {
45567 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45571 var l = p.split(':').shift().replace(/\s+/g,'');
45572 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45574 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45578 // only allow 'c whitelisted system attributes'
45579 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45587 if (clean.length) {
45588 node.setAttribute(n, clean.join(';'));
45590 node.removeAttribute(n);
45599 * @class Roo.htmleditor.FilterBlack
45600 * remove blacklisted elements.
45602 * Run a new Blacklisted Filter
45603 * @param {Object} config Configuration options
45606 Roo.htmleditor.FilterBlack = function(cfg)
45608 Roo.apply(this, cfg);
45609 this.walk(cfg.node);
45612 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45614 tag : true, // all elements.
45616 replace : function(n)
45618 n.parentNode.removeChild(n);
45622 * @class Roo.htmleditor.FilterComment
45625 * Run a new Comments Filter
45626 * @param {Object} config Configuration options
45628 Roo.htmleditor.FilterComment = function(cfg)
45630 this.walk(cfg.node);
45633 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45636 replaceComment : function(n)
45638 n.parentNode.removeChild(n);
45641 * @class Roo.htmleditor.FilterKeepChildren
45642 * remove tags but keep children
45644 * Run a new Keep Children Filter
45645 * @param {Object} config Configuration options
45648 Roo.htmleditor.FilterKeepChildren = function(cfg)
45650 Roo.apply(this, cfg);
45651 if (this.tag === false) {
45652 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45654 this.walk(cfg.node);
45657 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45661 replaceTag : function(node)
45663 // walk children...
45665 var ar = Array.from(node.childNodes);
45667 for (var i = 0; i < ar.length; i++) {
45668 if (ar[i].nodeType == 1) {
45670 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45671 || // array and it matches
45672 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45674 this.replaceTag(ar[i]); // child is blacklisted as well...
45679 ar = Array.from(node.childNodes);
45680 for (var i = 0; i < ar.length; i++) {
45682 node.removeChild(ar[i]);
45683 // what if we need to walk these???
45684 node.parentNode.insertBefore(ar[i], node);
45685 if (this.tag !== false) {
45690 node.parentNode.removeChild(node);
45691 return false; // don't walk children
45696 * @class Roo.htmleditor.FilterParagraph
45697 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45698 * like on 'push' to remove the <p> tags and replace them with line breaks.
45700 * Run a new Paragraph Filter
45701 * @param {Object} config Configuration options
45704 Roo.htmleditor.FilterParagraph = function(cfg)
45706 // no need to apply config.
45707 this.walk(cfg.node);
45710 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45717 replaceTag : function(node)
45720 if (node.childNodes.length == 1 &&
45721 node.childNodes[0].nodeType == 3 &&
45722 node.childNodes[0].textContent.trim().length < 1
45724 // remove and replace with '<BR>';
45725 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45726 return false; // no need to walk..
45728 var ar = Array.from(node.childNodes);
45729 for (var i = 0; i < ar.length; i++) {
45730 node.removeChild(ar[i]);
45731 // what if we need to walk these???
45732 node.parentNode.insertBefore(ar[i], node);
45734 // now what about this?
45738 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45739 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45740 node.parentNode.removeChild(node);
45747 * @class Roo.htmleditor.FilterSpan
45748 * filter span's with no attributes out..
45750 * Run a new Span Filter
45751 * @param {Object} config Configuration options
45754 Roo.htmleditor.FilterSpan = function(cfg)
45756 // no need to apply config.
45757 this.walk(cfg.node);
45760 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45766 replaceTag : function(node)
45768 if (node.attributes && node.attributes.length > 0) {
45769 return true; // walk if there are any.
45771 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45777 * @class Roo.htmleditor.FilterTableWidth
45778 try and remove table width data - as that frequently messes up other stuff.
45780 * was cleanTableWidths.
45782 * Quite often pasting from word etc.. results in tables with column and widths.
45783 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45786 * Run a new Table Filter
45787 * @param {Object} config Configuration options
45790 Roo.htmleditor.FilterTableWidth = function(cfg)
45792 // no need to apply config.
45793 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45794 this.walk(cfg.node);
45797 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45802 replaceTag: function(node) {
45806 if (node.hasAttribute('width')) {
45807 node.removeAttribute('width');
45811 if (node.hasAttribute("style")) {
45814 var styles = node.getAttribute("style").split(";");
45816 Roo.each(styles, function(s) {
45817 if (!s.match(/:/)) {
45820 var kv = s.split(":");
45821 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45824 // what ever is left... we allow.
45827 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45828 if (!nstyle.length) {
45829 node.removeAttribute('style');
45833 return true; // continue doing children..
45836 * @class Roo.htmleditor.FilterWord
45837 * try and clean up all the mess that Word generates.
45839 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
45842 * Run a new Span Filter
45843 * @param {Object} config Configuration options
45846 Roo.htmleditor.FilterWord = function(cfg)
45848 // no need to apply config.
45849 this.walk(cfg.node);
45852 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45858 * Clean up MS wordisms...
45860 replaceTag : function(node)
45863 // no idea what this does - span with text, replaceds with just text.
45865 node.nodeName == 'SPAN' &&
45866 !node.hasAttributes() &&
45867 node.childNodes.length == 1 &&
45868 node.firstChild.nodeName == "#text"
45870 var textNode = node.firstChild;
45871 node.removeChild(textNode);
45872 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
45873 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45875 node.parentNode.insertBefore(textNode, node);
45876 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
45877 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45880 node.parentNode.removeChild(node);
45881 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45886 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45887 node.parentNode.removeChild(node);
45888 return false; // dont do chidlren
45890 //Roo.log(node.tagName);
45891 // remove - but keep children..
45892 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45893 //Roo.log('-- removed');
45894 while (node.childNodes.length) {
45895 var cn = node.childNodes[0];
45896 node.removeChild(cn);
45897 node.parentNode.insertBefore(cn, node);
45898 // move node to parent - and clean it..
45899 this.replaceTag(cn);
45901 node.parentNode.removeChild(node);
45902 /// no need to iterate chidlren = it's got none..
45903 //this.iterateChildren(node, this.cleanWord);
45904 return false; // no need to iterate children.
45907 if (node.className.length) {
45909 var cn = node.className.split(/\W+/);
45911 Roo.each(cn, function(cls) {
45912 if (cls.match(/Mso[a-zA-Z]+/)) {
45917 node.className = cna.length ? cna.join(' ') : '';
45919 node.removeAttribute("class");
45923 if (node.hasAttribute("lang")) {
45924 node.removeAttribute("lang");
45927 if (node.hasAttribute("style")) {
45929 var styles = node.getAttribute("style").split(";");
45931 Roo.each(styles, function(s) {
45932 if (!s.match(/:/)) {
45935 var kv = s.split(":");
45936 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45939 // what ever is left... we allow.
45942 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45943 if (!nstyle.length) {
45944 node.removeAttribute('style');
45947 return true; // do children
45954 * @class Roo.htmleditor.FilterStyleToTag
45955 * part of the word stuff... - certain 'styles' should be converted to tags.
45957 * font-weight: bold -> bold
45958 * ?? super / subscrit etc..
45961 * Run a new style to tag filter.
45962 * @param {Object} config Configuration options
45964 Roo.htmleditor.FilterStyleToTag = function(cfg)
45968 B : [ 'fontWeight' , 'bold'],
45969 I : [ 'fontStyle' , 'italic'],
45970 //pre : [ 'font-style' , 'italic'],
45971 // h1.. h6 ?? font-size?
45972 SUP : [ 'verticalAlign' , 'super' ],
45973 SUB : [ 'verticalAlign' , 'sub' ]
45978 Roo.apply(this, cfg);
45981 this.walk(cfg.node);
45988 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45990 tag: true, // all tags
45995 replaceTag : function(node)
45999 if (node.getAttribute("style") === null) {
46003 for (var k in this.tags) {
46004 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46006 node.style.removeProperty(this.tags[k][0]);
46009 if (!inject.length) {
46012 var cn = Array.from(node.childNodes);
46014 Roo.each(inject, function(t) {
46015 var nc = node.ownerDocument.createelement(t);
46016 nn.appendChild(nc);
46019 for(var i = 0;i < cn.length;cn++) {
46020 node.removeChild(cn[i]);
46021 nn.appendChild(cn[i]);
46023 return true /// iterate thru
46027 * @class Roo.htmleditor.FilterLongBr
46028 * BR/BR/BR - keep a maximum of 2...
46030 * Run a new Long BR Filter
46031 * @param {Object} config Configuration options
46034 Roo.htmleditor.FilterLongBr = function(cfg)
46036 // no need to apply config.
46037 this.walk(cfg.node);
46040 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46047 replaceTag : function(node)
46050 var ps = node.nextSibling;
46051 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46052 ps = ps.nextSibling;
46055 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
46056 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46060 if (!ps || ps.nodeType != 1) {
46064 if (!ps || ps.tagName != 'BR') {
46073 if (!node.previousSibling) {
46076 var ps = node.previousSibling;
46078 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46079 ps = ps.previousSibling;
46081 if (!ps || ps.nodeType != 1) {
46084 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46085 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46089 node.parentNode.removeChild(node); // remove me...
46091 return false; // no need to do children
46097 * @class Roo.htmleditor.Tidy
46099 * @cfg {Roo.HtmlEditorCore} core the editor.
46101 * Create a new Filter.
46102 * @param {Object} config Configuration options
46106 Roo.htmleditor.Tidy = function(cfg) {
46107 Roo.apply(this, cfg);
46109 this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46113 Roo.htmleditor.Tidy.toString = function(node)
46115 return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46118 Roo.htmleditor.Tidy.prototype = {
46121 wrap : function(s) {
46122 return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46126 tidy : function(node, indent) {
46128 if (node.nodeType == 3) {
46132 return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46137 if (node.nodeType != 1) {
46143 if (node.tagName == 'BODY') {
46145 return this.cn(node, '');
46148 // Prints the node tagName, such as <A>, <IMG>, etc
46149 var ret = "<" + node.tagName + this.attr(node) ;
46151 // elements with no children..
46152 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46158 var cindent = indent === false ? '' : (indent + ' ');
46159 // tags where we will not pad the children.. (inline text tags etc..)
46160 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46166 var cn = this.cn(node, cindent );
46168 return ret + cn + '</' + node.tagName + '>';
46171 cn: function(node, indent)
46175 var ar = Array.from(node.childNodes);
46176 for (var i = 0 ; i < ar.length ; i++) {
46180 if (indent !== false // indent==false preservies everything
46182 && ar[i].nodeType == 3
46183 && ar[i].nodeValue.length > 0
46184 && ar[i].nodeValue.match(/^\s+/)
46186 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46187 ret.pop(); // remove line break from last?
46190 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46192 if (indent !== false
46193 && ar[i].nodeType == 1 // element - and indent is not set...
46195 ret.push("\n" + indent);
46198 ret.push(this.tidy(ar[i], indent));
46199 // text + trailing indent
46200 if (indent !== false
46201 && ar[i].nodeType == 3
46202 && ar[i].nodeValue.length > 0
46203 && ar[i].nodeValue.match(/\s+$/)
46205 ret.push("\n" + indent);
46212 // what if all text?
46215 return ret.join('');
46220 attr : function(node)
46223 for(i = 0; i < node.attributes.length;i++) {
46225 // skip empty values?
46226 if (!node.attributes.item(i).value.length) {
46229 attr.push( node.attributes.item(i).name + '="' +
46230 Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46233 return attr.length ? (' ' + attr.join(' ') ) : '';
46241 * @class Roo.htmleditor.KeyEnter
46242 * Handle Enter press..
46243 * @cfg {Roo.HtmlEditorCore} core the editor.
46245 * Create a new Filter.
46246 * @param {Object} config Configuration options
46251 Roo.htmleditor.KeyEnter = function(cfg) {
46252 Roo.apply(this, cfg);
46253 // this does not actually call walk as it's really just a abstract class
46255 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46259 Roo.htmleditor.KeyEnter.prototype = {
46263 keypress : function(e)
46265 if (e.charCode != 13) {
46268 e.preventDefault();
46269 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46270 var doc = this.core.doc;
46272 var docFragment = doc.createDocumentFragment();
46275 var newEle = doc.createTextNode('\n');
46276 docFragment.appendChild(newEle);
46279 var range = this.core.win.getSelection().getRangeAt(0);
46280 var n = range.commonAncestorContainer ;
46281 while (n && n.nodeType != 1) {
46285 if (n && n.tagName == 'UL') {
46286 li = doc.createElement('LI');
46290 if (n && n.tagName == 'LI') {
46291 li = doc.createElement('LI');
46292 if (n.nextSibling) {
46293 n.parentNode.insertBefore(li, n.firstSibling);
46296 n.parentNode.appendChild(li);
46300 range = doc.createRange();
46301 range.setStartAfter(li);
46302 range.collapse(true);
46304 //make the cursor there
46305 var sel = this.core.win.getSelection();
46306 sel.removeAllRanges();
46307 sel.addRange(range);
46312 //add the br, or p, or something else
46313 newEle = doc.createElement('br');
46314 docFragment.appendChild(newEle);
46316 //make the br replace selection
46318 range.deleteContents();
46320 range.insertNode(docFragment);
46321 range = range.cloneRange();
46322 range.collapse(true);
46323 var sel = this.core.win.getSelection();
46324 sel.removeAllRanges();
46325 sel.addRange(range);
46326 sel.collapseToEnd();
46334 * @class Roo.htmleditor.Block
46335 * Base class for html editor blocks - do not use it directly .. extend it..
46336 * @cfg {DomElement} node The node to apply stuff to.
46337 * @cfg {String} friendly_name the name that appears in the context bar about this block
46338 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46341 * Create a new Filter.
46342 * @param {Object} config Configuration options
46345 Roo.htmleditor.Block = function(cfg)
46347 // do nothing .. should not be called really.
46350 Roo.htmleditor.Block.factory = function(node)
46353 var id = Roo.get(node).id;
46354 if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46355 Roo.htmleditor.Block.cache[id].readElement();
46356 return Roo.htmleditor.Block.cache[id];
46359 var cls = Roo.htmleditor['Block' + node.getAttribute('data-block')];
46360 if (typeof(cls) == 'undefined') {
46361 Roo.log("OOps missing block : " + 'Block' + node.getAttribute('data-block'));
46364 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46365 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
46367 // question goes here... do we need to clear out this cache sometimes?
46368 // or show we make it relivant to the htmleditor.
46369 Roo.htmleditor.Block.cache = {};
46371 Roo.htmleditor.Block.prototype = {
46375 // used by context menu
46376 friendly_name : 'Image with caption',
46380 * Update a node with values from this object
46381 * @param {DomElement} node
46383 updateElement : function(node)
46385 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46388 * convert to plain HTML for calling insertAtCursor..
46390 toHTML : function()
46392 return Roo.DomHelper.markup(this.toObject());
46395 * used by readEleemnt to extract data from a node
46396 * may need improving as it's pretty basic
46398 * @param {DomElement} node
46399 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46400 * @param {String} attribute (use html - for contents, or style for using next param as style)
46401 * @param {String} style the style property - eg. text-align
46403 getVal : function(node, tag, attr, style)
46406 if (tag !== true && n.tagName != tag.toUpperCase()) {
46407 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46408 // but kiss for now.
46409 n = node.getElementsByTagName(tag).item(0);
46411 if (attr == 'html') {
46412 return n.innerHTML;
46414 if (attr == 'style') {
46415 return n.style[style]
46418 return Roo.get(n).attr(attr);
46422 * create a DomHelper friendly object - for use with
46423 * Roo.DomHelper.markup / overwrite / etc..
46426 toObject : function()
46431 * Read a node that has a 'data-block' property - and extract the values from it.
46432 * @param {DomElement} node - the node
46434 readElement : function(node)
46445 * @class Roo.htmleditor.BlockFigure
46446 * Block that has an image and a figcaption
46447 * @cfg {String} image_src the url for the image
46448 * @cfg {String} align (left|right) alignment for the block default left
46449 * @cfg {String} text_align (left|right) alignment for the text caption default left.
46450 * @cfg {String} caption the text to appear below (and in the alt tag)
46451 * @cfg {String|number} image_width the width of the image number or %?
46452 * @cfg {String|number} image_height the height of the image number or %?
46455 * Create a new Filter.
46456 * @param {Object} config Configuration options
46459 Roo.htmleditor.BlockFigure = function(cfg)
46462 this.readElement(cfg.node);
46463 this.updateElement(cfg.node);
46465 Roo.apply(this, cfg);
46467 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46475 text_align: 'left',
46480 // used by context menu
46481 friendly_name : 'Image with caption',
46483 context : { // ?? static really
46496 opts : [[ "left"],[ "right"]],
46501 title: "Caption Align",
46502 opts : [ [ "left"],[ "right"],[ "center"]],
46513 * create a DomHelper friendly object - for use with
46514 * Roo.DomHelper.markup / overwrite / etc..
46516 toObject : function()
46518 var d = document.createElement('div');
46519 d.innerHTML = this.caption;
46523 'data-block' : 'Figure',
46524 contenteditable : 'false',
46527 float : this.align ,
46528 width : this.width,
46529 margin: this.margin
46534 src : this.image_src,
46535 alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46542 contenteditable : true,
46544 'text-align': this.text_align
46546 html : this.caption
46553 readElement : function(node)
46555 this.image_src = this.getVal(node, 'img', 'src');
46556 this.align = this.getVal(node, 'figure', 'style', 'float');
46557 this.caption = this.getVal(node, 'figcaption', 'html');
46558 this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46559 this.width = this.getVal(node, 'figure', 'style', 'width');
46560 this.margin = this.getVal(node, 'figure', 'style', 'margin');
46573 //<script type="text/javascript">
46576 * Based Ext JS Library 1.1.1
46577 * Copyright(c) 2006-2007, Ext JS, LLC.
46583 * @class Roo.HtmlEditorCore
46584 * @extends Roo.Component
46585 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46587 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46590 Roo.HtmlEditorCore = function(config){
46593 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46598 * @event initialize
46599 * Fires when the editor is fully initialized (including the iframe)
46600 * @param {Roo.HtmlEditorCore} this
46605 * Fires when the editor is first receives the focus. Any insertion must wait
46606 * until after this event.
46607 * @param {Roo.HtmlEditorCore} this
46611 * @event beforesync
46612 * Fires before the textarea is updated with content from the editor iframe. Return false
46613 * to cancel the sync.
46614 * @param {Roo.HtmlEditorCore} this
46615 * @param {String} html
46619 * @event beforepush
46620 * Fires before the iframe editor is updated with content from the textarea. Return false
46621 * to cancel the push.
46622 * @param {Roo.HtmlEditorCore} this
46623 * @param {String} html
46628 * Fires when the textarea is updated with content from the editor iframe.
46629 * @param {Roo.HtmlEditorCore} this
46630 * @param {String} html
46635 * Fires when the iframe editor is updated with content from the textarea.
46636 * @param {Roo.HtmlEditorCore} this
46637 * @param {String} html
46642 * @event editorevent
46643 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46644 * @param {Roo.HtmlEditorCore} this
46650 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46652 // defaults : white / black...
46653 this.applyBlacklists();
46660 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
46664 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
46670 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
46675 * @cfg {Number} height (in pixels)
46679 * @cfg {Number} width (in pixels)
46684 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46687 stylesheets: false,
46690 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46692 allowComments: false,
46696 // private properties
46697 validationEvent : false,
46699 initialized : false,
46701 sourceEditMode : false,
46702 onFocus : Roo.emptyFn,
46704 hideMode:'offsets',
46708 // blacklist + whitelisted elements..
46715 undoManager : false,
46717 * Protected method that will not generally be called directly. It
46718 * is called when the editor initializes the iframe with HTML contents. Override this method if you
46719 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46721 getDocMarkup : function(){
46725 // inherit styels from page...??
46726 if (this.stylesheets === false) {
46728 Roo.get(document.head).select('style').each(function(node) {
46729 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46732 Roo.get(document.head).select('link').each(function(node) {
46733 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46736 } else if (!this.stylesheets.length) {
46738 st = '<style type="text/css">' +
46739 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46742 for (var i in this.stylesheets) {
46743 if (typeof(this.stylesheets[i]) != 'string') {
46746 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46751 st += '<style type="text/css">' +
46752 'IMG { cursor: pointer } ' +
46755 var cls = 'roo-htmleditor-body';
46757 if(this.bodyCls.length){
46758 cls += ' ' + this.bodyCls;
46761 return '<html><head>' + st +
46762 //<style type="text/css">' +
46763 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46765 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
46769 onRender : function(ct, position)
46772 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46773 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46776 this.el.dom.style.border = '0 none';
46777 this.el.dom.setAttribute('tabIndex', -1);
46778 this.el.addClass('x-hidden hide');
46782 if(Roo.isIE){ // fix IE 1px bogus margin
46783 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46787 this.frameId = Roo.id();
46791 var iframe = this.owner.wrap.createChild({
46793 cls: 'form-control', // bootstrap..
46795 name: this.frameId,
46796 frameBorder : 'no',
46797 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
46802 this.iframe = iframe.dom;
46804 this.assignDocWin();
46806 this.doc.designMode = 'on';
46809 this.doc.write(this.getDocMarkup());
46813 var task = { // must defer to wait for browser to be ready
46815 //console.log("run task?" + this.doc.readyState);
46816 this.assignDocWin();
46817 if(this.doc.body || this.doc.readyState == 'complete'){
46819 this.doc.designMode="on";
46824 Roo.TaskMgr.stop(task);
46825 this.initEditor.defer(10, this);
46832 Roo.TaskMgr.start(task);
46837 onResize : function(w, h)
46839 Roo.log('resize: ' +w + ',' + h );
46840 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46844 if(typeof w == 'number'){
46846 this.iframe.style.width = w + 'px';
46848 if(typeof h == 'number'){
46850 this.iframe.style.height = h + 'px';
46852 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46859 * Toggles the editor between standard and source edit mode.
46860 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46862 toggleSourceEdit : function(sourceEditMode){
46864 this.sourceEditMode = sourceEditMode === true;
46866 if(this.sourceEditMode){
46868 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
46871 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46872 //this.iframe.className = '';
46875 //this.setSize(this.owner.wrap.getSize());
46876 //this.fireEvent('editmodechange', this, this.sourceEditMode);
46883 * Protected method that will not generally be called directly. If you need/want
46884 * custom HTML cleanup, this is the method you should override.
46885 * @param {String} html The HTML to be cleaned
46886 * return {String} The cleaned HTML
46888 cleanHtml : function(html){
46889 html = String(html);
46890 if(html.length > 5){
46891 if(Roo.isSafari){ // strip safari nonsense
46892 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46895 if(html == ' '){
46902 * HTML Editor -> Textarea
46903 * Protected method that will not generally be called directly. Syncs the contents
46904 * of the editor iframe with the textarea.
46906 syncValue : function()
46908 Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46909 if(this.initialized){
46911 this.undoManager.addEvent();
46914 var bd = (this.doc.body || this.doc.documentElement);
46915 //this.cleanUpPaste(); -- this is done else where and causes havoc..
46917 // not sure if this is really the place for this
46918 // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46919 // this has to update attributes that get duped.. like alt and caption..
46922 //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46923 // Roo.htmleditor.Block.factory(e);
46927 var div = document.createElement('div');
46928 div.innerHTML = bd.innerHTML;
46929 // remove content editable. (blocks)
46932 new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46934 var html = div.innerHTML;
46936 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46937 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46939 html = '<div style="'+m[0]+'">' + html + '</div>';
46942 html = this.cleanHtml(html);
46943 // fix up the special chars.. normaly like back quotes in word...
46944 // however we do not want to do this with chinese..
46945 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46947 var cc = match.charCodeAt();
46949 // Get the character value, handling surrogate pairs
46950 if (match.length == 2) {
46951 // It's a surrogate pair, calculate the Unicode code point
46952 var high = match.charCodeAt(0) - 0xD800;
46953 var low = match.charCodeAt(1) - 0xDC00;
46954 cc = (high * 0x400) + low + 0x10000;
46956 (cc >= 0x4E00 && cc < 0xA000 ) ||
46957 (cc >= 0x3400 && cc < 0x4E00 ) ||
46958 (cc >= 0xf900 && cc < 0xfb00 )
46963 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46964 return "&#" + cc + ";";
46971 if(this.owner.fireEvent('beforesync', this, html) !== false){
46972 this.el.dom.value = html;
46973 this.owner.fireEvent('sync', this, html);
46979 * TEXTAREA -> EDITABLE
46980 * Protected method that will not generally be called directly. Pushes the value of the textarea
46981 * into the iframe editor.
46983 pushValue : function()
46985 Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46986 if(this.initialized){
46987 var v = this.el.dom.value.trim();
46990 if(this.owner.fireEvent('beforepush', this, v) !== false){
46991 var d = (this.doc.body || this.doc.documentElement);
46994 this.el.dom.value = d.innerHTML;
46995 this.owner.fireEvent('push', this, v);
46998 Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
47000 Roo.htmleditor.Block.factory(e);
47003 var lc = this.doc.body.lastChild;
47004 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
47005 // add an extra line at the end.
47006 this.doc.body.appendChild(this.doc.createElement('br'));
47014 deferFocus : function(){
47015 this.focus.defer(10, this);
47019 focus : function(){
47020 if(this.win && !this.sourceEditMode){
47027 assignDocWin: function()
47029 var iframe = this.iframe;
47032 this.doc = iframe.contentWindow.document;
47033 this.win = iframe.contentWindow;
47035 // if (!Roo.get(this.frameId)) {
47038 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47039 // this.win = Roo.get(this.frameId).dom.contentWindow;
47041 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47045 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47046 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47051 initEditor : function(){
47052 //console.log("INIT EDITOR");
47053 this.assignDocWin();
47057 this.doc.designMode="on";
47059 this.doc.write(this.getDocMarkup());
47062 var dbody = (this.doc.body || this.doc.documentElement);
47063 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47064 // this copies styles from the containing element into thsi one..
47065 // not sure why we need all of this..
47066 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47068 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47069 //ss['background-attachment'] = 'fixed'; // w3c
47070 dbody.bgProperties = 'fixed'; // ie
47071 //Roo.DomHelper.applyStyles(dbody, ss);
47072 Roo.EventManager.on(this.doc, {
47073 //'mousedown': this.onEditorEvent,
47074 'mouseup': this.onEditorEvent,
47075 'dblclick': this.onEditorEvent,
47076 'click': this.onEditorEvent,
47077 'keyup': this.onEditorEvent,
47082 Roo.EventManager.on(this.doc, {
47083 'paste': this.onPasteEvent,
47087 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47089 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47090 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47092 this.initialized = true;
47095 // initialize special key events - enter
47096 new Roo.htmleditor.KeyEnter({core : this});
47100 this.owner.fireEvent('initialize', this);
47104 onPasteEvent : function(e,v)
47106 // I think we better assume paste is going to be a dirty load of rubish from word..
47108 // even pasting into a 'email version' of this widget will have to clean up that mess.
47109 var cd = (e.browserEvent.clipboardData || window.clipboardData);
47111 // check what type of paste - if it's an image, then handle it differently.
47112 if (cd.files.length > 0) {
47114 var urlAPI = (window.createObjectURL && window) ||
47115 (window.URL && URL.revokeObjectURL && URL) ||
47116 (window.webkitURL && webkitURL);
47118 var url = urlAPI.createObjectURL( cd.files[0]);
47119 this.insertAtCursor('<img src=" + url + ">');
47123 var html = cd.getData('text/html'); // clipboard event
47124 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47125 var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47129 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47130 .map(function(g) { return g.toDataURL(); });
47133 html = this.cleanWordChars(html);
47135 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47137 if (images.length > 0) {
47138 Roo.each(d.getElementsByTagName('img'), function(img, i) {
47139 img.setAttribute('src', images[i]);
47144 new Roo.htmleditor.FilterStyleToTag({ node : d });
47145 new Roo.htmleditor.FilterAttributes({
47147 attrib_white : ['href', 'src', 'name', 'align'],
47148 attrib_clean : ['href', 'src' ]
47150 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47151 // should be fonts..
47152 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47153 new Roo.htmleditor.FilterParagraph({ node : d });
47154 new Roo.htmleditor.FilterSpan({ node : d });
47155 new Roo.htmleditor.FilterLongBr({ node : d });
47159 this.insertAtCursor(d.innerHTML);
47161 e.preventDefault();
47163 // default behaveiour should be our local cleanup paste? (optional?)
47164 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47165 //this.owner.fireEvent('paste', e, v);
47168 onDestroy : function(){
47174 //for (var i =0; i < this.toolbars.length;i++) {
47175 // // fixme - ask toolbars for heights?
47176 // this.toolbars[i].onDestroy();
47179 //this.wrap.dom.innerHTML = '';
47180 //this.wrap.remove();
47185 onFirstFocus : function(){
47187 this.assignDocWin();
47188 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47190 this.activated = true;
47193 if(Roo.isGecko){ // prevent silly gecko errors
47195 var s = this.win.getSelection();
47196 if(!s.focusNode || s.focusNode.nodeType != 3){
47197 var r = s.getRangeAt(0);
47198 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47203 this.execCmd('useCSS', true);
47204 this.execCmd('styleWithCSS', false);
47207 this.owner.fireEvent('activate', this);
47211 adjustFont: function(btn){
47212 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47213 //if(Roo.isSafari){ // safari
47216 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47217 if(Roo.isSafari){ // safari
47218 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47219 v = (v < 10) ? 10 : v;
47220 v = (v > 48) ? 48 : v;
47221 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47226 v = Math.max(1, v+adjust);
47228 this.execCmd('FontSize', v );
47231 onEditorEvent : function(e)
47233 this.owner.fireEvent('editorevent', this, e);
47234 // this.updateToolbar();
47235 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47238 insertTag : function(tg)
47240 // could be a bit smarter... -> wrap the current selected tRoo..
47241 if (tg.toLowerCase() == 'span' ||
47242 tg.toLowerCase() == 'code' ||
47243 tg.toLowerCase() == 'sup' ||
47244 tg.toLowerCase() == 'sub'
47247 range = this.createRange(this.getSelection());
47248 var wrappingNode = this.doc.createElement(tg.toLowerCase());
47249 wrappingNode.appendChild(range.extractContents());
47250 range.insertNode(wrappingNode);
47257 this.execCmd("formatblock", tg);
47258 this.undoManager.addEvent();
47261 insertText : function(txt)
47265 var range = this.createRange();
47266 range.deleteContents();
47267 //alert(Sender.getAttribute('label'));
47269 range.insertNode(this.doc.createTextNode(txt));
47270 this.undoManager.addEvent();
47276 * Executes a Midas editor command on the editor document and performs necessary focus and
47277 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47278 * @param {String} cmd The Midas command
47279 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47281 relayCmd : function(cmd, value){
47283 this.execCmd(cmd, value);
47284 this.owner.fireEvent('editorevent', this);
47285 //this.updateToolbar();
47286 this.owner.deferFocus();
47290 * Executes a Midas editor command directly on the editor document.
47291 * For visual commands, you should use {@link #relayCmd} instead.
47292 * <b>This should only be called after the editor is initialized.</b>
47293 * @param {String} cmd The Midas command
47294 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47296 execCmd : function(cmd, value){
47297 this.doc.execCommand(cmd, false, value === undefined ? null : value);
47304 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47306 * @param {String} text | dom node..
47308 insertAtCursor : function(text)
47311 if(!this.activated){
47315 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47319 // from jquery ui (MIT licenced)
47321 var win = this.win;
47323 if (win.getSelection && win.getSelection().getRangeAt) {
47325 // delete the existing?
47327 this.createRange(this.getSelection()).deleteContents();
47328 range = win.getSelection().getRangeAt(0);
47329 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47330 range.insertNode(node);
47331 range = range.cloneRange();
47332 range.collapse(false);
47334 win.getSelection().removeAllRanges();
47335 win.getSelection().addRange(range);
47339 } else if (win.document.selection && win.document.selection.createRange) {
47340 // no firefox support
47341 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47342 win.document.selection.createRange().pasteHTML(txt);
47345 // no firefox support
47346 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47347 this.execCmd('InsertHTML', txt);
47355 mozKeyPress : function(e){
47357 var c = e.getCharCode(), cmd;
47360 c = String.fromCharCode(c).toLowerCase();
47374 // this.cleanUpPaste.defer(100, this);
47382 e.preventDefault();
47390 fixKeys : function(){ // load time branching for fastest keydown performance
47392 return function(e){
47393 var k = e.getKey(), r;
47396 r = this.doc.selection.createRange();
47399 r.pasteHTML('    ');
47406 r = this.doc.selection.createRange();
47408 var target = r.parentElement();
47409 if(!target || target.tagName.toLowerCase() != 'li'){
47411 r.pasteHTML('<br/>');
47417 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47418 // this.cleanUpPaste.defer(100, this);
47424 }else if(Roo.isOpera){
47425 return function(e){
47426 var k = e.getKey();
47430 this.execCmd('InsertHTML','    ');
47433 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47434 // this.cleanUpPaste.defer(100, this);
47439 }else if(Roo.isSafari){
47440 return function(e){
47441 var k = e.getKey();
47445 this.execCmd('InsertText','\t');
47449 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47450 // this.cleanUpPaste.defer(100, this);
47458 getAllAncestors: function()
47460 var p = this.getSelectedNode();
47463 a.push(p); // push blank onto stack..
47464 p = this.getParentElement();
47468 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47472 a.push(this.doc.body);
47476 lastSelNode : false,
47479 getSelection : function()
47481 this.assignDocWin();
47482 return Roo.isIE ? this.doc.selection : this.win.getSelection();
47485 * Select a dom node
47486 * @param {DomElement} node the node to select
47488 selectNode : function(node)
47490 var nodeRange = node.ownerDocument.createRange();
47492 nodeRange.selectNode(node);
47494 nodeRange.selectNodeContents(node);
47496 //nodeRange.collapse(true);
47497 var s = this.win.getSelection();
47498 s.removeAllRanges();
47499 s.addRange(nodeRange);
47502 getSelectedNode: function()
47504 // this may only work on Gecko!!!
47506 // should we cache this!!!!
47511 var range = this.createRange(this.getSelection()).cloneRange();
47514 var parent = range.parentElement();
47516 var testRange = range.duplicate();
47517 testRange.moveToElementText(parent);
47518 if (testRange.inRange(range)) {
47521 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47524 parent = parent.parentElement;
47529 // is ancestor a text element.
47530 var ac = range.commonAncestorContainer;
47531 if (ac.nodeType == 3) {
47532 ac = ac.parentNode;
47535 var ar = ac.childNodes;
47538 var other_nodes = [];
47539 var has_other_nodes = false;
47540 for (var i=0;i<ar.length;i++) {
47541 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
47544 // fullly contained node.
47546 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47551 // probably selected..
47552 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47553 other_nodes.push(ar[i]);
47557 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
47562 has_other_nodes = true;
47564 if (!nodes.length && other_nodes.length) {
47565 nodes= other_nodes;
47567 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47573 createRange: function(sel)
47575 // this has strange effects when using with
47576 // top toolbar - not sure if it's a great idea.
47577 //this.editor.contentWindow.focus();
47578 if (typeof sel != "undefined") {
47580 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47582 return this.doc.createRange();
47585 return this.doc.createRange();
47588 getParentElement: function()
47591 this.assignDocWin();
47592 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47594 var range = this.createRange(sel);
47597 var p = range.commonAncestorContainer;
47598 while (p.nodeType == 3) { // text node
47609 * Range intersection.. the hard stuff...
47613 * [ -- selected range --- ]
47617 * if end is before start or hits it. fail.
47618 * if start is after end or hits it fail.
47620 * if either hits (but other is outside. - then it's not
47626 // @see http://www.thismuchiknow.co.uk/?p=64.
47627 rangeIntersectsNode : function(range, node)
47629 var nodeRange = node.ownerDocument.createRange();
47631 nodeRange.selectNode(node);
47633 nodeRange.selectNodeContents(node);
47636 var rangeStartRange = range.cloneRange();
47637 rangeStartRange.collapse(true);
47639 var rangeEndRange = range.cloneRange();
47640 rangeEndRange.collapse(false);
47642 var nodeStartRange = nodeRange.cloneRange();
47643 nodeStartRange.collapse(true);
47645 var nodeEndRange = nodeRange.cloneRange();
47646 nodeEndRange.collapse(false);
47648 return rangeStartRange.compareBoundaryPoints(
47649 Range.START_TO_START, nodeEndRange) == -1 &&
47650 rangeEndRange.compareBoundaryPoints(
47651 Range.START_TO_START, nodeStartRange) == 1;
47655 rangeCompareNode : function(range, node)
47657 var nodeRange = node.ownerDocument.createRange();
47659 nodeRange.selectNode(node);
47661 nodeRange.selectNodeContents(node);
47665 range.collapse(true);
47667 nodeRange.collapse(true);
47669 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47670 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
47672 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47674 var nodeIsBefore = ss == 1;
47675 var nodeIsAfter = ee == -1;
47677 if (nodeIsBefore && nodeIsAfter) {
47680 if (!nodeIsBefore && nodeIsAfter) {
47681 return 1; //right trailed.
47684 if (nodeIsBefore && !nodeIsAfter) {
47685 return 2; // left trailed.
47691 cleanWordChars : function(input) {// change the chars to hex code
47694 [ 8211, "–" ],
47695 [ 8212, "—" ],
47703 var output = input;
47704 Roo.each(swapCodes, function(sw) {
47705 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47707 output = output.replace(swapper, sw[1]);
47717 cleanUpChild : function (node)
47720 new Roo.htmleditor.FilterComment({node : node});
47721 new Roo.htmleditor.FilterAttributes({
47723 attrib_black : this.ablack,
47724 attrib_clean : this.aclean,
47725 style_white : this.cwhite,
47726 style_black : this.cblack
47728 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47729 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47735 * Clean up MS wordisms...
47736 * @deprecated - use filter directly
47738 cleanWord : function(node)
47740 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47747 * @deprecated - use filters
47749 cleanTableWidths : function(node)
47751 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47758 applyBlacklists : function()
47760 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
47761 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
47763 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
47764 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
47765 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
47769 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47770 if (b.indexOf(tag) > -1) {
47773 this.white.push(tag);
47777 Roo.each(w, function(tag) {
47778 if (b.indexOf(tag) > -1) {
47781 if (this.white.indexOf(tag) > -1) {
47784 this.white.push(tag);
47789 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47790 if (w.indexOf(tag) > -1) {
47793 this.black.push(tag);
47797 Roo.each(b, function(tag) {
47798 if (w.indexOf(tag) > -1) {
47801 if (this.black.indexOf(tag) > -1) {
47804 this.black.push(tag);
47809 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
47810 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
47814 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47815 if (b.indexOf(tag) > -1) {
47818 this.cwhite.push(tag);
47822 Roo.each(w, function(tag) {
47823 if (b.indexOf(tag) > -1) {
47826 if (this.cwhite.indexOf(tag) > -1) {
47829 this.cwhite.push(tag);
47834 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47835 if (w.indexOf(tag) > -1) {
47838 this.cblack.push(tag);
47842 Roo.each(b, function(tag) {
47843 if (w.indexOf(tag) > -1) {
47846 if (this.cblack.indexOf(tag) > -1) {
47849 this.cblack.push(tag);
47854 setStylesheets : function(stylesheets)
47856 if(typeof(stylesheets) == 'string'){
47857 Roo.get(this.iframe.contentDocument.head).createChild({
47859 rel : 'stylesheet',
47868 Roo.each(stylesheets, function(s) {
47873 Roo.get(_this.iframe.contentDocument.head).createChild({
47875 rel : 'stylesheet',
47884 removeStylesheets : function()
47888 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47893 setStyle : function(style)
47895 Roo.get(this.iframe.contentDocument.head).createChild({
47904 // hide stuff that is not compatible
47918 * @event specialkey
47922 * @cfg {String} fieldClass @hide
47925 * @cfg {String} focusClass @hide
47928 * @cfg {String} autoCreate @hide
47931 * @cfg {String} inputType @hide
47934 * @cfg {String} invalidClass @hide
47937 * @cfg {String} invalidText @hide
47940 * @cfg {String} msgFx @hide
47943 * @cfg {String} validateOnBlur @hide
47947 Roo.HtmlEditorCore.white = [
47948 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47950 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
47951 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
47952 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
47953 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
47954 'TABLE', 'UL', 'XMP',
47956 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
47959 'DIR', 'MENU', 'OL', 'UL', 'DL',
47965 Roo.HtmlEditorCore.black = [
47966 // 'embed', 'object', // enable - backend responsiblity to clean thiese
47968 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
47969 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
47970 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
47971 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
47972 //'FONT' // CLEAN LATER..
47973 'COLGROUP', 'COL' // messy tables.
47976 Roo.HtmlEditorCore.clean = [ // ?? needed???
47977 'SCRIPT', 'STYLE', 'TITLE', 'XML'
47979 Roo.HtmlEditorCore.tag_remove = [
47984 Roo.HtmlEditorCore.ablack = [
47988 Roo.HtmlEditorCore.aclean = [
47989 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
47993 Roo.HtmlEditorCore.pwhite= [
47994 'http', 'https', 'mailto'
47997 // white listed style attributes.
47998 Roo.HtmlEditorCore.cwhite= [
47999 // 'text-align', /// default is to allow most things..
48005 // black listed style attributes.
48006 Roo.HtmlEditorCore.cblack= [
48007 // 'font-size' -- this can be set by the project
48013 //<script type="text/javascript">
48016 * Ext JS Library 1.1.1
48017 * Copyright(c) 2006-2007, Ext JS, LLC.
48023 Roo.form.HtmlEditor = function(config){
48027 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48029 if (!this.toolbars) {
48030 this.toolbars = [];
48032 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48038 * @class Roo.form.HtmlEditor
48039 * @extends Roo.form.Field
48040 * Provides a lightweight HTML Editor component.
48042 * This has been tested on Fireforx / Chrome.. IE may not be so great..
48044 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48045 * supported by this editor.</b><br/><br/>
48046 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48047 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48049 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48051 * @cfg {Boolean} clearUp
48055 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48060 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
48065 * @cfg {Number} height (in pixels)
48069 * @cfg {Number} width (in pixels)
48074 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
48077 stylesheets: false,
48081 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48086 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48092 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48097 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48102 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48104 allowComments: false,
48106 * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48115 // private properties
48116 validationEvent : false,
48118 initialized : false,
48121 onFocus : Roo.emptyFn,
48123 hideMode:'offsets',
48125 actionMode : 'container', // defaults to hiding it...
48127 defaultAutoCreate : { // modified by initCompnoent..
48129 style:"width:500px;height:300px;",
48130 autocomplete: "new-password"
48134 initComponent : function(){
48137 * @event initialize
48138 * Fires when the editor is fully initialized (including the iframe)
48139 * @param {HtmlEditor} this
48144 * Fires when the editor is first receives the focus. Any insertion must wait
48145 * until after this event.
48146 * @param {HtmlEditor} this
48150 * @event beforesync
48151 * Fires before the textarea is updated with content from the editor iframe. Return false
48152 * to cancel the sync.
48153 * @param {HtmlEditor} this
48154 * @param {String} html
48158 * @event beforepush
48159 * Fires before the iframe editor is updated with content from the textarea. Return false
48160 * to cancel the push.
48161 * @param {HtmlEditor} this
48162 * @param {String} html
48167 * Fires when the textarea is updated with content from the editor iframe.
48168 * @param {HtmlEditor} this
48169 * @param {String} html
48174 * Fires when the iframe editor is updated with content from the textarea.
48175 * @param {HtmlEditor} this
48176 * @param {String} html
48180 * @event editmodechange
48181 * Fires when the editor switches edit modes
48182 * @param {HtmlEditor} this
48183 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48185 editmodechange: true,
48187 * @event editorevent
48188 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48189 * @param {HtmlEditor} this
48193 * @event firstfocus
48194 * Fires when on first focus - needed by toolbars..
48195 * @param {HtmlEditor} this
48200 * Auto save the htmlEditor value as a file into Events
48201 * @param {HtmlEditor} this
48205 * @event savedpreview
48206 * preview the saved version of htmlEditor
48207 * @param {HtmlEditor} this
48209 savedpreview: true,
48212 * @event stylesheetsclick
48213 * Fires when press the Sytlesheets button
48214 * @param {Roo.HtmlEditorCore} this
48216 stylesheetsclick: true,
48219 * Fires when press user pastes into the editor
48220 * @param {Roo.HtmlEditorCore} this
48224 this.defaultAutoCreate = {
48226 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48227 autocomplete: "new-password"
48232 * Protected method that will not generally be called directly. It
48233 * is called when the editor creates its toolbar. Override this method if you need to
48234 * add custom toolbar buttons.
48235 * @param {HtmlEditor} editor
48237 createToolbar : function(editor){
48238 Roo.log("create toolbars");
48239 if (!editor.toolbars || !editor.toolbars.length) {
48240 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48243 for (var i =0 ; i < editor.toolbars.length;i++) {
48244 editor.toolbars[i] = Roo.factory(
48245 typeof(editor.toolbars[i]) == 'string' ?
48246 { xtype: editor.toolbars[i]} : editor.toolbars[i],
48247 Roo.form.HtmlEditor);
48248 editor.toolbars[i].init(editor);
48256 onRender : function(ct, position)
48259 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48261 this.wrap = this.el.wrap({
48262 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48265 this.editorcore.onRender(ct, position);
48267 if (this.resizable) {
48268 this.resizeEl = new Roo.Resizable(this.wrap, {
48272 minHeight : this.height,
48273 height: this.height,
48274 handles : this.resizable,
48277 resize : function(r, w, h) {
48278 _t.onResize(w,h); // -something
48284 this.createToolbar(this);
48288 this.setSize(this.wrap.getSize());
48290 if (this.resizeEl) {
48291 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48292 // should trigger onReize..
48295 this.keyNav = new Roo.KeyNav(this.el, {
48297 "tab" : function(e){
48298 e.preventDefault();
48300 var value = this.getValue();
48302 var start = this.el.dom.selectionStart;
48303 var end = this.el.dom.selectionEnd;
48307 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48308 this.el.dom.setSelectionRange(end + 1, end + 1);
48312 var f = value.substring(0, start).split("\t");
48314 if(f.pop().length != 0){
48318 this.setValue(f.join("\t") + value.substring(end));
48319 this.el.dom.setSelectionRange(start - 1, start - 1);
48323 "home" : function(e){
48324 e.preventDefault();
48326 var curr = this.el.dom.selectionStart;
48327 var lines = this.getValue().split("\n");
48334 this.el.dom.setSelectionRange(0, 0);
48340 for (var i = 0; i < lines.length;i++) {
48341 pos += lines[i].length;
48351 pos -= lines[i].length;
48357 this.el.dom.setSelectionRange(pos, pos);
48361 this.el.dom.selectionStart = pos;
48362 this.el.dom.selectionEnd = curr;
48365 "end" : function(e){
48366 e.preventDefault();
48368 var curr = this.el.dom.selectionStart;
48369 var lines = this.getValue().split("\n");
48376 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48382 for (var i = 0; i < lines.length;i++) {
48384 pos += lines[i].length;
48398 this.el.dom.setSelectionRange(pos, pos);
48402 this.el.dom.selectionStart = curr;
48403 this.el.dom.selectionEnd = pos;
48408 doRelay : function(foo, bar, hname){
48409 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48415 // if(this.autosave && this.w){
48416 // this.autoSaveFn = setInterval(this.autosave, 1000);
48421 onResize : function(w, h)
48423 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48428 if(typeof w == 'number'){
48429 var aw = w - this.wrap.getFrameWidth('lr');
48430 this.el.setWidth(this.adjustWidth('textarea', aw));
48433 if(typeof h == 'number'){
48435 for (var i =0; i < this.toolbars.length;i++) {
48436 // fixme - ask toolbars for heights?
48437 tbh += this.toolbars[i].tb.el.getHeight();
48438 if (this.toolbars[i].footer) {
48439 tbh += this.toolbars[i].footer.el.getHeight();
48446 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48447 ah -= 5; // knock a few pixes off for look..
48449 this.el.setHeight(this.adjustWidth('textarea', ah));
48453 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48454 this.editorcore.onResize(ew,eh);
48459 * Toggles the editor between standard and source edit mode.
48460 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48462 toggleSourceEdit : function(sourceEditMode)
48464 this.editorcore.toggleSourceEdit(sourceEditMode);
48466 if(this.editorcore.sourceEditMode){
48467 Roo.log('editor - showing textarea');
48470 // Roo.log(this.syncValue());
48471 this.editorcore.syncValue();
48472 this.el.removeClass('x-hidden');
48473 this.el.dom.removeAttribute('tabIndex');
48475 this.el.dom.scrollTop = 0;
48478 for (var i = 0; i < this.toolbars.length; i++) {
48479 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48480 this.toolbars[i].tb.hide();
48481 this.toolbars[i].footer.hide();
48486 Roo.log('editor - hiding textarea');
48488 // Roo.log(this.pushValue());
48489 this.editorcore.pushValue();
48491 this.el.addClass('x-hidden');
48492 this.el.dom.setAttribute('tabIndex', -1);
48494 for (var i = 0; i < this.toolbars.length; i++) {
48495 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48496 this.toolbars[i].tb.show();
48497 this.toolbars[i].footer.show();
48501 //this.deferFocus();
48504 this.setSize(this.wrap.getSize());
48505 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48507 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48510 // private (for BoxComponent)
48511 adjustSize : Roo.BoxComponent.prototype.adjustSize,
48513 // private (for BoxComponent)
48514 getResizeEl : function(){
48518 // private (for BoxComponent)
48519 getPositionEl : function(){
48524 initEvents : function(){
48525 this.originalValue = this.getValue();
48529 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48532 markInvalid : Roo.emptyFn,
48534 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48537 clearInvalid : Roo.emptyFn,
48539 setValue : function(v){
48540 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48541 this.editorcore.pushValue();
48546 deferFocus : function(){
48547 this.focus.defer(10, this);
48551 focus : function(){
48552 this.editorcore.focus();
48558 onDestroy : function(){
48564 for (var i =0; i < this.toolbars.length;i++) {
48565 // fixme - ask toolbars for heights?
48566 this.toolbars[i].onDestroy();
48569 this.wrap.dom.innerHTML = '';
48570 this.wrap.remove();
48575 onFirstFocus : function(){
48576 //Roo.log("onFirstFocus");
48577 this.editorcore.onFirstFocus();
48578 for (var i =0; i < this.toolbars.length;i++) {
48579 this.toolbars[i].onFirstFocus();
48585 syncValue : function()
48587 this.editorcore.syncValue();
48590 pushValue : function()
48592 this.editorcore.pushValue();
48595 setStylesheets : function(stylesheets)
48597 this.editorcore.setStylesheets(stylesheets);
48600 removeStylesheets : function()
48602 this.editorcore.removeStylesheets();
48606 // hide stuff that is not compatible
48620 * @event specialkey
48624 * @cfg {String} fieldClass @hide
48627 * @cfg {String} focusClass @hide
48630 * @cfg {String} autoCreate @hide
48633 * @cfg {String} inputType @hide
48636 * @cfg {String} invalidClass @hide
48639 * @cfg {String} invalidText @hide
48642 * @cfg {String} msgFx @hide
48645 * @cfg {String} validateOnBlur @hide
48649 // <script type="text/javascript">
48652 * Ext JS Library 1.1.1
48653 * Copyright(c) 2006-2007, Ext JS, LLC.
48659 * @class Roo.form.HtmlEditorToolbar1
48664 new Roo.form.HtmlEditor({
48667 new Roo.form.HtmlEditorToolbar1({
48668 disable : { fonts: 1 , format: 1, ..., ... , ...],
48674 * @cfg {Object} disable List of elements to disable..
48675 * @cfg {Array} btns List of additional buttons.
48679 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48682 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48685 Roo.apply(this, config);
48687 // default disabled, based on 'good practice'..
48688 this.disable = this.disable || {};
48689 Roo.applyIf(this.disable, {
48692 specialElements : true
48696 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48697 // dont call parent... till later.
48700 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
48707 editorcore : false,
48709 * @cfg {Object} disable List of toolbar elements to disable
48716 * @cfg {String} createLinkText The default text for the create link prompt
48718 createLinkText : 'Please enter the URL for the link:',
48720 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48722 defaultLinkValue : 'http:/'+'/',
48726 * @cfg {Array} fontFamilies An array of available font families
48744 // "á" , ?? a acute?
48749 "°" // , // degrees
48751 // "é" , // e ecute
48752 // "ú" , // u ecute?
48755 specialElements : [
48757 text: "Insert Table",
48760 ihtml : '<table><tr><td>Cell</td></tr></table>'
48764 text: "Insert Image",
48767 ihtml : '<img src="about:blank"/>'
48776 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
48777 "input:submit", "input:button", "select", "textarea", "label" ],
48780 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
48782 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48791 * @cfg {String} defaultFont default font to use.
48793 defaultFont: 'tahoma',
48795 fontSelect : false,
48798 formatCombo : false,
48800 init : function(editor)
48802 this.editor = editor;
48803 this.editorcore = editor.editorcore ? editor.editorcore : editor;
48804 var editorcore = this.editorcore;
48808 var fid = editorcore.frameId;
48810 function btn(id, toggle, handler){
48811 var xid = fid + '-'+ id ;
48815 cls : 'x-btn-icon x-edit-'+id,
48816 enableToggle:toggle !== false,
48817 scope: _t, // was editor...
48818 handler:handler||_t.relayBtnCmd,
48819 clickEvent:'mousedown',
48820 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48827 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48829 // stop form submits
48830 tb.el.on('click', function(e){
48831 e.preventDefault(); // what does this do?
48834 if(!this.disable.font) { // && !Roo.isSafari){
48835 /* why no safari for fonts
48836 editor.fontSelect = tb.el.createChild({
48839 cls:'x-font-select',
48840 html: this.createFontOptions()
48843 editor.fontSelect.on('change', function(){
48844 var font = editor.fontSelect.dom.value;
48845 editor.relayCmd('fontname', font);
48846 editor.deferFocus();
48850 editor.fontSelect.dom,
48856 if(!this.disable.formats){
48857 this.formatCombo = new Roo.form.ComboBox({
48858 store: new Roo.data.SimpleStore({
48861 data : this.formats // from states.js
48865 //autoCreate : {tag: "div", size: "20"},
48866 displayField:'tag',
48870 triggerAction: 'all',
48871 emptyText:'Add tag',
48872 selectOnFocus:true,
48875 'select': function(c, r, i) {
48876 editorcore.insertTag(r.get('tag'));
48882 tb.addField(this.formatCombo);
48886 if(!this.disable.format){
48891 btn('strikethrough')
48894 if(!this.disable.fontSize){
48899 btn('increasefontsize', false, editorcore.adjustFont),
48900 btn('decreasefontsize', false, editorcore.adjustFont)
48905 if(!this.disable.colors){
48908 id:editorcore.frameId +'-forecolor',
48909 cls:'x-btn-icon x-edit-forecolor',
48910 clickEvent:'mousedown',
48911 tooltip: this.buttonTips['forecolor'] || undefined,
48913 menu : new Roo.menu.ColorMenu({
48914 allowReselect: true,
48915 focus: Roo.emptyFn,
48918 selectHandler: function(cp, color){
48919 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48920 editor.deferFocus();
48923 clickEvent:'mousedown'
48926 id:editorcore.frameId +'backcolor',
48927 cls:'x-btn-icon x-edit-backcolor',
48928 clickEvent:'mousedown',
48929 tooltip: this.buttonTips['backcolor'] || undefined,
48931 menu : new Roo.menu.ColorMenu({
48932 focus: Roo.emptyFn,
48935 allowReselect: true,
48936 selectHandler: function(cp, color){
48938 editorcore.execCmd('useCSS', false);
48939 editorcore.execCmd('hilitecolor', color);
48940 editorcore.execCmd('useCSS', true);
48941 editor.deferFocus();
48943 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
48944 Roo.isSafari || Roo.isIE ? '#'+color : color);
48945 editor.deferFocus();
48949 clickEvent:'mousedown'
48954 // now add all the items...
48957 if(!this.disable.alignments){
48960 btn('justifyleft'),
48961 btn('justifycenter'),
48962 btn('justifyright')
48966 //if(!Roo.isSafari){
48967 if(!this.disable.links){
48970 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
48974 if(!this.disable.lists){
48977 btn('insertorderedlist'),
48978 btn('insertunorderedlist')
48981 if(!this.disable.sourceEdit){
48984 btn('sourceedit', true, function(btn){
48985 this.toggleSourceEdit(btn.pressed);
48992 // special menu.. - needs to be tidied up..
48993 if (!this.disable.special) {
48996 cls: 'x-edit-none',
49002 for (var i =0; i < this.specialChars.length; i++) {
49003 smenu.menu.items.push({
49005 html: this.specialChars[i],
49006 handler: function(a,b) {
49007 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
49008 //editor.insertAtCursor(a.html);
49022 if (!this.disable.cleanStyles) {
49024 cls: 'x-btn-icon x-btn-clear',
49030 for (var i =0; i < this.cleanStyles.length; i++) {
49031 cmenu.menu.items.push({
49032 actiontype : this.cleanStyles[i],
49033 html: 'Remove ' + this.cleanStyles[i],
49034 handler: function(a,b) {
49037 var c = Roo.get(editorcore.doc.body);
49038 c.select('[style]').each(function(s) {
49039 s.dom.style.removeProperty(a.actiontype);
49041 editorcore.syncValue();
49046 cmenu.menu.items.push({
49047 actiontype : 'tablewidths',
49048 html: 'Remove Table Widths',
49049 handler: function(a,b) {
49050 editorcore.cleanTableWidths();
49051 editorcore.syncValue();
49055 cmenu.menu.items.push({
49056 actiontype : 'word',
49057 html: 'Remove MS Word Formating',
49058 handler: function(a,b) {
49059 editorcore.cleanWord();
49060 editorcore.syncValue();
49065 cmenu.menu.items.push({
49066 actiontype : 'all',
49067 html: 'Remove All Styles',
49068 handler: function(a,b) {
49070 var c = Roo.get(editorcore.doc.body);
49071 c.select('[style]').each(function(s) {
49072 s.dom.removeAttribute('style');
49074 editorcore.syncValue();
49079 cmenu.menu.items.push({
49080 actiontype : 'all',
49081 html: 'Remove All CSS Classes',
49082 handler: function(a,b) {
49084 var c = Roo.get(editorcore.doc.body);
49085 c.select('[class]').each(function(s) {
49086 s.dom.removeAttribute('class');
49088 editorcore.cleanWord();
49089 editorcore.syncValue();
49094 cmenu.menu.items.push({
49095 actiontype : 'tidy',
49096 html: 'Tidy HTML Source',
49097 handler: function(a,b) {
49098 new Roo.htmleditor.Tidy(editorcore.doc.body);
49099 editorcore.syncValue();
49108 if (!this.disable.specialElements) {
49111 cls: 'x-edit-none',
49116 for (var i =0; i < this.specialElements.length; i++) {
49117 semenu.menu.items.push(
49119 handler: function(a,b) {
49120 editor.insertAtCursor(this.ihtml);
49122 }, this.specialElements[i])
49134 for(var i =0; i< this.btns.length;i++) {
49135 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49136 b.cls = 'x-edit-none';
49138 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49139 b.cls += ' x-init-enable';
49142 b.scope = editorcore;
49150 // disable everything...
49152 this.tb.items.each(function(item){
49155 item.id != editorcore.frameId+ '-sourceedit' &&
49156 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49162 this.rendered = true;
49164 // the all the btns;
49165 editor.on('editorevent', this.updateToolbar, this);
49166 // other toolbars need to implement this..
49167 //editor.on('editmodechange', this.updateToolbar, this);
49171 relayBtnCmd : function(btn) {
49172 this.editorcore.relayCmd(btn.cmd);
49174 // private used internally
49175 createLink : function(){
49176 Roo.log("create link?");
49177 var url = prompt(this.createLinkText, this.defaultLinkValue);
49178 if(url && url != 'http:/'+'/'){
49179 this.editorcore.relayCmd('createlink', url);
49185 * Protected method that will not generally be called directly. It triggers
49186 * a toolbar update by reading the markup state of the current selection in the editor.
49188 updateToolbar: function(){
49190 if(!this.editorcore.activated){
49191 this.editor.onFirstFocus();
49195 var btns = this.tb.items.map,
49196 doc = this.editorcore.doc,
49197 frameId = this.editorcore.frameId;
49199 if(!this.disable.font && !Roo.isSafari){
49201 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49202 if(name != this.fontSelect.dom.value){
49203 this.fontSelect.dom.value = name;
49207 if(!this.disable.format){
49208 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49209 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49210 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49211 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49213 if(!this.disable.alignments){
49214 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49215 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49216 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49218 if(!Roo.isSafari && !this.disable.lists){
49219 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49220 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49223 var ans = this.editorcore.getAllAncestors();
49224 if (this.formatCombo) {
49227 var store = this.formatCombo.store;
49228 this.formatCombo.setValue("");
49229 for (var i =0; i < ans.length;i++) {
49230 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49232 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49240 // hides menus... - so this cant be on a menu...
49241 Roo.menu.MenuMgr.hideAll();
49243 //this.editorsyncValue();
49247 createFontOptions : function(){
49248 var buf = [], fs = this.fontFamilies, ff, lc;
49252 for(var i = 0, len = fs.length; i< len; i++){
49254 lc = ff.toLowerCase();
49256 '<option value="',lc,'" style="font-family:',ff,';"',
49257 (this.defaultFont == lc ? ' selected="true">' : '>'),
49262 return buf.join('');
49265 toggleSourceEdit : function(sourceEditMode){
49267 Roo.log("toolbar toogle");
49268 if(sourceEditMode === undefined){
49269 sourceEditMode = !this.sourceEditMode;
49271 this.sourceEditMode = sourceEditMode === true;
49272 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49273 // just toggle the button?
49274 if(btn.pressed !== this.sourceEditMode){
49275 btn.toggle(this.sourceEditMode);
49279 if(sourceEditMode){
49280 Roo.log("disabling buttons");
49281 this.tb.items.each(function(item){
49282 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49288 Roo.log("enabling buttons");
49289 if(this.editorcore.initialized){
49290 this.tb.items.each(function(item){
49296 Roo.log("calling toggole on editor");
49297 // tell the editor that it's been pressed..
49298 this.editor.toggleSourceEdit(sourceEditMode);
49302 * Object collection of toolbar tooltips for the buttons in the editor. The key
49303 * is the command id associated with that button and the value is a valid QuickTips object.
49308 title: 'Bold (Ctrl+B)',
49309 text: 'Make the selected text bold.',
49310 cls: 'x-html-editor-tip'
49313 title: 'Italic (Ctrl+I)',
49314 text: 'Make the selected text italic.',
49315 cls: 'x-html-editor-tip'
49323 title: 'Bold (Ctrl+B)',
49324 text: 'Make the selected text bold.',
49325 cls: 'x-html-editor-tip'
49328 title: 'Italic (Ctrl+I)',
49329 text: 'Make the selected text italic.',
49330 cls: 'x-html-editor-tip'
49333 title: 'Underline (Ctrl+U)',
49334 text: 'Underline the selected text.',
49335 cls: 'x-html-editor-tip'
49338 title: 'Strikethrough',
49339 text: 'Strikethrough the selected text.',
49340 cls: 'x-html-editor-tip'
49342 increasefontsize : {
49343 title: 'Grow Text',
49344 text: 'Increase the font size.',
49345 cls: 'x-html-editor-tip'
49347 decreasefontsize : {
49348 title: 'Shrink Text',
49349 text: 'Decrease the font size.',
49350 cls: 'x-html-editor-tip'
49353 title: 'Text Highlight Color',
49354 text: 'Change the background color of the selected text.',
49355 cls: 'x-html-editor-tip'
49358 title: 'Font Color',
49359 text: 'Change the color of the selected text.',
49360 cls: 'x-html-editor-tip'
49363 title: 'Align Text Left',
49364 text: 'Align text to the left.',
49365 cls: 'x-html-editor-tip'
49368 title: 'Center Text',
49369 text: 'Center text in the editor.',
49370 cls: 'x-html-editor-tip'
49373 title: 'Align Text Right',
49374 text: 'Align text to the right.',
49375 cls: 'x-html-editor-tip'
49377 insertunorderedlist : {
49378 title: 'Bullet List',
49379 text: 'Start a bulleted list.',
49380 cls: 'x-html-editor-tip'
49382 insertorderedlist : {
49383 title: 'Numbered List',
49384 text: 'Start a numbered list.',
49385 cls: 'x-html-editor-tip'
49388 title: 'Hyperlink',
49389 text: 'Make the selected text a hyperlink.',
49390 cls: 'x-html-editor-tip'
49393 title: 'Source Edit',
49394 text: 'Switch to source editing mode.',
49395 cls: 'x-html-editor-tip'
49399 onDestroy : function(){
49402 this.tb.items.each(function(item){
49404 item.menu.removeAll();
49406 item.menu.el.destroy();
49414 onFirstFocus: function() {
49415 this.tb.items.each(function(item){
49424 // <script type="text/javascript">
49427 * Ext JS Library 1.1.1
49428 * Copyright(c) 2006-2007, Ext JS, LLC.
49435 * @class Roo.form.HtmlEditor.ToolbarContext
49440 new Roo.form.HtmlEditor({
49443 { xtype: 'ToolbarStandard', styles : {} }
49444 { xtype: 'ToolbarContext', disable : {} }
49450 * @config : {Object} disable List of elements to disable.. (not done yet.)
49451 * @config : {Object} styles Map of styles available.
49455 Roo.form.HtmlEditor.ToolbarContext = function(config)
49458 Roo.apply(this, config);
49459 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49460 // dont call parent... till later.
49461 this.styles = this.styles || {};
49466 Roo.form.HtmlEditor.ToolbarContext.types = {
49481 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49507 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49578 name : 'selectoptions',
49584 // should we really allow this??
49585 // should this just be
49602 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49603 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49605 Roo.form.HtmlEditor.ToolbarContext.options = {
49607 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49608 [ 'Courier New', 'Courier New'],
49609 [ 'Tahoma', 'Tahoma'],
49610 [ 'Times New Roman,serif', 'Times'],
49611 [ 'Verdana','Verdana' ]
49615 // fixme - these need to be configurable..
49618 //Roo.form.HtmlEditor.ToolbarContext.types
49621 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
49628 editorcore : false,
49630 * @cfg {Object} disable List of toolbar elements to disable
49635 * @cfg {Object} styles List of styles
49636 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
49638 * These must be defined in the page, so they get rendered correctly..
49649 init : function(editor)
49651 this.editor = editor;
49652 this.editorcore = editor.editorcore ? editor.editorcore : editor;
49653 var editorcore = this.editorcore;
49655 var fid = editorcore.frameId;
49657 function btn(id, toggle, handler){
49658 var xid = fid + '-'+ id ;
49662 cls : 'x-btn-icon x-edit-'+id,
49663 enableToggle:toggle !== false,
49664 scope: editorcore, // was editor...
49665 handler:handler||editorcore.relayBtnCmd,
49666 clickEvent:'mousedown',
49667 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49671 // create a new element.
49672 var wdiv = editor.wrap.createChild({
49674 }, editor.wrap.dom.firstChild.nextSibling, true);
49676 // can we do this more than once??
49678 // stop form submits
49681 // disable everything...
49682 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49683 this.toolbars = {};
49685 for (var i in ty) {
49687 this.toolbars[i] = this.buildToolbar(ty[i],i);
49689 this.tb = this.toolbars.BODY;
49691 this.buildFooter();
49692 this.footer.show();
49693 editor.on('hide', function( ) { this.footer.hide() }, this);
49694 editor.on('show', function( ) { this.footer.show() }, this);
49697 this.rendered = true;
49699 // the all the btns;
49700 editor.on('editorevent', this.updateToolbar, this);
49701 // other toolbars need to implement this..
49702 //editor.on('editmodechange', this.updateToolbar, this);
49708 * Protected method that will not generally be called directly. It triggers
49709 * a toolbar update by reading the markup state of the current selection in the editor.
49711 * Note you can force an update by calling on('editorevent', scope, false)
49713 updateToolbar: function(editor ,ev, sel)
49717 ev.stopEvent(); // se if we can stop this looping with mutiple events.
49721 // capture mouse up - this is handy for selecting images..
49722 // perhaps should go somewhere else...
49723 if(!this.editorcore.activated){
49724 this.editor.onFirstFocus();
49727 //Roo.log(ev ? ev.target : 'NOTARGET');
49730 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49731 // selectNode - might want to handle IE?
49736 (ev.type == 'mouseup' || ev.type == 'click' ) &&
49737 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49738 // they have click on an image...
49739 // let's see if we can change the selection...
49742 // this triggers looping?
49743 //this.editorcore.selectNode(sel);
49746 Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
49747 //Roo.get(node).addClass('roo-ed-selection');
49749 //var updateFooter = sel ? false : true;
49752 var ans = this.editorcore.getAllAncestors();
49755 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49758 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
49759 sel = sel ? sel : this.editorcore.doc.body;
49760 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49764 var tn = sel.tagName.toUpperCase();
49765 var lastSel = this.tb.selectedNode;
49766 this.tb.selectedNode = sel;
49767 var left_label = tn;
49769 // ok see if we are editing a block?
49772 // you are not actually selecting the block.
49773 if (sel && sel.hasAttribute('data-block')) {
49775 } else if (sel && !sel.hasAttribute('contenteditable')) {
49776 var sel_el = Roo.get(sel);
49777 db = sel_el.findParent('[data-block]');
49778 var cepar = sel_el.findParent('[contenteditable=true]');
49779 if (db && cepar && cepar.tagName != 'BODY') {
49780 db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49786 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49788 block = Roo.htmleditor.Block.factory(db);
49792 db.className += ' roo-ed-selection'; // since we removed it earlier... its not there..
49793 tn = 'BLOCK.' + db.getAttribute('data-block');
49795 //this.editorcore.selectNode(db);
49796 if (typeof(this.toolbars[tn]) == 'undefined') {
49797 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
49799 this.toolbars[tn].selectedNode = db;
49800 left_label = block.friendly_name;
49801 ans = this.editorcore.getAllAncestors();
49809 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49810 return; // no change?
49816 ///console.log("show: " + tn);
49817 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49821 this.tb.items.first().el.innerHTML = left_label + ': ';
49824 // update attributes
49825 if (block && this.tb.fields) {
49827 this.tb.fields.each(function(e) {
49828 e.setValue(block[e.name]);
49832 } else if (this.tb.fields && this.tb.selectedNode) {
49833 this.tb.fields.each( function(e) {
49835 e.setValue(this.tb.selectedNode.style[e.stylename]);
49838 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49840 this.updateToolbarStyles(this.tb.selectedNode);
49845 Roo.menu.MenuMgr.hideAll();
49850 // update the footer
49852 this.updateFooter(ans);
49856 updateToolbarStyles : function(sel)
49858 var hasStyles = false;
49859 for(var i in this.styles) {
49865 if (hasStyles && this.tb.hasStyles) {
49866 var st = this.tb.fields.item(0);
49868 st.store.removeAll();
49869 var cn = sel.className.split(/\s+/);
49872 if (this.styles['*']) {
49874 Roo.each(this.styles['*'], function(v) {
49875 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
49878 if (this.styles[tn]) {
49879 Roo.each(this.styles[tn], function(v) {
49880 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
49884 st.store.loadData(avs);
49891 updateFooter : function(ans)
49894 if (ans === false) {
49895 this.footDisp.dom.innerHTML = '';
49899 this.footerEls = ans.reverse();
49900 Roo.each(this.footerEls, function(a,i) {
49901 if (!a) { return; }
49902 html += html.length ? ' > ' : '';
49904 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49909 var sz = this.footDisp.up('td').getSize();
49910 this.footDisp.dom.style.width = (sz.width -10) + 'px';
49911 this.footDisp.dom.style.marginLeft = '5px';
49913 this.footDisp.dom.style.overflow = 'hidden';
49915 this.footDisp.dom.innerHTML = html;
49922 onDestroy : function(){
49925 this.tb.items.each(function(item){
49927 item.menu.removeAll();
49929 item.menu.el.destroy();
49937 onFirstFocus: function() {
49938 // need to do this for all the toolbars..
49939 this.tb.items.each(function(item){
49943 buildToolbar: function(tlist, nm, friendly_name, block)
49945 var editor = this.editor;
49946 var editorcore = this.editorcore;
49947 // create a new element.
49948 var wdiv = editor.wrap.createChild({
49950 }, editor.wrap.dom.firstChild.nextSibling, true);
49953 var tb = new Roo.Toolbar(wdiv);
49954 ///this.tb = tb; // << this sets the active toolbar..
49955 if (tlist === false && block) {
49956 tlist = block.contextMenu(this);
49959 tb.hasStyles = false;
49962 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
49964 var styles = Array.from(this.styles);
49968 if (styles && styles.length) {
49969 tb.hasStyles = true;
49970 // this needs a multi-select checkbox...
49971 tb.addField( new Roo.form.ComboBox({
49972 store: new Roo.data.SimpleStore({
49974 fields: ['val', 'selected'],
49977 name : '-roo-edit-className',
49978 attrname : 'className',
49979 displayField: 'val',
49983 triggerAction: 'all',
49984 emptyText:'Select Style',
49985 selectOnFocus:true,
49988 'select': function(c, r, i) {
49989 // initial support only for on class per el..
49990 tb.selectedNode.className = r ? r.get('val') : '';
49991 editorcore.syncValue();
49998 var tbc = Roo.form.HtmlEditor.ToolbarContext;
50001 for (var i = 0; i < tlist.length; i++) {
50003 // newer versions will use xtype cfg to create menus.
50004 if (typeof(tlist[i].xtype) != 'undefined') {
50006 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
50012 var item = tlist[i];
50013 tb.add(item.title + ": ");
50016 //optname == used so you can configure the options available..
50017 var opts = item.opts ? item.opts : false;
50018 if (item.optname) { // use the b
50019 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50024 // opts == pulldown..
50025 tb.addField( new Roo.form.ComboBox({
50026 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50028 fields: ['val', 'display'],
50031 name : '-roo-edit-' + tlist[i].name,
50033 attrname : tlist[i].name,
50034 stylename : item.style ? item.style : false,
50036 displayField: item.displayField ? item.displayField : 'val',
50037 valueField : 'val',
50039 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
50041 triggerAction: 'all',
50042 emptyText:'Select',
50043 selectOnFocus:true,
50044 width: item.width ? item.width : 130,
50046 'select': function(c, r, i) {
50047 if (tb.selectedNode.hasAttribute('data-block')) {
50048 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50049 b[c.attrname] = r.get('val');
50050 b.updateElement(tb.selectedNode);
50051 editorcore.syncValue();
50056 tb.selectedNode.style[c.stylename] = r.get('val');
50057 editorcore.syncValue();
50061 tb.selectedNode.removeAttribute(c.attrname);
50062 editorcore.syncValue();
50065 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50066 editorcore.syncValue();
50075 tb.addField( new Roo.form.TextField({
50078 //allowBlank:false,
50084 tb.addField( new Roo.form.TextField({
50085 name: '-roo-edit-' + tlist[i].name,
50086 attrname : tlist[i].name,
50092 'change' : function(f, nv, ov) {
50094 if (tb.selectedNode.hasAttribute('data-block')) {
50095 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50096 b[f.attrname] = nv;
50097 b.updateElement(tb.selectedNode);
50098 editorcore.syncValue();
50102 tb.selectedNode.setAttribute(f.attrname, nv);
50103 editorcore.syncValue();
50116 text: 'Stylesheets',
50119 click : function ()
50121 _this.editor.fireEvent('stylesheetsclick', _this.editor);
50129 text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50132 click : function ()
50134 var sn = tb.selectedNode;
50136 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50142 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50143 if (sn.hasAttribute('data-block')) {
50144 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
50145 sn.parentNode.removeChild(sn);
50147 } else if (sn && sn.tagName != 'BODY') {
50148 // remove and keep parents.
50149 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50154 var range = editorcore.createRange();
50156 range.setStart(stn,0);
50157 range.setEnd(stn,0);
50158 var selection = editorcore.getSelection();
50159 selection.removeAllRanges();
50160 selection.addRange(range);
50163 //_this.updateToolbar(null, null, pn);
50164 _this.updateToolbar(null, null, null);
50165 _this.updateFooter(false);
50176 tb.el.on('click', function(e){
50177 e.preventDefault(); // what does this do?
50179 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50182 // dont need to disable them... as they will get hidden
50187 buildFooter : function()
50190 var fel = this.editor.wrap.createChild();
50191 this.footer = new Roo.Toolbar(fel);
50192 // toolbar has scrolly on left / right?
50193 var footDisp= new Roo.Toolbar.Fill();
50199 handler : function() {
50200 _t.footDisp.scrollTo('left',0,true)
50204 this.footer.add( footDisp );
50209 handler : function() {
50211 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50215 var fel = Roo.get(footDisp.el);
50216 fel.addClass('x-editor-context');
50217 this.footDispWrap = fel;
50218 this.footDispWrap.overflow = 'hidden';
50220 this.footDisp = fel.createChild();
50221 this.footDispWrap.on('click', this.onContextClick, this)
50225 // when the footer contect changes
50226 onContextClick : function (ev,dom)
50228 ev.preventDefault();
50229 var cn = dom.className;
50231 if (!cn.match(/x-ed-loc-/)) {
50234 var n = cn.split('-').pop();
50235 var ans = this.footerEls;
50238 this.editorcore.selectNode(sel);
50241 this.updateToolbar(null, null, sel);
50258 * Ext JS Library 1.1.1
50259 * Copyright(c) 2006-2007, Ext JS, LLC.
50261 * Originally Released Under LGPL - original licence link has changed is not relivant.
50264 * <script type="text/javascript">
50268 * @class Roo.form.BasicForm
50269 * @extends Roo.util.Observable
50270 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50272 * @param {String/HTMLElement/Roo.Element} el The form element or its id
50273 * @param {Object} config Configuration options
50275 Roo.form.BasicForm = function(el, config){
50276 this.allItems = [];
50277 this.childForms = [];
50278 Roo.apply(this, config);
50280 * The Roo.form.Field items in this form.
50281 * @type MixedCollection
50285 this.items = new Roo.util.MixedCollection(false, function(o){
50286 return o.id || (o.id = Roo.id());
50290 * @event beforeaction
50291 * Fires before any action is performed. Return false to cancel the action.
50292 * @param {Form} this
50293 * @param {Action} action The action to be performed
50295 beforeaction: true,
50297 * @event actionfailed
50298 * Fires when an action fails.
50299 * @param {Form} this
50300 * @param {Action} action The action that failed
50302 actionfailed : true,
50304 * @event actioncomplete
50305 * Fires when an action is completed.
50306 * @param {Form} this
50307 * @param {Action} action The action that completed
50309 actioncomplete : true
50314 Roo.form.BasicForm.superclass.constructor.call(this);
50316 Roo.form.BasicForm.popover.apply();
50319 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50321 * @cfg {String} method
50322 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50325 * @cfg {DataReader} reader
50326 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50327 * This is optional as there is built-in support for processing JSON.
50330 * @cfg {DataReader} errorReader
50331 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50332 * This is completely optional as there is built-in support for processing JSON.
50335 * @cfg {String} url
50336 * The URL to use for form actions if one isn't supplied in the action options.
50339 * @cfg {Boolean} fileUpload
50340 * Set to true if this form is a file upload.
50344 * @cfg {Object} baseParams
50345 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50350 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50355 activeAction : null,
50358 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50359 * or setValues() data instead of when the form was first created.
50361 trackResetOnLoad : false,
50365 * childForms - used for multi-tab forms
50368 childForms : false,
50371 * allItems - full list of fields.
50377 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50378 * element by passing it or its id or mask the form itself by passing in true.
50381 waitMsgTarget : false,
50386 disableMask : false,
50389 * @cfg {Boolean} errorMask (true|false) default false
50394 * @cfg {Number} maskOffset Default 100
50399 initEl : function(el){
50400 this.el = Roo.get(el);
50401 this.id = this.el.id || Roo.id();
50402 this.el.on('submit', this.onSubmit, this);
50403 this.el.addClass('x-form');
50407 onSubmit : function(e){
50412 * Returns true if client-side validation on the form is successful.
50415 isValid : function(){
50417 var target = false;
50418 this.items.each(function(f){
50425 if(!target && f.el.isVisible(true)){
50430 if(this.errorMask && !valid){
50431 Roo.form.BasicForm.popover.mask(this, target);
50437 * Returns array of invalid form fields.
50441 invalidFields : function()
50444 this.items.each(function(f){
50457 * DEPRICATED Returns true if any fields in this form have changed since their original load.
50460 isDirty : function(){
50462 this.items.each(function(f){
50472 * Returns true if any fields in this form have changed since their original load. (New version)
50476 hasChanged : function()
50479 this.items.each(function(f){
50480 if(f.hasChanged()){
50489 * Resets all hasChanged to 'false' -
50490 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50491 * So hasChanged storage is only to be used for this purpose
50494 resetHasChanged : function()
50496 this.items.each(function(f){
50497 f.resetHasChanged();
50504 * Performs a predefined action (submit or load) or custom actions you define on this form.
50505 * @param {String} actionName The name of the action type
50506 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
50507 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50508 * accept other config options):
50510 Property Type Description
50511 ---------------- --------------- ----------------------------------------------------------------------------------
50512 url String The url for the action (defaults to the form's url)
50513 method String The form method to use (defaults to the form's method, or POST if not defined)
50514 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
50515 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
50516 validate the form on the client (defaults to false)
50518 * @return {BasicForm} this
50520 doAction : function(action, options){
50521 if(typeof action == 'string'){
50522 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50524 if(this.fireEvent('beforeaction', this, action) !== false){
50525 this.beforeAction(action);
50526 action.run.defer(100, action);
50532 * Shortcut to do a submit action.
50533 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50534 * @return {BasicForm} this
50536 submit : function(options){
50537 this.doAction('submit', options);
50542 * Shortcut to do a load action.
50543 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50544 * @return {BasicForm} this
50546 load : function(options){
50547 this.doAction('load', options);
50552 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50553 * @param {Record} record The record to edit
50554 * @return {BasicForm} this
50556 updateRecord : function(record){
50557 record.beginEdit();
50558 var fs = record.fields;
50559 fs.each(function(f){
50560 var field = this.findField(f.name);
50562 record.set(f.name, field.getValue());
50570 * Loads an Roo.data.Record into this form.
50571 * @param {Record} record The record to load
50572 * @return {BasicForm} this
50574 loadRecord : function(record){
50575 this.setValues(record.data);
50580 beforeAction : function(action){
50581 var o = action.options;
50583 if(!this.disableMask) {
50584 if(this.waitMsgTarget === true){
50585 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50586 }else if(this.waitMsgTarget){
50587 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50588 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50590 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50598 afterAction : function(action, success){
50599 this.activeAction = null;
50600 var o = action.options;
50602 if(!this.disableMask) {
50603 if(this.waitMsgTarget === true){
50605 }else if(this.waitMsgTarget){
50606 this.waitMsgTarget.unmask();
50608 Roo.MessageBox.updateProgress(1);
50609 Roo.MessageBox.hide();
50617 Roo.callback(o.success, o.scope, [this, action]);
50618 this.fireEvent('actioncomplete', this, action);
50622 // failure condition..
50623 // we have a scenario where updates need confirming.
50624 // eg. if a locking scenario exists..
50625 // we look for { errors : { needs_confirm : true }} in the response.
50627 (typeof(action.result) != 'undefined') &&
50628 (typeof(action.result.errors) != 'undefined') &&
50629 (typeof(action.result.errors.needs_confirm) != 'undefined')
50632 Roo.MessageBox.confirm(
50633 "Change requires confirmation",
50634 action.result.errorMsg,
50639 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
50649 Roo.callback(o.failure, o.scope, [this, action]);
50650 // show an error message if no failed handler is set..
50651 if (!this.hasListener('actionfailed')) {
50652 Roo.MessageBox.alert("Error",
50653 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50654 action.result.errorMsg :
50655 "Saving Failed, please check your entries or try again"
50659 this.fireEvent('actionfailed', this, action);
50665 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50666 * @param {String} id The value to search for
50669 findField : function(id){
50670 var field = this.items.get(id);
50672 this.items.each(function(f){
50673 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50679 return field || null;
50683 * Add a secondary form to this one,
50684 * Used to provide tabbed forms. One form is primary, with hidden values
50685 * which mirror the elements from the other forms.
50687 * @param {Roo.form.Form} form to add.
50690 addForm : function(form)
50693 if (this.childForms.indexOf(form) > -1) {
50697 this.childForms.push(form);
50699 Roo.each(form.allItems, function (fe) {
50701 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50702 if (this.findField(n)) { // already added..
50705 var add = new Roo.form.Hidden({
50708 add.render(this.el);
50715 * Mark fields in this form invalid in bulk.
50716 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50717 * @return {BasicForm} this
50719 markInvalid : function(errors){
50720 if(errors instanceof Array){
50721 for(var i = 0, len = errors.length; i < len; i++){
50722 var fieldError = errors[i];
50723 var f = this.findField(fieldError.id);
50725 f.markInvalid(fieldError.msg);
50731 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50732 field.markInvalid(errors[id]);
50736 Roo.each(this.childForms || [], function (f) {
50737 f.markInvalid(errors);
50744 * Set values for fields in this form in bulk.
50745 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50746 * @return {BasicForm} this
50748 setValues : function(values){
50749 if(values instanceof Array){ // array of objects
50750 for(var i = 0, len = values.length; i < len; i++){
50752 var f = this.findField(v.id);
50754 f.setValue(v.value);
50755 if(this.trackResetOnLoad){
50756 f.originalValue = f.getValue();
50760 }else{ // object hash
50763 if(typeof values[id] != 'function' && (field = this.findField(id))){
50765 if (field.setFromData &&
50766 field.valueField &&
50767 field.displayField &&
50768 // combos' with local stores can
50769 // be queried via setValue()
50770 // to set their value..
50771 (field.store && !field.store.isLocal)
50775 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50776 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50777 field.setFromData(sd);
50780 field.setValue(values[id]);
50784 if(this.trackResetOnLoad){
50785 field.originalValue = field.getValue();
50790 this.resetHasChanged();
50793 Roo.each(this.childForms || [], function (f) {
50794 f.setValues(values);
50795 f.resetHasChanged();
50802 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50803 * they are returned as an array.
50804 * @param {Boolean} asString
50807 getValues : function(asString)
50809 if (this.childForms) {
50810 // copy values from the child forms
50811 Roo.each(this.childForms, function (f) {
50812 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50817 if (typeof(FormData) != 'undefined' && asString !== true) {
50818 // this relies on a 'recent' version of chrome apparently...
50820 var fd = (new FormData(this.el.dom)).entries();
50822 var ent = fd.next();
50823 while (!ent.done) {
50824 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50835 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50836 if(asString === true){
50839 return Roo.urlDecode(fs);
50843 * Returns the fields in this form as an object with key/value pairs.
50844 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50845 * Normally this will not return readOnly data
50846 * @param {Boolean} with_readonly return readonly field data.
50849 getFieldValues : function(with_readonly)
50851 if (this.childForms) {
50852 // copy values from the child forms
50853 // should this call getFieldValues - probably not as we do not currently copy
50854 // hidden fields when we generate..
50855 Roo.each(this.childForms, function (f) {
50856 this.setValues(f.getFieldValues());
50861 this.items.each(function(f){
50863 if (f.readOnly && with_readonly !== true) {
50864 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50865 // if a subform contains a copy of them.
50866 // if you have subforms with the same editable data, you will need to copy the data back
50870 if (!f.getName()) {
50873 var v = f.getValue();
50874 if (f.inputType =='radio') {
50875 if (typeof(ret[f.getName()]) == 'undefined') {
50876 ret[f.getName()] = ''; // empty..
50879 if (!f.el.dom.checked) {
50883 v = f.el.dom.value;
50887 // not sure if this supported any more..
50888 if ((typeof(v) == 'object') && f.getRawValue) {
50889 v = f.getRawValue() ; // dates..
50891 // combo boxes where name != hiddenName...
50892 if (f.name != f.getName()) {
50893 ret[f.name] = f.getRawValue();
50895 ret[f.getName()] = v;
50902 * Clears all invalid messages in this form.
50903 * @return {BasicForm} this
50905 clearInvalid : function(){
50906 this.items.each(function(f){
50910 Roo.each(this.childForms || [], function (f) {
50919 * Resets this form.
50920 * @return {BasicForm} this
50922 reset : function(){
50923 this.items.each(function(f){
50927 Roo.each(this.childForms || [], function (f) {
50930 this.resetHasChanged();
50936 * Add Roo.form components to this form.
50937 * @param {Field} field1
50938 * @param {Field} field2 (optional)
50939 * @param {Field} etc (optional)
50940 * @return {BasicForm} this
50943 this.items.addAll(Array.prototype.slice.call(arguments, 0));
50949 * Removes a field from the items collection (does NOT remove its markup).
50950 * @param {Field} field
50951 * @return {BasicForm} this
50953 remove : function(field){
50954 this.items.remove(field);
50959 * Looks at the fields in this form, checks them for an id attribute,
50960 * and calls applyTo on the existing dom element with that id.
50961 * @return {BasicForm} this
50963 render : function(){
50964 this.items.each(function(f){
50965 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50973 * Calls {@link Ext#apply} for all fields in this form with the passed object.
50974 * @param {Object} values
50975 * @return {BasicForm} this
50977 applyToFields : function(o){
50978 this.items.each(function(f){
50985 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50986 * @param {Object} values
50987 * @return {BasicForm} this
50989 applyIfToFields : function(o){
50990 this.items.each(function(f){
50998 Roo.BasicForm = Roo.form.BasicForm;
51000 Roo.apply(Roo.form.BasicForm, {
51014 intervalID : false,
51020 if(this.isApplied){
51025 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51026 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51027 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51028 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51031 this.maskEl.top.enableDisplayMode("block");
51032 this.maskEl.left.enableDisplayMode("block");
51033 this.maskEl.bottom.enableDisplayMode("block");
51034 this.maskEl.right.enableDisplayMode("block");
51036 Roo.get(document.body).on('click', function(){
51040 Roo.get(document.body).on('touchstart', function(){
51044 this.isApplied = true
51047 mask : function(form, target)
51051 this.target = target;
51053 if(!this.form.errorMask || !target.el){
51057 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51059 var ot = this.target.el.calcOffsetsTo(scrollable);
51061 var scrollTo = ot[1] - this.form.maskOffset;
51063 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51065 scrollable.scrollTo('top', scrollTo);
51067 var el = this.target.wrap || this.target.el;
51069 var box = el.getBox();
51071 this.maskEl.top.setStyle('position', 'absolute');
51072 this.maskEl.top.setStyle('z-index', 10000);
51073 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51074 this.maskEl.top.setLeft(0);
51075 this.maskEl.top.setTop(0);
51076 this.maskEl.top.show();
51078 this.maskEl.left.setStyle('position', 'absolute');
51079 this.maskEl.left.setStyle('z-index', 10000);
51080 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51081 this.maskEl.left.setLeft(0);
51082 this.maskEl.left.setTop(box.y - this.padding);
51083 this.maskEl.left.show();
51085 this.maskEl.bottom.setStyle('position', 'absolute');
51086 this.maskEl.bottom.setStyle('z-index', 10000);
51087 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51088 this.maskEl.bottom.setLeft(0);
51089 this.maskEl.bottom.setTop(box.bottom + this.padding);
51090 this.maskEl.bottom.show();
51092 this.maskEl.right.setStyle('position', 'absolute');
51093 this.maskEl.right.setStyle('z-index', 10000);
51094 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51095 this.maskEl.right.setLeft(box.right + this.padding);
51096 this.maskEl.right.setTop(box.y - this.padding);
51097 this.maskEl.right.show();
51099 this.intervalID = window.setInterval(function() {
51100 Roo.form.BasicForm.popover.unmask();
51103 window.onwheel = function(){ return false;};
51105 (function(){ this.isMasked = true; }).defer(500, this);
51109 unmask : function()
51111 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51115 this.maskEl.top.setStyle('position', 'absolute');
51116 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51117 this.maskEl.top.hide();
51119 this.maskEl.left.setStyle('position', 'absolute');
51120 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51121 this.maskEl.left.hide();
51123 this.maskEl.bottom.setStyle('position', 'absolute');
51124 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51125 this.maskEl.bottom.hide();
51127 this.maskEl.right.setStyle('position', 'absolute');
51128 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51129 this.maskEl.right.hide();
51131 window.onwheel = function(){ return true;};
51133 if(this.intervalID){
51134 window.clearInterval(this.intervalID);
51135 this.intervalID = false;
51138 this.isMasked = false;
51146 * Ext JS Library 1.1.1
51147 * Copyright(c) 2006-2007, Ext JS, LLC.
51149 * Originally Released Under LGPL - original licence link has changed is not relivant.
51152 * <script type="text/javascript">
51156 * @class Roo.form.Form
51157 * @extends Roo.form.BasicForm
51158 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51159 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51161 * @param {Object} config Configuration options
51163 Roo.form.Form = function(config){
51165 if (config.items) {
51166 xitems = config.items;
51167 delete config.items;
51171 Roo.form.Form.superclass.constructor.call(this, null, config);
51172 this.url = this.url || this.action;
51174 this.root = new Roo.form.Layout(Roo.applyIf({
51178 this.active = this.root;
51180 * Array of all the buttons that have been added to this form via {@link addButton}
51184 this.allItems = [];
51187 * @event clientvalidation
51188 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51189 * @param {Form} this
51190 * @param {Boolean} valid true if the form has passed client-side validation
51192 clientvalidation: true,
51195 * Fires when the form is rendered
51196 * @param {Roo.form.Form} form
51201 if (this.progressUrl) {
51202 // push a hidden field onto the list of fields..
51206 name : 'UPLOAD_IDENTIFIER'
51211 Roo.each(xitems, this.addxtype, this);
51215 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51217 * @cfg {Roo.Button} buttons[] buttons at bottom of form
51221 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51224 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51227 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51229 buttonAlign:'center',
51232 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51237 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51238 * This property cascades to child containers if not set.
51243 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51244 * fires a looping event with that state. This is required to bind buttons to the valid
51245 * state using the config value formBind:true on the button.
51247 monitorValid : false,
51250 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51255 * @cfg {String} progressUrl - Url to return progress data
51258 progressUrl : false,
51260 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51261 * sending a formdata with extra parameters - eg uploaded elements.
51267 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51268 * fields are added and the column is closed. If no fields are passed the column remains open
51269 * until end() is called.
51270 * @param {Object} config The config to pass to the column
51271 * @param {Field} field1 (optional)
51272 * @param {Field} field2 (optional)
51273 * @param {Field} etc (optional)
51274 * @return Column The column container object
51276 column : function(c){
51277 var col = new Roo.form.Column(c);
51279 if(arguments.length > 1){ // duplicate code required because of Opera
51280 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51287 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51288 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51289 * until end() is called.
51290 * @param {Object} config The config to pass to the fieldset
51291 * @param {Field} field1 (optional)
51292 * @param {Field} field2 (optional)
51293 * @param {Field} etc (optional)
51294 * @return FieldSet The fieldset container object
51296 fieldset : function(c){
51297 var fs = new Roo.form.FieldSet(c);
51299 if(arguments.length > 1){ // duplicate code required because of Opera
51300 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51307 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51308 * fields are added and the container is closed. If no fields are passed the container remains open
51309 * until end() is called.
51310 * @param {Object} config The config to pass to the Layout
51311 * @param {Field} field1 (optional)
51312 * @param {Field} field2 (optional)
51313 * @param {Field} etc (optional)
51314 * @return Layout The container object
51316 container : function(c){
51317 var l = new Roo.form.Layout(c);
51319 if(arguments.length > 1){ // duplicate code required because of Opera
51320 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51327 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51328 * @param {Object} container A Roo.form.Layout or subclass of Layout
51329 * @return {Form} this
51331 start : function(c){
51332 // cascade label info
51333 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51334 this.active.stack.push(c);
51335 c.ownerCt = this.active;
51341 * Closes the current open container
51342 * @return {Form} this
51345 if(this.active == this.root){
51348 this.active = this.active.ownerCt;
51353 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
51354 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51355 * as the label of the field.
51356 * @param {Field} field1
51357 * @param {Field} field2 (optional)
51358 * @param {Field} etc. (optional)
51359 * @return {Form} this
51362 this.active.stack.push.apply(this.active.stack, arguments);
51363 this.allItems.push.apply(this.allItems,arguments);
51365 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51366 if(a[i].isFormField){
51371 Roo.form.Form.superclass.add.apply(this, r);
51381 * Find any element that has been added to a form, using it's ID or name
51382 * This can include framesets, columns etc. along with regular fields..
51383 * @param {String} id - id or name to find.
51385 * @return {Element} e - or false if nothing found.
51387 findbyId : function(id)
51393 Roo.each(this.allItems, function(f){
51394 if (f.id == id || f.name == id ){
51405 * Render this form into the passed container. This should only be called once!
51406 * @param {String/HTMLElement/Element} container The element this component should be rendered into
51407 * @return {Form} this
51409 render : function(ct)
51415 var o = this.autoCreate || {
51417 method : this.method || 'POST',
51418 id : this.id || Roo.id()
51420 this.initEl(ct.createChild(o));
51422 this.root.render(this.el);
51426 this.items.each(function(f){
51427 f.render('x-form-el-'+f.id);
51430 if(this.buttons.length > 0){
51431 // tables are required to maintain order and for correct IE layout
51432 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51433 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51434 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51436 var tr = tb.getElementsByTagName('tr')[0];
51437 for(var i = 0, len = this.buttons.length; i < len; i++) {
51438 var b = this.buttons[i];
51439 var td = document.createElement('td');
51440 td.className = 'x-form-btn-td';
51441 b.render(tr.appendChild(td));
51444 if(this.monitorValid){ // initialize after render
51445 this.startMonitoring();
51447 this.fireEvent('rendered', this);
51452 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51453 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51454 * object or a valid Roo.DomHelper element config
51455 * @param {Function} handler The function called when the button is clicked
51456 * @param {Object} scope (optional) The scope of the handler function
51457 * @return {Roo.Button}
51459 addButton : function(config, handler, scope){
51463 minWidth: this.minButtonWidth,
51466 if(typeof config == "string"){
51469 Roo.apply(bc, config);
51471 var btn = new Roo.Button(null, bc);
51472 this.buttons.push(btn);
51477 * Adds a series of form elements (using the xtype property as the factory method.
51478 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51479 * @param {Object} config
51482 addxtype : function()
51484 var ar = Array.prototype.slice.call(arguments, 0);
51486 for(var i = 0; i < ar.length; i++) {
51488 continue; // skip -- if this happends something invalid got sent, we
51489 // should ignore it, as basically that interface element will not show up
51490 // and that should be pretty obvious!!
51493 if (Roo.form[ar[i].xtype]) {
51495 var fe = Roo.factory(ar[i], Roo.form);
51501 fe.store.form = this;
51506 this.allItems.push(fe);
51507 if (fe.items && fe.addxtype) {
51508 fe.addxtype.apply(fe, fe.items);
51518 // console.log('adding ' + ar[i].xtype);
51520 if (ar[i].xtype == 'Button') {
51521 //console.log('adding button');
51522 //console.log(ar[i]);
51523 this.addButton(ar[i]);
51524 this.allItems.push(fe);
51528 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51529 alert('end is not supported on xtype any more, use items');
51531 // //console.log('adding end');
51539 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51540 * option "monitorValid"
51542 startMonitoring : function(){
51545 Roo.TaskMgr.start({
51546 run : this.bindHandler,
51547 interval : this.monitorPoll || 200,
51554 * Stops monitoring of the valid state of this form
51556 stopMonitoring : function(){
51557 this.bound = false;
51561 bindHandler : function(){
51563 return false; // stops binding
51566 this.items.each(function(f){
51567 if(!f.isValid(true)){
51572 for(var i = 0, len = this.buttons.length; i < len; i++){
51573 var btn = this.buttons[i];
51574 if(btn.formBind === true && btn.disabled === valid){
51575 btn.setDisabled(!valid);
51578 this.fireEvent('clientvalidation', this, valid);
51592 Roo.Form = Roo.form.Form;
51595 * Ext JS Library 1.1.1
51596 * Copyright(c) 2006-2007, Ext JS, LLC.
51598 * Originally Released Under LGPL - original licence link has changed is not relivant.
51601 * <script type="text/javascript">
51604 // as we use this in bootstrap.
51605 Roo.namespace('Roo.form');
51607 * @class Roo.form.Action
51608 * Internal Class used to handle form actions
51610 * @param {Roo.form.BasicForm} el The form element or its id
51611 * @param {Object} config Configuration options
51616 // define the action interface
51617 Roo.form.Action = function(form, options){
51619 this.options = options || {};
51622 * Client Validation Failed
51625 Roo.form.Action.CLIENT_INVALID = 'client';
51627 * Server Validation Failed
51630 Roo.form.Action.SERVER_INVALID = 'server';
51632 * Connect to Server Failed
51635 Roo.form.Action.CONNECT_FAILURE = 'connect';
51637 * Reading Data from Server Failed
51640 Roo.form.Action.LOAD_FAILURE = 'load';
51642 Roo.form.Action.prototype = {
51644 failureType : undefined,
51645 response : undefined,
51646 result : undefined,
51648 // interface method
51649 run : function(options){
51653 // interface method
51654 success : function(response){
51658 // interface method
51659 handleResponse : function(response){
51663 // default connection failure
51664 failure : function(response){
51666 this.response = response;
51667 this.failureType = Roo.form.Action.CONNECT_FAILURE;
51668 this.form.afterAction(this, false);
51671 processResponse : function(response){
51672 this.response = response;
51673 if(!response.responseText){
51676 this.result = this.handleResponse(response);
51677 return this.result;
51680 // utility functions used internally
51681 getUrl : function(appendParams){
51682 var url = this.options.url || this.form.url || this.form.el.dom.action;
51684 var p = this.getParams();
51686 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51692 getMethod : function(){
51693 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51696 getParams : function(){
51697 var bp = this.form.baseParams;
51698 var p = this.options.params;
51700 if(typeof p == "object"){
51701 p = Roo.urlEncode(Roo.applyIf(p, bp));
51702 }else if(typeof p == 'string' && bp){
51703 p += '&' + Roo.urlEncode(bp);
51706 p = Roo.urlEncode(bp);
51711 createCallback : function(){
51713 success: this.success,
51714 failure: this.failure,
51716 timeout: (this.form.timeout*1000),
51717 upload: this.form.fileUpload ? this.success : undefined
51722 Roo.form.Action.Submit = function(form, options){
51723 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51726 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51729 haveProgress : false,
51730 uploadComplete : false,
51732 // uploadProgress indicator.
51733 uploadProgress : function()
51735 if (!this.form.progressUrl) {
51739 if (!this.haveProgress) {
51740 Roo.MessageBox.progress("Uploading", "Uploading");
51742 if (this.uploadComplete) {
51743 Roo.MessageBox.hide();
51747 this.haveProgress = true;
51749 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51751 var c = new Roo.data.Connection();
51753 url : this.form.progressUrl,
51758 success : function(req){
51759 //console.log(data);
51763 rdata = Roo.decode(req.responseText)
51765 Roo.log("Invalid data from server..");
51769 if (!rdata || !rdata.success) {
51771 Roo.MessageBox.alert(Roo.encode(rdata));
51774 var data = rdata.data;
51776 if (this.uploadComplete) {
51777 Roo.MessageBox.hide();
51782 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51783 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51786 this.uploadProgress.defer(2000,this);
51789 failure: function(data) {
51790 Roo.log('progress url failed ');
51801 // run get Values on the form, so it syncs any secondary forms.
51802 this.form.getValues();
51804 var o = this.options;
51805 var method = this.getMethod();
51806 var isPost = method == 'POST';
51807 if(o.clientValidation === false || this.form.isValid()){
51809 if (this.form.progressUrl) {
51810 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51811 (new Date() * 1) + '' + Math.random());
51816 Roo.Ajax.request(Roo.apply(this.createCallback(), {
51817 form:this.form.el.dom,
51818 url:this.getUrl(!isPost),
51820 params:isPost ? this.getParams() : null,
51821 isUpload: this.form.fileUpload,
51822 formData : this.form.formData
51825 this.uploadProgress();
51827 }else if (o.clientValidation !== false){ // client validation failed
51828 this.failureType = Roo.form.Action.CLIENT_INVALID;
51829 this.form.afterAction(this, false);
51833 success : function(response)
51835 this.uploadComplete= true;
51836 if (this.haveProgress) {
51837 Roo.MessageBox.hide();
51841 var result = this.processResponse(response);
51842 if(result === true || result.success){
51843 this.form.afterAction(this, true);
51847 this.form.markInvalid(result.errors);
51848 this.failureType = Roo.form.Action.SERVER_INVALID;
51850 this.form.afterAction(this, false);
51852 failure : function(response)
51854 this.uploadComplete= true;
51855 if (this.haveProgress) {
51856 Roo.MessageBox.hide();
51859 this.response = response;
51860 this.failureType = Roo.form.Action.CONNECT_FAILURE;
51861 this.form.afterAction(this, false);
51864 handleResponse : function(response){
51865 if(this.form.errorReader){
51866 var rs = this.form.errorReader.read(response);
51869 for(var i = 0, len = rs.records.length; i < len; i++) {
51870 var r = rs.records[i];
51871 errors[i] = r.data;
51874 if(errors.length < 1){
51878 success : rs.success,
51884 ret = Roo.decode(response.responseText);
51888 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51898 Roo.form.Action.Load = function(form, options){
51899 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51900 this.reader = this.form.reader;
51903 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51908 Roo.Ajax.request(Roo.apply(
51909 this.createCallback(), {
51910 method:this.getMethod(),
51911 url:this.getUrl(false),
51912 params:this.getParams()
51916 success : function(response){
51918 var result = this.processResponse(response);
51919 if(result === true || !result.success || !result.data){
51920 this.failureType = Roo.form.Action.LOAD_FAILURE;
51921 this.form.afterAction(this, false);
51924 this.form.clearInvalid();
51925 this.form.setValues(result.data);
51926 this.form.afterAction(this, true);
51929 handleResponse : function(response){
51930 if(this.form.reader){
51931 var rs = this.form.reader.read(response);
51932 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51934 success : rs.success,
51938 return Roo.decode(response.responseText);
51942 Roo.form.Action.ACTION_TYPES = {
51943 'load' : Roo.form.Action.Load,
51944 'submit' : Roo.form.Action.Submit
51947 * Ext JS Library 1.1.1
51948 * Copyright(c) 2006-2007, Ext JS, LLC.
51950 * Originally Released Under LGPL - original licence link has changed is not relivant.
51953 * <script type="text/javascript">
51957 * @class Roo.form.Layout
51958 * @extends Roo.Component
51959 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51960 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51962 * @param {Object} config Configuration options
51964 Roo.form.Layout = function(config){
51966 if (config.items) {
51967 xitems = config.items;
51968 delete config.items;
51970 Roo.form.Layout.superclass.constructor.call(this, config);
51972 Roo.each(xitems, this.addxtype, this);
51976 Roo.extend(Roo.form.Layout, Roo.Component, {
51978 * @cfg {String/Object} autoCreate
51979 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51982 * @cfg {String/Object/Function} style
51983 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51984 * a function which returns such a specification.
51987 * @cfg {String} labelAlign
51988 * Valid values are "left," "top" and "right" (defaults to "left")
51991 * @cfg {Number} labelWidth
51992 * Fixed width in pixels of all field labels (defaults to undefined)
51995 * @cfg {Boolean} clear
51996 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
52000 * @cfg {String} labelSeparator
52001 * The separator to use after field labels (defaults to ':')
52003 labelSeparator : ':',
52005 * @cfg {Boolean} hideLabels
52006 * True to suppress the display of field labels in this layout (defaults to false)
52008 hideLabels : false,
52011 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
52016 onRender : function(ct, position){
52017 if(this.el){ // from markup
52018 this.el = Roo.get(this.el);
52019 }else { // generate
52020 var cfg = this.getAutoCreate();
52021 this.el = ct.createChild(cfg, position);
52024 this.el.applyStyles(this.style);
52026 if(this.labelAlign){
52027 this.el.addClass('x-form-label-'+this.labelAlign);
52029 if(this.hideLabels){
52030 this.labelStyle = "display:none";
52031 this.elementStyle = "padding-left:0;";
52033 if(typeof this.labelWidth == 'number'){
52034 this.labelStyle = "width:"+this.labelWidth+"px;";
52035 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52037 if(this.labelAlign == 'top'){
52038 this.labelStyle = "width:auto;";
52039 this.elementStyle = "padding-left:0;";
52042 var stack = this.stack;
52043 var slen = stack.length;
52045 if(!this.fieldTpl){
52046 var t = new Roo.Template(
52047 '<div class="x-form-item {5}">',
52048 '<label for="{0}" style="{2}">{1}{4}</label>',
52049 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52051 '</div><div class="x-form-clear-left"></div>'
52053 t.disableFormats = true;
52055 Roo.form.Layout.prototype.fieldTpl = t;
52057 for(var i = 0; i < slen; i++) {
52058 if(stack[i].isFormField){
52059 this.renderField(stack[i]);
52061 this.renderComponent(stack[i]);
52066 this.el.createChild({cls:'x-form-clear'});
52071 renderField : function(f){
52072 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52075 f.labelStyle||this.labelStyle||'', //2
52076 this.elementStyle||'', //3
52077 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52078 f.itemCls||this.itemCls||'' //5
52079 ], true).getPrevSibling());
52083 renderComponent : function(c){
52084 c.render(c.isLayout ? this.el : this.el.createChild());
52087 * Adds a object form elements (using the xtype property as the factory method.)
52088 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
52089 * @param {Object} config
52091 addxtype : function(o)
52093 // create the lement.
52094 o.form = this.form;
52095 var fe = Roo.factory(o, Roo.form);
52096 this.form.allItems.push(fe);
52097 this.stack.push(fe);
52099 if (fe.isFormField) {
52100 this.form.items.add(fe);
52108 * @class Roo.form.Column
52109 * @extends Roo.form.Layout
52110 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52111 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52113 * @param {Object} config Configuration options
52115 Roo.form.Column = function(config){
52116 Roo.form.Column.superclass.constructor.call(this, config);
52119 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52121 * @cfg {Number/String} width
52122 * The fixed width of the column in pixels or CSS value (defaults to "auto")
52125 * @cfg {String/Object} autoCreate
52126 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52130 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52133 onRender : function(ct, position){
52134 Roo.form.Column.superclass.onRender.call(this, ct, position);
52136 this.el.setWidth(this.width);
52143 * @class Roo.form.Row
52144 * @extends Roo.form.Layout
52145 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52146 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52148 * @param {Object} config Configuration options
52152 Roo.form.Row = function(config){
52153 Roo.form.Row.superclass.constructor.call(this, config);
52156 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52158 * @cfg {Number/String} width
52159 * The fixed width of the column in pixels or CSS value (defaults to "auto")
52162 * @cfg {Number/String} height
52163 * The fixed height of the column in pixels or CSS value (defaults to "auto")
52165 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52169 onRender : function(ct, position){
52170 //console.log('row render');
52172 var t = new Roo.Template(
52173 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52174 '<label for="{0}" style="{2}">{1}{4}</label>',
52175 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52179 t.disableFormats = true;
52181 Roo.form.Layout.prototype.rowTpl = t;
52183 this.fieldTpl = this.rowTpl;
52185 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52186 var labelWidth = 100;
52188 if ((this.labelAlign != 'top')) {
52189 if (typeof this.labelWidth == 'number') {
52190 labelWidth = this.labelWidth
52192 this.padWidth = 20 + labelWidth;
52196 Roo.form.Column.superclass.onRender.call(this, ct, position);
52198 this.el.setWidth(this.width);
52201 this.el.setHeight(this.height);
52206 renderField : function(f){
52207 f.fieldEl = this.fieldTpl.append(this.el, [
52208 f.id, f.fieldLabel,
52209 f.labelStyle||this.labelStyle||'',
52210 this.elementStyle||'',
52211 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52212 f.itemCls||this.itemCls||'',
52213 f.width ? f.width + this.padWidth : 160 + this.padWidth
52220 * @class Roo.form.FieldSet
52221 * @extends Roo.form.Layout
52222 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52223 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52225 * @param {Object} config Configuration options
52227 Roo.form.FieldSet = function(config){
52228 Roo.form.FieldSet.superclass.constructor.call(this, config);
52231 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52233 * @cfg {String} legend
52234 * The text to display as the legend for the FieldSet (defaults to '')
52237 * @cfg {String/Object} autoCreate
52238 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52242 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52245 onRender : function(ct, position){
52246 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52248 this.setLegend(this.legend);
52253 setLegend : function(text){
52255 this.el.child('legend').update(text);
52260 * Ext JS Library 1.1.1
52261 * Copyright(c) 2006-2007, Ext JS, LLC.
52263 * Originally Released Under LGPL - original licence link has changed is not relivant.
52266 * <script type="text/javascript">
52269 * @class Roo.form.VTypes
52270 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52273 Roo.form.VTypes = function(){
52274 // closure these in so they are only created once.
52275 var alpha = /^[a-zA-Z_]+$/;
52276 var alphanum = /^[a-zA-Z0-9_]+$/;
52277 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52278 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52280 // All these messages and functions are configurable
52283 * The function used to validate email addresses
52284 * @param {String} value The email address
52286 'email' : function(v){
52287 return email.test(v);
52290 * The error text to display when the email validation function returns false
52293 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52295 * The keystroke filter mask to be applied on email input
52298 'emailMask' : /[a-z0-9_\.\-@]/i,
52301 * The function used to validate URLs
52302 * @param {String} value The URL
52304 'url' : function(v){
52305 return url.test(v);
52308 * The error text to display when the url validation function returns false
52311 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52314 * The function used to validate alpha values
52315 * @param {String} value The value
52317 'alpha' : function(v){
52318 return alpha.test(v);
52321 * The error text to display when the alpha validation function returns false
52324 'alphaText' : 'This field should only contain letters and _',
52326 * The keystroke filter mask to be applied on alpha input
52329 'alphaMask' : /[a-z_]/i,
52332 * The function used to validate alphanumeric values
52333 * @param {String} value The value
52335 'alphanum' : function(v){
52336 return alphanum.test(v);
52339 * The error text to display when the alphanumeric validation function returns false
52342 'alphanumText' : 'This field should only contain letters, numbers and _',
52344 * The keystroke filter mask to be applied on alphanumeric input
52347 'alphanumMask' : /[a-z0-9_]/i
52349 }();//<script type="text/javascript">
52352 * @class Roo.form.FCKeditor
52353 * @extends Roo.form.TextArea
52354 * Wrapper around the FCKEditor http://www.fckeditor.net
52356 * Creates a new FCKeditor
52357 * @param {Object} config Configuration options
52359 Roo.form.FCKeditor = function(config){
52360 Roo.form.FCKeditor.superclass.constructor.call(this, config);
52363 * @event editorinit
52364 * Fired when the editor is initialized - you can add extra handlers here..
52365 * @param {FCKeditor} this
52366 * @param {Object} the FCK object.
52373 Roo.form.FCKeditor.editors = { };
52374 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52376 //defaultAutoCreate : {
52377 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
52381 * @cfg {Object} fck options - see fck manual for details.
52386 * @cfg {Object} fck toolbar set (Basic or Default)
52388 toolbarSet : 'Basic',
52390 * @cfg {Object} fck BasePath
52392 basePath : '/fckeditor/',
52400 onRender : function(ct, position)
52403 this.defaultAutoCreate = {
52405 style:"width:300px;height:60px;",
52406 autocomplete: "new-password"
52409 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52412 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52413 if(this.preventScrollbars){
52414 this.el.setStyle("overflow", "hidden");
52416 this.el.setHeight(this.growMin);
52419 //console.log('onrender' + this.getId() );
52420 Roo.form.FCKeditor.editors[this.getId()] = this;
52423 this.replaceTextarea() ;
52427 getEditor : function() {
52428 return this.fckEditor;
52431 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
52432 * @param {Mixed} value The value to set
52436 setValue : function(value)
52438 //console.log('setValue: ' + value);
52440 if(typeof(value) == 'undefined') { // not sure why this is happending...
52443 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52445 //if(!this.el || !this.getEditor()) {
52446 // this.value = value;
52447 //this.setValue.defer(100,this,[value]);
52451 if(!this.getEditor()) {
52455 this.getEditor().SetData(value);
52462 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
52463 * @return {Mixed} value The field value
52465 getValue : function()
52468 if (this.frame && this.frame.dom.style.display == 'none') {
52469 return Roo.form.FCKeditor.superclass.getValue.call(this);
52472 if(!this.el || !this.getEditor()) {
52474 // this.getValue.defer(100,this);
52479 var value=this.getEditor().GetData();
52480 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52481 return Roo.form.FCKeditor.superclass.getValue.call(this);
52487 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
52488 * @return {Mixed} value The field value
52490 getRawValue : function()
52492 if (this.frame && this.frame.dom.style.display == 'none') {
52493 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52496 if(!this.el || !this.getEditor()) {
52497 //this.getRawValue.defer(100,this);
52504 var value=this.getEditor().GetData();
52505 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52506 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52510 setSize : function(w,h) {
52514 //if (this.frame && this.frame.dom.style.display == 'none') {
52515 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52518 //if(!this.el || !this.getEditor()) {
52519 // this.setSize.defer(100,this, [w,h]);
52525 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52527 this.frame.dom.setAttribute('width', w);
52528 this.frame.dom.setAttribute('height', h);
52529 this.frame.setSize(w,h);
52533 toggleSourceEdit : function(value) {
52537 this.el.dom.style.display = value ? '' : 'none';
52538 this.frame.dom.style.display = value ? 'none' : '';
52543 focus: function(tag)
52545 if (this.frame.dom.style.display == 'none') {
52546 return Roo.form.FCKeditor.superclass.focus.call(this);
52548 if(!this.el || !this.getEditor()) {
52549 this.focus.defer(100,this, [tag]);
52556 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52557 this.getEditor().Focus();
52559 if (!this.getEditor().Selection.GetSelection()) {
52560 this.focus.defer(100,this, [tag]);
52565 var r = this.getEditor().EditorDocument.createRange();
52566 r.setStart(tgs[0],0);
52567 r.setEnd(tgs[0],0);
52568 this.getEditor().Selection.GetSelection().removeAllRanges();
52569 this.getEditor().Selection.GetSelection().addRange(r);
52570 this.getEditor().Focus();
52577 replaceTextarea : function()
52579 if ( document.getElementById( this.getId() + '___Frame' ) ) {
52582 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52584 // We must check the elements firstly using the Id and then the name.
52585 var oTextarea = document.getElementById( this.getId() );
52587 var colElementsByName = document.getElementsByName( this.getId() ) ;
52589 oTextarea.style.display = 'none' ;
52591 if ( oTextarea.tabIndex ) {
52592 this.TabIndex = oTextarea.tabIndex ;
52595 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52596 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52597 this.frame = Roo.get(this.getId() + '___Frame')
52600 _getConfigHtml : function()
52604 for ( var o in this.fckconfig ) {
52605 sConfig += sConfig.length > 0 ? '&' : '';
52606 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52609 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52613 _getIFrameHtml : function()
52615 var sFile = 'fckeditor.html' ;
52616 /* no idea what this is about..
52619 if ( (/fcksource=true/i).test( window.top.location.search ) )
52620 sFile = 'fckeditor.original.html' ;
52625 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52626 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
52629 var html = '<iframe id="' + this.getId() +
52630 '___Frame" src="' + sLink +
52631 '" width="' + this.width +
52632 '" height="' + this.height + '"' +
52633 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
52634 ' frameborder="0" scrolling="no"></iframe>' ;
52639 _insertHtmlBefore : function( html, element )
52641 if ( element.insertAdjacentHTML ) {
52643 element.insertAdjacentHTML( 'beforeBegin', html ) ;
52645 var oRange = document.createRange() ;
52646 oRange.setStartBefore( element ) ;
52647 var oFragment = oRange.createContextualFragment( html );
52648 element.parentNode.insertBefore( oFragment, element ) ;
52661 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52663 function FCKeditor_OnComplete(editorInstance){
52664 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52665 f.fckEditor = editorInstance;
52666 //console.log("loaded");
52667 f.fireEvent('editorinit', f, editorInstance);
52687 //<script type="text/javascript">
52689 * @class Roo.form.GridField
52690 * @extends Roo.form.Field
52691 * Embed a grid (or editable grid into a form)
52694 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52696 * xgrid.store = Roo.data.Store
52697 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52698 * xgrid.store.reader = Roo.data.JsonReader
52702 * Creates a new GridField
52703 * @param {Object} config Configuration options
52705 Roo.form.GridField = function(config){
52706 Roo.form.GridField.superclass.constructor.call(this, config);
52710 Roo.extend(Roo.form.GridField, Roo.form.Field, {
52712 * @cfg {Number} width - used to restrict width of grid..
52716 * @cfg {Number} height - used to restrict height of grid..
52720 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52726 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52727 * {tag: "input", type: "checkbox", autocomplete: "off"})
52729 // defaultAutoCreate : { tag: 'div' },
52730 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52732 * @cfg {String} addTitle Text to include for adding a title.
52736 onResize : function(){
52737 Roo.form.Field.superclass.onResize.apply(this, arguments);
52740 initEvents : function(){
52741 // Roo.form.Checkbox.superclass.initEvents.call(this);
52742 // has no events...
52747 getResizeEl : function(){
52751 getPositionEl : function(){
52756 onRender : function(ct, position){
52758 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52759 var style = this.style;
52762 Roo.form.GridField.superclass.onRender.call(this, ct, position);
52763 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52764 this.viewEl = this.wrap.createChild({ tag: 'div' });
52766 this.viewEl.applyStyles(style);
52769 this.viewEl.setWidth(this.width);
52772 this.viewEl.setHeight(this.height);
52774 //if(this.inputValue !== undefined){
52775 //this.setValue(this.value);
52778 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52781 this.grid.render();
52782 this.grid.getDataSource().on('remove', this.refreshValue, this);
52783 this.grid.getDataSource().on('update', this.refreshValue, this);
52784 this.grid.on('afteredit', this.refreshValue, this);
52790 * Sets the value of the item.
52791 * @param {String} either an object or a string..
52793 setValue : function(v){
52795 v = v || []; // empty set..
52796 // this does not seem smart - it really only affects memoryproxy grids..
52797 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52798 var ds = this.grid.getDataSource();
52799 // assumes a json reader..
52801 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
52802 ds.loadData( data);
52804 // clear selection so it does not get stale.
52805 if (this.grid.sm) {
52806 this.grid.sm.clearSelections();
52809 Roo.form.GridField.superclass.setValue.call(this, v);
52810 this.refreshValue();
52811 // should load data in the grid really....
52815 refreshValue: function() {
52817 this.grid.getDataSource().each(function(r) {
52820 this.el.dom.value = Roo.encode(val);
52828 * Ext JS Library 1.1.1
52829 * Copyright(c) 2006-2007, Ext JS, LLC.
52831 * Originally Released Under LGPL - original licence link has changed is not relivant.
52834 * <script type="text/javascript">
52837 * @class Roo.form.DisplayField
52838 * @extends Roo.form.Field
52839 * A generic Field to display non-editable data.
52840 * @cfg {Boolean} closable (true|false) default false
52842 * Creates a new Display Field item.
52843 * @param {Object} config Configuration options
52845 Roo.form.DisplayField = function(config){
52846 Roo.form.DisplayField.superclass.constructor.call(this, config);
52851 * Fires after the click the close btn
52852 * @param {Roo.form.DisplayField} this
52858 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
52859 inputType: 'hidden',
52865 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52867 focusClass : undefined,
52869 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52871 fieldClass: 'x-form-field',
52874 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52876 valueRenderer: undefined,
52880 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52881 * {tag: "input", type: "checkbox", autocomplete: "off"})
52884 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52888 onResize : function(){
52889 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52893 initEvents : function(){
52894 // Roo.form.Checkbox.superclass.initEvents.call(this);
52895 // has no events...
52898 this.closeEl.on('click', this.onClose, this);
52904 getResizeEl : function(){
52908 getPositionEl : function(){
52913 onRender : function(ct, position){
52915 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52916 //if(this.inputValue !== undefined){
52917 this.wrap = this.el.wrap();
52919 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52922 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52925 if (this.bodyStyle) {
52926 this.viewEl.applyStyles(this.bodyStyle);
52928 //this.viewEl.setStyle('padding', '2px');
52930 this.setValue(this.value);
52935 initValue : Roo.emptyFn,
52940 onClick : function(){
52945 * Sets the checked state of the checkbox.
52946 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52948 setValue : function(v){
52950 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
52951 // this might be called before we have a dom element..
52952 if (!this.viewEl) {
52955 this.viewEl.dom.innerHTML = html;
52956 Roo.form.DisplayField.superclass.setValue.call(this, v);
52960 onClose : function(e)
52962 e.preventDefault();
52964 this.fireEvent('close', this);
52973 * @class Roo.form.DayPicker
52974 * @extends Roo.form.Field
52975 * A Day picker show [M] [T] [W] ....
52977 * Creates a new Day Picker
52978 * @param {Object} config Configuration options
52980 Roo.form.DayPicker= function(config){
52981 Roo.form.DayPicker.superclass.constructor.call(this, config);
52985 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
52987 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52989 focusClass : undefined,
52991 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52993 fieldClass: "x-form-field",
52996 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52997 * {tag: "input", type: "checkbox", autocomplete: "off"})
52999 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
53002 actionMode : 'viewEl',
53006 inputType : 'hidden',
53009 inputElement: false, // real input element?
53010 basedOn: false, // ????
53012 isFormField: true, // not sure where this is needed!!!!
53014 onResize : function(){
53015 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
53016 if(!this.boxLabel){
53017 this.el.alignTo(this.wrap, 'c-c');
53021 initEvents : function(){
53022 Roo.form.Checkbox.superclass.initEvents.call(this);
53023 this.el.on("click", this.onClick, this);
53024 this.el.on("change", this.onClick, this);
53028 getResizeEl : function(){
53032 getPositionEl : function(){
53038 onRender : function(ct, position){
53039 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53041 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53043 var r1 = '<table><tr>';
53044 var r2 = '<tr class="x-form-daypick-icons">';
53045 for (var i=0; i < 7; i++) {
53046 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53047 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
53050 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53051 viewEl.select('img').on('click', this.onClick, this);
53052 this.viewEl = viewEl;
53055 // this will not work on Chrome!!!
53056 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
53057 this.el.on('propertychange', this.setFromHidden, this); //ie
53065 initValue : Roo.emptyFn,
53068 * Returns the checked state of the checkbox.
53069 * @return {Boolean} True if checked, else false
53071 getValue : function(){
53072 return this.el.dom.value;
53077 onClick : function(e){
53078 //this.setChecked(!this.checked);
53079 Roo.get(e.target).toggleClass('x-menu-item-checked');
53080 this.refreshValue();
53081 //if(this.el.dom.checked != this.checked){
53082 // this.setValue(this.el.dom.checked);
53087 refreshValue : function()
53090 this.viewEl.select('img',true).each(function(e,i,n) {
53091 val += e.is(".x-menu-item-checked") ? String(n) : '';
53093 this.setValue(val, true);
53097 * Sets the checked state of the checkbox.
53098 * On is always based on a string comparison between inputValue and the param.
53099 * @param {Boolean/String} value - the value to set
53100 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53102 setValue : function(v,suppressEvent){
53103 if (!this.el.dom) {
53106 var old = this.el.dom.value ;
53107 this.el.dom.value = v;
53108 if (suppressEvent) {
53112 // update display..
53113 this.viewEl.select('img',true).each(function(e,i,n) {
53115 var on = e.is(".x-menu-item-checked");
53116 var newv = v.indexOf(String(n)) > -1;
53118 e.toggleClass('x-menu-item-checked');
53124 this.fireEvent('change', this, v, old);
53129 // handle setting of hidden value by some other method!!?!?
53130 setFromHidden: function()
53135 //console.log("SET FROM HIDDEN");
53136 //alert('setFrom hidden');
53137 this.setValue(this.el.dom.value);
53140 onDestroy : function()
53143 Roo.get(this.viewEl).remove();
53146 Roo.form.DayPicker.superclass.onDestroy.call(this);
53150 * RooJS Library 1.1.1
53151 * Copyright(c) 2008-2011 Alan Knowles
53158 * @class Roo.form.ComboCheck
53159 * @extends Roo.form.ComboBox
53160 * A combobox for multiple select items.
53162 * FIXME - could do with a reset button..
53165 * Create a new ComboCheck
53166 * @param {Object} config Configuration options
53168 Roo.form.ComboCheck = function(config){
53169 Roo.form.ComboCheck.superclass.constructor.call(this, config);
53170 // should verify some data...
53172 // hiddenName = required..
53173 // displayField = required
53174 // valudField == required
53175 var req= [ 'hiddenName', 'displayField', 'valueField' ];
53177 Roo.each(req, function(e) {
53178 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53179 throw "Roo.form.ComboCheck : missing value for: " + e;
53186 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53191 selectedClass: 'x-menu-item-checked',
53194 onRender : function(ct, position){
53200 var cls = 'x-combo-list';
53203 this.tpl = new Roo.Template({
53204 html : '<div class="'+cls+'-item x-menu-check-item">' +
53205 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
53206 '<span>{' + this.displayField + '}</span>' +
53213 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53214 this.view.singleSelect = false;
53215 this.view.multiSelect = true;
53216 this.view.toggleSelect = true;
53217 this.pageTb.add(new Roo.Toolbar.Fill(), {
53220 handler: function()
53227 onViewOver : function(e, t){
53233 onViewClick : function(doFocus,index){
53237 select: function () {
53238 //Roo.log("SELECT CALLED");
53241 selectByValue : function(xv, scrollIntoView){
53242 var ar = this.getValueArray();
53245 Roo.each(ar, function(v) {
53246 if(v === undefined || v === null){
53249 var r = this.findRecord(this.valueField, v);
53251 sels.push(this.store.indexOf(r))
53255 this.view.select(sels);
53261 onSelect : function(record, index){
53262 // Roo.log("onselect Called");
53263 // this is only called by the clear button now..
53264 this.view.clearSelections();
53265 this.setValue('[]');
53266 if (this.value != this.valueBefore) {
53267 this.fireEvent('change', this, this.value, this.valueBefore);
53268 this.valueBefore = this.value;
53271 getValueArray : function()
53276 //Roo.log(this.value);
53277 if (typeof(this.value) == 'undefined') {
53280 var ar = Roo.decode(this.value);
53281 return ar instanceof Array ? ar : []; //?? valid?
53284 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
53289 expand : function ()
53292 Roo.form.ComboCheck.superclass.expand.call(this);
53293 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53294 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53299 collapse : function(){
53300 Roo.form.ComboCheck.superclass.collapse.call(this);
53301 var sl = this.view.getSelectedIndexes();
53302 var st = this.store;
53306 Roo.each(sl, function(i) {
53308 nv.push(r.get(this.valueField));
53310 this.setValue(Roo.encode(nv));
53311 if (this.value != this.valueBefore) {
53313 this.fireEvent('change', this, this.value, this.valueBefore);
53314 this.valueBefore = this.value;
53319 setValue : function(v){
53323 var vals = this.getValueArray();
53325 Roo.each(vals, function(k) {
53326 var r = this.findRecord(this.valueField, k);
53328 tv.push(r.data[this.displayField]);
53329 }else if(this.valueNotFoundText !== undefined){
53330 tv.push( this.valueNotFoundText );
53335 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53336 this.hiddenField.value = v;
53342 * Ext JS Library 1.1.1
53343 * Copyright(c) 2006-2007, Ext JS, LLC.
53345 * Originally Released Under LGPL - original licence link has changed is not relivant.
53348 * <script type="text/javascript">
53352 * @class Roo.form.Signature
53353 * @extends Roo.form.Field
53357 * @param {Object} config Configuration options
53360 Roo.form.Signature = function(config){
53361 Roo.form.Signature.superclass.constructor.call(this, config);
53363 this.addEvents({// not in used??
53366 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53367 * @param {Roo.form.Signature} combo This combo box
53372 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53373 * @param {Roo.form.ComboBox} combo This combo box
53374 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53380 Roo.extend(Roo.form.Signature, Roo.form.Field, {
53382 * @cfg {Object} labels Label to use when rendering a form.
53386 * confirm : "Confirm"
53391 confirm : "Confirm"
53394 * @cfg {Number} width The signature panel width (defaults to 300)
53398 * @cfg {Number} height The signature panel height (defaults to 100)
53402 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53404 allowBlank : false,
53407 // {Object} signPanel The signature SVG panel element (defaults to {})
53409 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53410 isMouseDown : false,
53411 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53412 isConfirmed : false,
53413 // {String} signatureTmp SVG mapping string (defaults to empty string)
53417 defaultAutoCreate : { // modified by initCompnoent..
53423 onRender : function(ct, position){
53425 Roo.form.Signature.superclass.onRender.call(this, ct, position);
53427 this.wrap = this.el.wrap({
53428 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53431 this.createToolbar(this);
53432 this.signPanel = this.wrap.createChild({
53434 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53438 this.svgID = Roo.id();
53439 this.svgEl = this.signPanel.createChild({
53440 xmlns : 'http://www.w3.org/2000/svg',
53442 id : this.svgID + "-svg",
53444 height: this.height,
53445 viewBox: '0 0 '+this.width+' '+this.height,
53449 id: this.svgID + "-svg-r",
53451 height: this.height,
53456 id: this.svgID + "-svg-l",
53458 y1: (this.height*0.8), // start set the line in 80% of height
53459 x2: this.width, // end
53460 y2: (this.height*0.8), // end set the line in 80% of height
53462 'stroke-width': "1",
53463 'stroke-dasharray': "3",
53464 'shape-rendering': "crispEdges",
53465 'pointer-events': "none"
53469 id: this.svgID + "-svg-p",
53471 'stroke-width': "3",
53473 'pointer-events': 'none'
53478 this.svgBox = this.svgEl.dom.getScreenCTM();
53480 createSVG : function(){
53481 var svg = this.signPanel;
53482 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53485 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53486 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53487 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53488 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53489 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53490 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53491 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53494 isTouchEvent : function(e){
53495 return e.type.match(/^touch/);
53497 getCoords : function (e) {
53498 var pt = this.svgEl.dom.createSVGPoint();
53501 if (this.isTouchEvent(e)) {
53502 pt.x = e.targetTouches[0].clientX;
53503 pt.y = e.targetTouches[0].clientY;
53505 var a = this.svgEl.dom.getScreenCTM();
53506 var b = a.inverse();
53507 var mx = pt.matrixTransform(b);
53508 return mx.x + ',' + mx.y;
53510 //mouse event headler
53511 down : function (e) {
53512 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53513 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53515 this.isMouseDown = true;
53517 e.preventDefault();
53519 move : function (e) {
53520 if (this.isMouseDown) {
53521 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53522 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53525 e.preventDefault();
53527 up : function (e) {
53528 this.isMouseDown = false;
53529 var sp = this.signatureTmp.split(' ');
53532 if(!sp[sp.length-2].match(/^L/)){
53536 this.signatureTmp = sp.join(" ");
53539 if(this.getValue() != this.signatureTmp){
53540 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53541 this.isConfirmed = false;
53543 e.preventDefault();
53547 * Protected method that will not generally be called directly. It
53548 * is called when the editor creates its toolbar. Override this method if you need to
53549 * add custom toolbar buttons.
53550 * @param {HtmlEditor} editor
53552 createToolbar : function(editor){
53553 function btn(id, toggle, handler){
53554 var xid = fid + '-'+ id ;
53558 cls : 'x-btn-icon x-edit-'+id,
53559 enableToggle:toggle !== false,
53560 scope: editor, // was editor...
53561 handler:handler||editor.relayBtnCmd,
53562 clickEvent:'mousedown',
53563 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53569 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53573 cls : ' x-signature-btn x-signature-'+id,
53574 scope: editor, // was editor...
53575 handler: this.reset,
53576 clickEvent:'mousedown',
53577 text: this.labels.clear
53584 cls : ' x-signature-btn x-signature-'+id,
53585 scope: editor, // was editor...
53586 handler: this.confirmHandler,
53587 clickEvent:'mousedown',
53588 text: this.labels.confirm
53595 * when user is clicked confirm then show this image.....
53597 * @return {String} Image Data URI
53599 getImageDataURI : function(){
53600 var svg = this.svgEl.dom.parentNode.innerHTML;
53601 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53606 * @return {Boolean} this.isConfirmed
53608 getConfirmed : function(){
53609 return this.isConfirmed;
53613 * @return {Number} this.width
53615 getWidth : function(){
53620 * @return {Number} this.height
53622 getHeight : function(){
53623 return this.height;
53626 getSignature : function(){
53627 return this.signatureTmp;
53630 reset : function(){
53631 this.signatureTmp = '';
53632 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53633 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53634 this.isConfirmed = false;
53635 Roo.form.Signature.superclass.reset.call(this);
53637 setSignature : function(s){
53638 this.signatureTmp = s;
53639 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53640 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53642 this.isConfirmed = false;
53643 Roo.form.Signature.superclass.reset.call(this);
53646 // Roo.log(this.signPanel.dom.contentWindow.up())
53649 setConfirmed : function(){
53653 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53656 confirmHandler : function(){
53657 if(!this.getSignature()){
53661 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53662 this.setValue(this.getSignature());
53663 this.isConfirmed = true;
53665 this.fireEvent('confirm', this);
53668 // Subclasses should provide the validation implementation by overriding this
53669 validateValue : function(value){
53670 if(this.allowBlank){
53674 if(this.isConfirmed){
53681 * Ext JS Library 1.1.1
53682 * Copyright(c) 2006-2007, Ext JS, LLC.
53684 * Originally Released Under LGPL - original licence link has changed is not relivant.
53687 * <script type="text/javascript">
53692 * @class Roo.form.ComboBox
53693 * @extends Roo.form.TriggerField
53694 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53696 * Create a new ComboBox.
53697 * @param {Object} config Configuration options
53699 Roo.form.Select = function(config){
53700 Roo.form.Select.superclass.constructor.call(this, config);
53704 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53706 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53709 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53710 * rendering into an Roo.Editor, defaults to false)
53713 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53714 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53717 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53720 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53721 * the dropdown list (defaults to undefined, with no header element)
53725 * @cfg {String/Roo.Template} tpl The template to use to render the output
53729 defaultAutoCreate : {tag: "select" },
53731 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53733 listWidth: undefined,
53735 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53736 * mode = 'remote' or 'text' if mode = 'local')
53738 displayField: undefined,
53740 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53741 * mode = 'remote' or 'value' if mode = 'local').
53742 * Note: use of a valueField requires the user make a selection
53743 * in order for a value to be mapped.
53745 valueField: undefined,
53749 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53750 * field's data value (defaults to the underlying DOM element's name)
53752 hiddenName: undefined,
53754 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53758 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53760 selectedClass: 'x-combo-selected',
53762 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
53763 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53764 * which displays a downward arrow icon).
53766 triggerClass : 'x-form-arrow-trigger',
53768 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53772 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53773 * anchor positions (defaults to 'tl-bl')
53775 listAlign: 'tl-bl?',
53777 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53781 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
53782 * query specified by the allQuery config option (defaults to 'query')
53784 triggerAction: 'query',
53786 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53787 * (defaults to 4, does not apply if editable = false)
53791 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53792 * delay (typeAheadDelay) if it matches a known value (defaults to false)
53796 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53797 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53801 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53802 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
53806 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
53807 * when editable = true (defaults to false)
53809 selectOnFocus:false,
53811 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53813 queryParam: 'query',
53815 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
53816 * when mode = 'remote' (defaults to 'Loading...')
53818 loadingText: 'Loading...',
53820 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53824 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53828 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53829 * traditional select (defaults to true)
53833 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53837 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53841 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53842 * listWidth has a higher value)
53846 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53847 * allow the user to set arbitrary text into the field (defaults to false)
53849 forceSelection:false,
53851 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53852 * if typeAhead = true (defaults to 250)
53854 typeAheadDelay : 250,
53856 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53857 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53859 valueNotFoundText : undefined,
53862 * @cfg {String} defaultValue The value displayed after loading the store.
53867 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53869 blockFocus : false,
53872 * @cfg {Boolean} disableClear Disable showing of clear button.
53874 disableClear : false,
53876 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
53878 alwaysQuery : false,
53884 // element that contains real text value.. (when hidden is used..)
53887 onRender : function(ct, position){
53888 Roo.form.Field.prototype.onRender.call(this, ct, position);
53891 this.store.on('beforeload', this.onBeforeLoad, this);
53892 this.store.on('load', this.onLoad, this);
53893 this.store.on('loadexception', this.onLoadException, this);
53894 this.store.load({});
53902 initEvents : function(){
53903 //Roo.form.ComboBox.superclass.initEvents.call(this);
53907 onDestroy : function(){
53910 this.store.un('beforeload', this.onBeforeLoad, this);
53911 this.store.un('load', this.onLoad, this);
53912 this.store.un('loadexception', this.onLoadException, this);
53914 //Roo.form.ComboBox.superclass.onDestroy.call(this);
53918 fireKey : function(e){
53919 if(e.isNavKeyPress() && !this.list.isVisible()){
53920 this.fireEvent("specialkey", this, e);
53925 onResize: function(w, h){
53933 * Allow or prevent the user from directly editing the field text. If false is passed,
53934 * the user will only be able to select from the items defined in the dropdown list. This method
53935 * is the runtime equivalent of setting the 'editable' config option at config time.
53936 * @param {Boolean} value True to allow the user to directly edit the field text
53938 setEditable : function(value){
53943 onBeforeLoad : function(){
53945 Roo.log("Select before load");
53948 this.innerList.update(this.loadingText ?
53949 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53950 //this.restrictHeight();
53951 this.selectedIndex = -1;
53955 onLoad : function(){
53958 var dom = this.el.dom;
53959 dom.innerHTML = '';
53960 var od = dom.ownerDocument;
53962 if (this.emptyText) {
53963 var op = od.createElement('option');
53964 op.setAttribute('value', '');
53965 op.innerHTML = String.format('{0}', this.emptyText);
53966 dom.appendChild(op);
53968 if(this.store.getCount() > 0){
53970 var vf = this.valueField;
53971 var df = this.displayField;
53972 this.store.data.each(function(r) {
53973 // which colmsn to use... testing - cdoe / title..
53974 var op = od.createElement('option');
53975 op.setAttribute('value', r.data[vf]);
53976 op.innerHTML = String.format('{0}', r.data[df]);
53977 dom.appendChild(op);
53979 if (typeof(this.defaultValue != 'undefined')) {
53980 this.setValue(this.defaultValue);
53985 //this.onEmptyResults();
53990 onLoadException : function()
53992 dom.innerHTML = '';
53994 Roo.log("Select on load exception");
53998 Roo.log(this.store.reader.jsonData);
53999 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54000 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54006 onTypeAhead : function(){
54011 onSelect : function(record, index){
54012 Roo.log('on select?');
54014 if(this.fireEvent('beforeselect', this, record, index) !== false){
54015 this.setFromData(index > -1 ? record.data : false);
54017 this.fireEvent('select', this, record, index);
54022 * Returns the currently selected field value or empty string if no value is set.
54023 * @return {String} value The selected value
54025 getValue : function(){
54026 var dom = this.el.dom;
54027 this.value = dom.options[dom.selectedIndex].value;
54033 * Clears any text/value currently set in the field
54035 clearValue : function(){
54037 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54042 * Sets the specified value into the field. If the value finds a match, the corresponding record text
54043 * will be displayed in the field. If the value does not match the data value of an existing item,
54044 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54045 * Otherwise the field will be blank (although the value will still be set).
54046 * @param {String} value The value to match
54048 setValue : function(v){
54049 var d = this.el.dom;
54050 for (var i =0; i < d.options.length;i++) {
54051 if (v == d.options[i].value) {
54052 d.selectedIndex = i;
54060 * @property {Object} the last set data for the element
54065 * Sets the value of the field based on a object which is related to the record format for the store.
54066 * @param {Object} value the value to set as. or false on reset?
54068 setFromData : function(o){
54069 Roo.log('setfrom data?');
54075 reset : function(){
54079 findRecord : function(prop, value){
54084 if(this.store.getCount() > 0){
54085 this.store.each(function(r){
54086 if(r.data[prop] == value){
54096 getName: function()
54098 // returns hidden if it's set..
54099 if (!this.rendered) {return ''};
54100 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
54108 onEmptyResults : function(){
54109 Roo.log('empty results');
54114 * Returns true if the dropdown list is expanded, else false.
54116 isExpanded : function(){
54121 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54122 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54123 * @param {String} value The data value of the item to select
54124 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54125 * selected item if it is not currently in view (defaults to true)
54126 * @return {Boolean} True if the value matched an item in the list, else false
54128 selectByValue : function(v, scrollIntoView){
54129 Roo.log('select By Value');
54132 if(v !== undefined && v !== null){
54133 var r = this.findRecord(this.valueField || this.displayField, v);
54135 this.select(this.store.indexOf(r), scrollIntoView);
54143 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54144 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54145 * @param {Number} index The zero-based index of the list item to select
54146 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54147 * selected item if it is not currently in view (defaults to true)
54149 select : function(index, scrollIntoView){
54150 Roo.log('select ');
54153 this.selectedIndex = index;
54154 this.view.select(index);
54155 if(scrollIntoView !== false){
54156 var el = this.view.getNode(index);
54158 this.innerList.scrollChildIntoView(el, false);
54166 validateBlur : function(){
54173 initQuery : function(){
54174 this.doQuery(this.getRawValue());
54178 doForce : function(){
54179 if(this.el.dom.value.length > 0){
54180 this.el.dom.value =
54181 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54187 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
54188 * query allowing the query action to be canceled if needed.
54189 * @param {String} query The SQL query to execute
54190 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54191 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
54192 * saved in the current store (defaults to false)
54194 doQuery : function(q, forceAll){
54196 Roo.log('doQuery?');
54197 if(q === undefined || q === null){
54202 forceAll: forceAll,
54206 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54210 forceAll = qe.forceAll;
54211 if(forceAll === true || (q.length >= this.minChars)){
54212 if(this.lastQuery != q || this.alwaysQuery){
54213 this.lastQuery = q;
54214 if(this.mode == 'local'){
54215 this.selectedIndex = -1;
54217 this.store.clearFilter();
54219 this.store.filter(this.displayField, q);
54223 this.store.baseParams[this.queryParam] = q;
54225 params: this.getParams(q)
54230 this.selectedIndex = -1;
54237 getParams : function(q){
54239 //p[this.queryParam] = q;
54242 p.limit = this.pageSize;
54248 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54250 collapse : function(){
54255 collapseIf : function(e){
54260 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54262 expand : function(){
54270 * @cfg {Boolean} grow
54274 * @cfg {Number} growMin
54278 * @cfg {Number} growMax
54286 setWidth : function()
54290 getResizeEl : function(){
54293 });//<script type="text/javasscript">
54297 * @class Roo.DDView
54298 * A DnD enabled version of Roo.View.
54299 * @param {Element/String} container The Element in which to create the View.
54300 * @param {String} tpl The template string used to create the markup for each element of the View
54301 * @param {Object} config The configuration properties. These include all the config options of
54302 * {@link Roo.View} plus some specific to this class.<br>
54304 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54305 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54307 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54308 .x-view-drag-insert-above {
54309 border-top:1px dotted #3366cc;
54311 .x-view-drag-insert-below {
54312 border-bottom:1px dotted #3366cc;
54318 Roo.DDView = function(container, tpl, config) {
54319 Roo.DDView.superclass.constructor.apply(this, arguments);
54320 this.getEl().setStyle("outline", "0px none");
54321 this.getEl().unselectable();
54322 if (this.dragGroup) {
54323 this.setDraggable(this.dragGroup.split(","));
54325 if (this.dropGroup) {
54326 this.setDroppable(this.dropGroup.split(","));
54328 if (this.deletable) {
54329 this.setDeletable();
54331 this.isDirtyFlag = false;
54337 Roo.extend(Roo.DDView, Roo.View, {
54338 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54339 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54340 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54341 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54345 reset: Roo.emptyFn,
54347 clearInvalid: Roo.form.Field.prototype.clearInvalid,
54349 validate: function() {
54353 destroy: function() {
54354 this.purgeListeners();
54355 this.getEl.removeAllListeners();
54356 this.getEl().remove();
54357 if (this.dragZone) {
54358 if (this.dragZone.destroy) {
54359 this.dragZone.destroy();
54362 if (this.dropZone) {
54363 if (this.dropZone.destroy) {
54364 this.dropZone.destroy();
54369 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54370 getName: function() {
54374 /** Loads the View from a JSON string representing the Records to put into the Store. */
54375 setValue: function(v) {
54377 throw "DDView.setValue(). DDView must be constructed with a valid Store";
54380 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54381 this.store.proxy = new Roo.data.MemoryProxy(data);
54385 /** @return {String} a parenthesised list of the ids of the Records in the View. */
54386 getValue: function() {
54388 this.store.each(function(rec) {
54389 result += rec.id + ',';
54391 return result.substr(0, result.length - 1) + ')';
54394 getIds: function() {
54395 var i = 0, result = new Array(this.store.getCount());
54396 this.store.each(function(rec) {
54397 result[i++] = rec.id;
54402 isDirty: function() {
54403 return this.isDirtyFlag;
54407 * Part of the Roo.dd.DropZone interface. If no target node is found, the
54408 * whole Element becomes the target, and this causes the drop gesture to append.
54410 getTargetFromEvent : function(e) {
54411 var target = e.getTarget();
54412 while ((target !== null) && (target.parentNode != this.el.dom)) {
54413 target = target.parentNode;
54416 target = this.el.dom.lastChild || this.el.dom;
54422 * Create the drag data which consists of an object which has the property "ddel" as
54423 * the drag proxy element.
54425 getDragData : function(e) {
54426 var target = this.findItemFromChild(e.getTarget());
54428 this.handleSelection(e);
54429 var selNodes = this.getSelectedNodes();
54432 copy: this.copy || (this.allowCopy && e.ctrlKey),
54436 var selectedIndices = this.getSelectedIndexes();
54437 for (var i = 0; i < selectedIndices.length; i++) {
54438 dragData.records.push(this.store.getAt(selectedIndices[i]));
54440 if (selNodes.length == 1) {
54441 dragData.ddel = target.cloneNode(true); // the div element
54443 var div = document.createElement('div'); // create the multi element drag "ghost"
54444 div.className = 'multi-proxy';
54445 for (var i = 0, len = selNodes.length; i < len; i++) {
54446 div.appendChild(selNodes[i].cloneNode(true));
54448 dragData.ddel = div;
54450 //console.log(dragData)
54451 //console.log(dragData.ddel.innerHTML)
54454 //console.log('nodragData')
54458 /** Specify to which ddGroup items in this DDView may be dragged. */
54459 setDraggable: function(ddGroup) {
54460 if (ddGroup instanceof Array) {
54461 Roo.each(ddGroup, this.setDraggable, this);
54464 if (this.dragZone) {
54465 this.dragZone.addToGroup(ddGroup);
54467 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54468 containerScroll: true,
54472 // Draggability implies selection. DragZone's mousedown selects the element.
54473 if (!this.multiSelect) { this.singleSelect = true; }
54475 // Wire the DragZone's handlers up to methods in *this*
54476 this.dragZone.getDragData = this.getDragData.createDelegate(this);
54480 /** Specify from which ddGroup this DDView accepts drops. */
54481 setDroppable: function(ddGroup) {
54482 if (ddGroup instanceof Array) {
54483 Roo.each(ddGroup, this.setDroppable, this);
54486 if (this.dropZone) {
54487 this.dropZone.addToGroup(ddGroup);
54489 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54490 containerScroll: true,
54494 // Wire the DropZone's handlers up to methods in *this*
54495 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54496 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54497 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54498 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54499 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54503 /** Decide whether to drop above or below a View node. */
54504 getDropPoint : function(e, n, dd){
54505 if (n == this.el.dom) { return "above"; }
54506 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54507 var c = t + (b - t) / 2;
54508 var y = Roo.lib.Event.getPageY(e);
54516 onNodeEnter : function(n, dd, e, data){
54520 onNodeOver : function(n, dd, e, data){
54521 var pt = this.getDropPoint(e, n, dd);
54522 // set the insert point style on the target node
54523 var dragElClass = this.dropNotAllowed;
54526 if (pt == "above"){
54527 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54528 targetElClass = "x-view-drag-insert-above";
54530 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54531 targetElClass = "x-view-drag-insert-below";
54533 if (this.lastInsertClass != targetElClass){
54534 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54535 this.lastInsertClass = targetElClass;
54538 return dragElClass;
54541 onNodeOut : function(n, dd, e, data){
54542 this.removeDropIndicators(n);
54545 onNodeDrop : function(n, dd, e, data){
54546 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54549 var pt = this.getDropPoint(e, n, dd);
54550 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54551 if (pt == "below") { insertAt++; }
54552 for (var i = 0; i < data.records.length; i++) {
54553 var r = data.records[i];
54554 var dup = this.store.getById(r.id);
54555 if (dup && (dd != this.dragZone)) {
54556 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54559 this.store.insert(insertAt++, r.copy());
54561 data.source.isDirtyFlag = true;
54563 this.store.insert(insertAt++, r);
54565 this.isDirtyFlag = true;
54568 this.dragZone.cachedTarget = null;
54572 removeDropIndicators : function(n){
54574 Roo.fly(n).removeClass([
54575 "x-view-drag-insert-above",
54576 "x-view-drag-insert-below"]);
54577 this.lastInsertClass = "_noclass";
54582 * Utility method. Add a delete option to the DDView's context menu.
54583 * @param {String} imageUrl The URL of the "delete" icon image.
54585 setDeletable: function(imageUrl) {
54586 if (!this.singleSelect && !this.multiSelect) {
54587 this.singleSelect = true;
54589 var c = this.getContextMenu();
54590 this.contextMenu.on("itemclick", function(item) {
54593 this.remove(this.getSelectedIndexes());
54597 this.contextMenu.add({
54604 /** Return the context menu for this DDView. */
54605 getContextMenu: function() {
54606 if (!this.contextMenu) {
54607 // Create the View's context menu
54608 this.contextMenu = new Roo.menu.Menu({
54609 id: this.id + "-contextmenu"
54611 this.el.on("contextmenu", this.showContextMenu, this);
54613 return this.contextMenu;
54616 disableContextMenu: function() {
54617 if (this.contextMenu) {
54618 this.el.un("contextmenu", this.showContextMenu, this);
54622 showContextMenu: function(e, item) {
54623 item = this.findItemFromChild(e.getTarget());
54626 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54627 this.contextMenu.showAt(e.getXY());
54632 * Remove {@link Roo.data.Record}s at the specified indices.
54633 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54635 remove: function(selectedIndices) {
54636 selectedIndices = [].concat(selectedIndices);
54637 for (var i = 0; i < selectedIndices.length; i++) {
54638 var rec = this.store.getAt(selectedIndices[i]);
54639 this.store.remove(rec);
54644 * Double click fires the event, but also, if this is draggable, and there is only one other
54645 * related DropZone, it transfers the selected node.
54647 onDblClick : function(e){
54648 var item = this.findItemFromChild(e.getTarget());
54650 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54653 if (this.dragGroup) {
54654 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54655 while (targets.indexOf(this.dropZone) > -1) {
54656 targets.remove(this.dropZone);
54658 if (targets.length == 1) {
54659 this.dragZone.cachedTarget = null;
54660 var el = Roo.get(targets[0].getEl());
54661 var box = el.getBox(true);
54662 targets[0].onNodeDrop(el.dom, {
54664 xy: [box.x, box.y + box.height - 1]
54665 }, null, this.getDragData(e));
54671 handleSelection: function(e) {
54672 this.dragZone.cachedTarget = null;
54673 var item = this.findItemFromChild(e.getTarget());
54675 this.clearSelections(true);
54678 if (item && (this.multiSelect || this.singleSelect)){
54679 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54680 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54681 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54682 this.unselect(item);
54684 this.select(item, this.multiSelect && e.ctrlKey);
54685 this.lastSelection = item;
54690 onItemClick : function(item, index, e){
54691 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54697 unselect : function(nodeInfo, suppressEvent){
54698 var node = this.getNode(nodeInfo);
54699 if(node && this.isSelected(node)){
54700 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54701 Roo.fly(node).removeClass(this.selectedClass);
54702 this.selections.remove(node);
54703 if(!suppressEvent){
54704 this.fireEvent("selectionchange", this, this.selections);
54712 * Ext JS Library 1.1.1
54713 * Copyright(c) 2006-2007, Ext JS, LLC.
54715 * Originally Released Under LGPL - original licence link has changed is not relivant.
54718 * <script type="text/javascript">
54722 * @class Roo.LayoutManager
54723 * @extends Roo.util.Observable
54724 * Base class for layout managers.
54726 Roo.LayoutManager = function(container, config){
54727 Roo.LayoutManager.superclass.constructor.call(this);
54728 this.el = Roo.get(container);
54729 // ie scrollbar fix
54730 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54731 document.body.scroll = "no";
54732 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54733 this.el.position('relative');
54735 this.id = this.el.id;
54736 this.el.addClass("x-layout-container");
54737 /** false to disable window resize monitoring @type Boolean */
54738 this.monitorWindowResize = true;
54743 * Fires when a layout is performed.
54744 * @param {Roo.LayoutManager} this
54748 * @event regionresized
54749 * Fires when the user resizes a region.
54750 * @param {Roo.LayoutRegion} region The resized region
54751 * @param {Number} newSize The new size (width for east/west, height for north/south)
54753 "regionresized" : true,
54755 * @event regioncollapsed
54756 * Fires when a region is collapsed.
54757 * @param {Roo.LayoutRegion} region The collapsed region
54759 "regioncollapsed" : true,
54761 * @event regionexpanded
54762 * Fires when a region is expanded.
54763 * @param {Roo.LayoutRegion} region The expanded region
54765 "regionexpanded" : true
54767 this.updating = false;
54768 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54771 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54773 * Returns true if this layout is currently being updated
54774 * @return {Boolean}
54776 isUpdating : function(){
54777 return this.updating;
54781 * Suspend the LayoutManager from doing auto-layouts while
54782 * making multiple add or remove calls
54784 beginUpdate : function(){
54785 this.updating = true;
54789 * Restore auto-layouts and optionally disable the manager from performing a layout
54790 * @param {Boolean} noLayout true to disable a layout update
54792 endUpdate : function(noLayout){
54793 this.updating = false;
54799 layout: function(){
54803 onRegionResized : function(region, newSize){
54804 this.fireEvent("regionresized", region, newSize);
54808 onRegionCollapsed : function(region){
54809 this.fireEvent("regioncollapsed", region);
54812 onRegionExpanded : function(region){
54813 this.fireEvent("regionexpanded", region);
54817 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54818 * performs box-model adjustments.
54819 * @return {Object} The size as an object {width: (the width), height: (the height)}
54821 getViewSize : function(){
54823 if(this.el.dom != document.body){
54824 size = this.el.getSize();
54826 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54828 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54829 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54834 * Returns the Element this layout is bound to.
54835 * @return {Roo.Element}
54837 getEl : function(){
54842 * Returns the specified region.
54843 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54844 * @return {Roo.LayoutRegion}
54846 getRegion : function(target){
54847 return this.regions[target.toLowerCase()];
54850 onWindowResize : function(){
54851 if(this.monitorWindowResize){
54857 * Ext JS Library 1.1.1
54858 * Copyright(c) 2006-2007, Ext JS, LLC.
54860 * Originally Released Under LGPL - original licence link has changed is not relivant.
54863 * <script type="text/javascript">
54866 * @class Roo.BorderLayout
54867 * @extends Roo.LayoutManager
54868 * @children Roo.ContentPanel
54869 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54870 * please see: <br><br>
54871 * <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>
54872 * <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>
54875 var layout = new Roo.BorderLayout(document.body, {
54909 preferredTabWidth: 150
54914 var CP = Roo.ContentPanel;
54916 layout.beginUpdate();
54917 layout.add("north", new CP("north", "North"));
54918 layout.add("south", new CP("south", {title: "South", closable: true}));
54919 layout.add("west", new CP("west", {title: "West"}));
54920 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54921 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54922 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54923 layout.getRegion("center").showPanel("center1");
54924 layout.endUpdate();
54927 <b>The container the layout is rendered into can be either the body element or any other element.
54928 If it is not the body element, the container needs to either be an absolute positioned element,
54929 or you will need to add "position:relative" to the css of the container. You will also need to specify
54930 the container size if it is not the body element.</b>
54933 * Create a new BorderLayout
54934 * @param {String/HTMLElement/Element} container The container this layout is bound to
54935 * @param {Object} config Configuration options
54937 Roo.BorderLayout = function(container, config){
54938 config = config || {};
54939 Roo.BorderLayout.superclass.constructor.call(this, container, config);
54940 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54941 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54942 var target = this.factory.validRegions[i];
54943 if(config[target]){
54944 this.addRegion(target, config[target]);
54949 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54952 * @cfg {Roo.LayoutRegion} east
54955 * @cfg {Roo.LayoutRegion} west
54958 * @cfg {Roo.LayoutRegion} north
54961 * @cfg {Roo.LayoutRegion} south
54964 * @cfg {Roo.LayoutRegion} center
54967 * Creates and adds a new region if it doesn't already exist.
54968 * @param {String} target The target region key (north, south, east, west or center).
54969 * @param {Object} config The regions config object
54970 * @return {BorderLayoutRegion} The new region
54972 addRegion : function(target, config){
54973 if(!this.regions[target]){
54974 var r = this.factory.create(target, this, config);
54975 this.bindRegion(target, r);
54977 return this.regions[target];
54981 bindRegion : function(name, r){
54982 this.regions[name] = r;
54983 r.on("visibilitychange", this.layout, this);
54984 r.on("paneladded", this.layout, this);
54985 r.on("panelremoved", this.layout, this);
54986 r.on("invalidated", this.layout, this);
54987 r.on("resized", this.onRegionResized, this);
54988 r.on("collapsed", this.onRegionCollapsed, this);
54989 r.on("expanded", this.onRegionExpanded, this);
54993 * Performs a layout update.
54995 layout : function(){
54996 if(this.updating) {
54999 var size = this.getViewSize();
55000 var w = size.width;
55001 var h = size.height;
55006 //var x = 0, y = 0;
55008 var rs = this.regions;
55009 var north = rs["north"];
55010 var south = rs["south"];
55011 var west = rs["west"];
55012 var east = rs["east"];
55013 var center = rs["center"];
55014 //if(this.hideOnLayout){ // not supported anymore
55015 //c.el.setStyle("display", "none");
55017 if(north && north.isVisible()){
55018 var b = north.getBox();
55019 var m = north.getMargins();
55020 b.width = w - (m.left+m.right);
55023 centerY = b.height + b.y + m.bottom;
55024 centerH -= centerY;
55025 north.updateBox(this.safeBox(b));
55027 if(south && south.isVisible()){
55028 var b = south.getBox();
55029 var m = south.getMargins();
55030 b.width = w - (m.left+m.right);
55032 var totalHeight = (b.height + m.top + m.bottom);
55033 b.y = h - totalHeight + m.top;
55034 centerH -= totalHeight;
55035 south.updateBox(this.safeBox(b));
55037 if(west && west.isVisible()){
55038 var b = west.getBox();
55039 var m = west.getMargins();
55040 b.height = centerH - (m.top+m.bottom);
55042 b.y = centerY + m.top;
55043 var totalWidth = (b.width + m.left + m.right);
55044 centerX += totalWidth;
55045 centerW -= totalWidth;
55046 west.updateBox(this.safeBox(b));
55048 if(east && east.isVisible()){
55049 var b = east.getBox();
55050 var m = east.getMargins();
55051 b.height = centerH - (m.top+m.bottom);
55052 var totalWidth = (b.width + m.left + m.right);
55053 b.x = w - totalWidth + m.left;
55054 b.y = centerY + m.top;
55055 centerW -= totalWidth;
55056 east.updateBox(this.safeBox(b));
55059 var m = center.getMargins();
55061 x: centerX + m.left,
55062 y: centerY + m.top,
55063 width: centerW - (m.left+m.right),
55064 height: centerH - (m.top+m.bottom)
55066 //if(this.hideOnLayout){
55067 //center.el.setStyle("display", "block");
55069 center.updateBox(this.safeBox(centerBox));
55072 this.fireEvent("layout", this);
55076 safeBox : function(box){
55077 box.width = Math.max(0, box.width);
55078 box.height = Math.max(0, box.height);
55083 * Adds a ContentPanel (or subclass) to this layout.
55084 * @param {String} target The target region key (north, south, east, west or center).
55085 * @param {Roo.ContentPanel} panel The panel to add
55086 * @return {Roo.ContentPanel} The added panel
55088 add : function(target, panel){
55090 target = target.toLowerCase();
55091 return this.regions[target].add(panel);
55095 * Remove a ContentPanel (or subclass) to this layout.
55096 * @param {String} target The target region key (north, south, east, west or center).
55097 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55098 * @return {Roo.ContentPanel} The removed panel
55100 remove : function(target, panel){
55101 target = target.toLowerCase();
55102 return this.regions[target].remove(panel);
55106 * Searches all regions for a panel with the specified id
55107 * @param {String} panelId
55108 * @return {Roo.ContentPanel} The panel or null if it wasn't found
55110 findPanel : function(panelId){
55111 var rs = this.regions;
55112 for(var target in rs){
55113 if(typeof rs[target] != "function"){
55114 var p = rs[target].getPanel(panelId);
55124 * Searches all regions for a panel with the specified id and activates (shows) it.
55125 * @param {String/ContentPanel} panelId The panels id or the panel itself
55126 * @return {Roo.ContentPanel} The shown panel or null
55128 showPanel : function(panelId) {
55129 var rs = this.regions;
55130 for(var target in rs){
55131 var r = rs[target];
55132 if(typeof r != "function"){
55133 if(r.hasPanel(panelId)){
55134 return r.showPanel(panelId);
55142 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55143 * @param {Roo.state.Provider} provider (optional) An alternate state provider
55145 restoreState : function(provider){
55147 provider = Roo.state.Manager;
55149 var sm = new Roo.LayoutStateManager();
55150 sm.init(this, provider);
55154 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
55155 * object should contain properties for each region to add ContentPanels to, and each property's value should be
55156 * a valid ContentPanel config object. Example:
55158 // Create the main layout
55159 var layout = new Roo.BorderLayout('main-ct', {
55170 // Create and add multiple ContentPanels at once via configs
55173 id: 'source-files',
55175 title:'Ext Source Files',
55188 * @param {Object} regions An object containing ContentPanel configs by region name
55190 batchAdd : function(regions){
55191 this.beginUpdate();
55192 for(var rname in regions){
55193 var lr = this.regions[rname];
55195 this.addTypedPanels(lr, regions[rname]);
55202 addTypedPanels : function(lr, ps){
55203 if(typeof ps == 'string'){
55204 lr.add(new Roo.ContentPanel(ps));
55206 else if(ps instanceof Array){
55207 for(var i =0, len = ps.length; i < len; i++){
55208 this.addTypedPanels(lr, ps[i]);
55211 else if(!ps.events){ // raw config?
55213 delete ps.el; // prevent conflict
55214 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55216 else { // panel object assumed!
55221 * Adds a xtype elements to the layout.
55225 xtype : 'ContentPanel',
55232 xtype : 'NestedLayoutPanel',
55238 items : [ ... list of content panels or nested layout panels.. ]
55242 * @param {Object} cfg Xtype definition of item to add.
55244 addxtype : function(cfg)
55246 // basically accepts a pannel...
55247 // can accept a layout region..!?!?
55248 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55250 if (!cfg.xtype.match(/Panel$/)) {
55255 if (typeof(cfg.region) == 'undefined') {
55256 Roo.log("Failed to add Panel, region was not set");
55260 var region = cfg.region;
55266 xitems = cfg.items;
55273 case 'ContentPanel': // ContentPanel (el, cfg)
55274 case 'ScrollPanel': // ContentPanel (el, cfg)
55276 if(cfg.autoCreate) {
55277 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55279 var el = this.el.createChild();
55280 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55283 this.add(region, ret);
55287 case 'TreePanel': // our new panel!
55288 cfg.el = this.el.createChild();
55289 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55290 this.add(region, ret);
55293 case 'NestedLayoutPanel':
55294 // create a new Layout (which is a Border Layout...
55295 var el = this.el.createChild();
55296 var clayout = cfg.layout;
55298 clayout.items = clayout.items || [];
55299 // replace this exitems with the clayout ones..
55300 xitems = clayout.items;
55303 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55304 cfg.background = false;
55306 var layout = new Roo.BorderLayout(el, clayout);
55308 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55309 //console.log('adding nested layout panel ' + cfg.toSource());
55310 this.add(region, ret);
55311 nb = {}; /// find first...
55316 // needs grid and region
55318 //var el = this.getRegion(region).el.createChild();
55319 var el = this.el.createChild();
55320 // create the grid first...
55322 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55324 if (region == 'center' && this.active ) {
55325 cfg.background = false;
55327 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55329 this.add(region, ret);
55330 if (cfg.background) {
55331 ret.on('activate', function(gp) {
55332 if (!gp.grid.rendered) {
55347 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55349 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55350 this.add(region, ret);
55353 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55357 // GridPanel (grid, cfg)
55360 this.beginUpdate();
55364 Roo.each(xitems, function(i) {
55365 region = nb && i.region ? i.region : false;
55367 var add = ret.addxtype(i);
55370 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55371 if (!i.background) {
55372 abn[region] = nb[region] ;
55379 // make the last non-background panel active..
55380 //if (nb) { Roo.log(abn); }
55383 for(var r in abn) {
55384 region = this.getRegion(r);
55386 // tried using nb[r], but it does not work..
55388 region.showPanel(abn[r]);
55399 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55400 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
55401 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55402 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
55405 var CP = Roo.ContentPanel;
55407 var layout = Roo.BorderLayout.create({
55411 panels: [new CP("north", "North")]
55420 panels: [new CP("west", {title: "West"})]
55429 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55438 panels: [new CP("south", {title: "South", closable: true})]
55445 preferredTabWidth: 150,
55447 new CP("center1", {title: "Close Me", closable: true}),
55448 new CP("center2", {title: "Center Panel", closable: false})
55453 layout.getRegion("center").showPanel("center1");
55458 Roo.BorderLayout.create = function(config, targetEl){
55459 var layout = new Roo.BorderLayout(targetEl || document.body, config);
55460 layout.beginUpdate();
55461 var regions = Roo.BorderLayout.RegionFactory.validRegions;
55462 for(var j = 0, jlen = regions.length; j < jlen; j++){
55463 var lr = regions[j];
55464 if(layout.regions[lr] && config[lr].panels){
55465 var r = layout.regions[lr];
55466 var ps = config[lr].panels;
55467 layout.addTypedPanels(r, ps);
55470 layout.endUpdate();
55475 Roo.BorderLayout.RegionFactory = {
55477 validRegions : ["north","south","east","west","center"],
55480 create : function(target, mgr, config){
55481 target = target.toLowerCase();
55482 if(config.lightweight || config.basic){
55483 return new Roo.BasicLayoutRegion(mgr, config, target);
55487 return new Roo.NorthLayoutRegion(mgr, config);
55489 return new Roo.SouthLayoutRegion(mgr, config);
55491 return new Roo.EastLayoutRegion(mgr, config);
55493 return new Roo.WestLayoutRegion(mgr, config);
55495 return new Roo.CenterLayoutRegion(mgr, config);
55497 throw 'Layout region "'+target+'" not supported.';
55501 * Ext JS Library 1.1.1
55502 * Copyright(c) 2006-2007, Ext JS, LLC.
55504 * Originally Released Under LGPL - original licence link has changed is not relivant.
55507 * <script type="text/javascript">
55511 * @class Roo.BasicLayoutRegion
55512 * @extends Roo.util.Observable
55513 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55514 * and does not have a titlebar, tabs or any other features. All it does is size and position
55515 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55517 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55519 this.position = pos;
55522 * @scope Roo.BasicLayoutRegion
55526 * @event beforeremove
55527 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55528 * @param {Roo.LayoutRegion} this
55529 * @param {Roo.ContentPanel} panel The panel
55530 * @param {Object} e The cancel event object
55532 "beforeremove" : true,
55534 * @event invalidated
55535 * Fires when the layout for this region is changed.
55536 * @param {Roo.LayoutRegion} this
55538 "invalidated" : true,
55540 * @event visibilitychange
55541 * Fires when this region is shown or hidden
55542 * @param {Roo.LayoutRegion} this
55543 * @param {Boolean} visibility true or false
55545 "visibilitychange" : true,
55547 * @event paneladded
55548 * Fires when a panel is added.
55549 * @param {Roo.LayoutRegion} this
55550 * @param {Roo.ContentPanel} panel The panel
55552 "paneladded" : true,
55554 * @event panelremoved
55555 * Fires when a panel is removed.
55556 * @param {Roo.LayoutRegion} this
55557 * @param {Roo.ContentPanel} panel The panel
55559 "panelremoved" : true,
55561 * @event beforecollapse
55562 * Fires when this region before collapse.
55563 * @param {Roo.LayoutRegion} this
55565 "beforecollapse" : true,
55568 * Fires when this region is collapsed.
55569 * @param {Roo.LayoutRegion} this
55571 "collapsed" : true,
55574 * Fires when this region is expanded.
55575 * @param {Roo.LayoutRegion} this
55580 * Fires when this region is slid into view.
55581 * @param {Roo.LayoutRegion} this
55583 "slideshow" : true,
55586 * Fires when this region slides out of view.
55587 * @param {Roo.LayoutRegion} this
55589 "slidehide" : true,
55591 * @event panelactivated
55592 * Fires when a panel is activated.
55593 * @param {Roo.LayoutRegion} this
55594 * @param {Roo.ContentPanel} panel The activated panel
55596 "panelactivated" : true,
55599 * Fires when the user resizes this region.
55600 * @param {Roo.LayoutRegion} this
55601 * @param {Number} newSize The new size (width for east/west, height for north/south)
55605 /** A collection of panels in this region. @type Roo.util.MixedCollection */
55606 this.panels = new Roo.util.MixedCollection();
55607 this.panels.getKey = this.getPanelId.createDelegate(this);
55609 this.activePanel = null;
55610 // ensure listeners are added...
55612 if (config.listeners || config.events) {
55613 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55614 listeners : config.listeners || {},
55615 events : config.events || {}
55619 if(skipConfig !== true){
55620 this.applyConfig(config);
55624 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55625 getPanelId : function(p){
55629 applyConfig : function(config){
55630 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55631 this.config = config;
55636 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
55637 * the width, for horizontal (north, south) the height.
55638 * @param {Number} newSize The new width or height
55640 resizeTo : function(newSize){
55641 var el = this.el ? this.el :
55642 (this.activePanel ? this.activePanel.getEl() : null);
55644 switch(this.position){
55647 el.setWidth(newSize);
55648 this.fireEvent("resized", this, newSize);
55652 el.setHeight(newSize);
55653 this.fireEvent("resized", this, newSize);
55659 getBox : function(){
55660 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55663 getMargins : function(){
55664 return this.margins;
55667 updateBox : function(box){
55669 var el = this.activePanel.getEl();
55670 el.dom.style.left = box.x + "px";
55671 el.dom.style.top = box.y + "px";
55672 this.activePanel.setSize(box.width, box.height);
55676 * Returns the container element for this region.
55677 * @return {Roo.Element}
55679 getEl : function(){
55680 return this.activePanel;
55684 * Returns true if this region is currently visible.
55685 * @return {Boolean}
55687 isVisible : function(){
55688 return this.activePanel ? true : false;
55691 setActivePanel : function(panel){
55692 panel = this.getPanel(panel);
55693 if(this.activePanel && this.activePanel != panel){
55694 this.activePanel.setActiveState(false);
55695 this.activePanel.getEl().setLeftTop(-10000,-10000);
55697 this.activePanel = panel;
55698 panel.setActiveState(true);
55700 panel.setSize(this.box.width, this.box.height);
55702 this.fireEvent("panelactivated", this, panel);
55703 this.fireEvent("invalidated");
55707 * Show the specified panel.
55708 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55709 * @return {Roo.ContentPanel} The shown panel or null
55711 showPanel : function(panel){
55712 if(panel = this.getPanel(panel)){
55713 this.setActivePanel(panel);
55719 * Get the active panel for this region.
55720 * @return {Roo.ContentPanel} The active panel or null
55722 getActivePanel : function(){
55723 return this.activePanel;
55727 * Add the passed ContentPanel(s)
55728 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55729 * @return {Roo.ContentPanel} The panel added (if only one was added)
55731 add : function(panel){
55732 if(arguments.length > 1){
55733 for(var i = 0, len = arguments.length; i < len; i++) {
55734 this.add(arguments[i]);
55738 if(this.hasPanel(panel)){
55739 this.showPanel(panel);
55742 var el = panel.getEl();
55743 if(el.dom.parentNode != this.mgr.el.dom){
55744 this.mgr.el.dom.appendChild(el.dom);
55746 if(panel.setRegion){
55747 panel.setRegion(this);
55749 this.panels.add(panel);
55750 el.setStyle("position", "absolute");
55751 if(!panel.background){
55752 this.setActivePanel(panel);
55753 if(this.config.initialSize && this.panels.getCount()==1){
55754 this.resizeTo(this.config.initialSize);
55757 this.fireEvent("paneladded", this, panel);
55762 * Returns true if the panel is in this region.
55763 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55764 * @return {Boolean}
55766 hasPanel : function(panel){
55767 if(typeof panel == "object"){ // must be panel obj
55768 panel = panel.getId();
55770 return this.getPanel(panel) ? true : false;
55774 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55775 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55776 * @param {Boolean} preservePanel Overrides the config preservePanel option
55777 * @return {Roo.ContentPanel} The panel that was removed
55779 remove : function(panel, preservePanel){
55780 panel = this.getPanel(panel);
55785 this.fireEvent("beforeremove", this, panel, e);
55786 if(e.cancel === true){
55789 var panelId = panel.getId();
55790 this.panels.removeKey(panelId);
55795 * Returns the panel specified or null if it's not in this region.
55796 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55797 * @return {Roo.ContentPanel}
55799 getPanel : function(id){
55800 if(typeof id == "object"){ // must be panel obj
55803 return this.panels.get(id);
55807 * Returns this regions position (north/south/east/west/center).
55810 getPosition: function(){
55811 return this.position;
55815 * Ext JS Library 1.1.1
55816 * Copyright(c) 2006-2007, Ext JS, LLC.
55818 * Originally Released Under LGPL - original licence link has changed is not relivant.
55821 * <script type="text/javascript">
55825 * @class Roo.LayoutRegion
55826 * @extends Roo.BasicLayoutRegion
55827 * This class represents a region in a layout manager.
55828 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
55829 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
55830 * @cfg {Boolean} floatable False to disable floating (defaults to true)
55831 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55832 * @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})
55833 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
55834 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
55835 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
55836 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
55837 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
55838 * @cfg {String} title The title for the region (overrides panel titles)
55839 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
55840 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55841 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
55842 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55843 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
55844 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55845 * the space available, similar to FireFox 1.5 tabs (defaults to false)
55846 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
55847 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
55848 * @cfg {Boolean} showPin True to show a pin button
55849 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
55850 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
55851 * @cfg {Boolean} disableTabTips True to disable tab tooltips
55852 * @cfg {Number} width For East/West panels
55853 * @cfg {Number} height For North/South panels
55854 * @cfg {Boolean} split To show the splitter
55855 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
55857 Roo.LayoutRegion = function(mgr, config, pos){
55858 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55859 var dh = Roo.DomHelper;
55860 /** This region's container element
55861 * @type Roo.Element */
55862 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55863 /** This region's title element
55864 * @type Roo.Element */
55866 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55867 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
55868 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55870 this.titleEl.enableDisplayMode();
55871 /** This region's title text element
55872 * @type HTMLElement */
55873 this.titleTextEl = this.titleEl.dom.firstChild;
55874 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55875 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55876 this.closeBtn.enableDisplayMode();
55877 this.closeBtn.on("click", this.closeClicked, this);
55878 this.closeBtn.hide();
55880 this.createBody(config);
55881 this.visible = true;
55882 this.collapsed = false;
55884 if(config.hideWhenEmpty){
55886 this.on("paneladded", this.validateVisibility, this);
55887 this.on("panelremoved", this.validateVisibility, this);
55889 this.applyConfig(config);
55892 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55894 createBody : function(){
55895 /** This region's body element
55896 * @type Roo.Element */
55897 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55900 applyConfig : function(c){
55901 if(c.collapsible && this.position != "center" && !this.collapsedEl){
55902 var dh = Roo.DomHelper;
55903 if(c.titlebar !== false){
55904 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55905 this.collapseBtn.on("click", this.collapse, this);
55906 this.collapseBtn.enableDisplayMode();
55908 if(c.showPin === true || this.showPin){
55909 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55910 this.stickBtn.enableDisplayMode();
55911 this.stickBtn.on("click", this.expand, this);
55912 this.stickBtn.hide();
55915 /** This region's collapsed element
55916 * @type Roo.Element */
55917 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55918 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55920 if(c.floatable !== false){
55921 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55922 this.collapsedEl.on("click", this.collapseClick, this);
55925 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55926 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55927 id: "message", unselectable: "on", style:{"float":"left"}});
55928 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55930 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55931 this.expandBtn.on("click", this.expand, this);
55933 if(this.collapseBtn){
55934 this.collapseBtn.setVisible(c.collapsible == true);
55936 this.cmargins = c.cmargins || this.cmargins ||
55937 (this.position == "west" || this.position == "east" ?
55938 {top: 0, left: 2, right:2, bottom: 0} :
55939 {top: 2, left: 0, right:0, bottom: 2});
55940 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55941 this.bottomTabs = c.tabPosition != "top";
55942 this.autoScroll = c.autoScroll || false;
55943 if(this.autoScroll){
55944 this.bodyEl.setStyle("overflow", "auto");
55946 this.bodyEl.setStyle("overflow", "hidden");
55948 //if(c.titlebar !== false){
55949 if((!c.titlebar && !c.title) || c.titlebar === false){
55950 this.titleEl.hide();
55952 this.titleEl.show();
55954 this.titleTextEl.innerHTML = c.title;
55958 this.duration = c.duration || .30;
55959 this.slideDuration = c.slideDuration || .45;
55962 this.collapse(true);
55969 * Returns true if this region is currently visible.
55970 * @return {Boolean}
55972 isVisible : function(){
55973 return this.visible;
55977 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55978 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
55980 setCollapsedTitle : function(title){
55981 title = title || " ";
55982 if(this.collapsedTitleTextEl){
55983 this.collapsedTitleTextEl.innerHTML = title;
55987 getBox : function(){
55989 if(!this.collapsed){
55990 b = this.el.getBox(false, true);
55992 b = this.collapsedEl.getBox(false, true);
55997 getMargins : function(){
55998 return this.collapsed ? this.cmargins : this.margins;
56001 highlight : function(){
56002 this.el.addClass("x-layout-panel-dragover");
56005 unhighlight : function(){
56006 this.el.removeClass("x-layout-panel-dragover");
56009 updateBox : function(box){
56011 if(!this.collapsed){
56012 this.el.dom.style.left = box.x + "px";
56013 this.el.dom.style.top = box.y + "px";
56014 this.updateBody(box.width, box.height);
56016 this.collapsedEl.dom.style.left = box.x + "px";
56017 this.collapsedEl.dom.style.top = box.y + "px";
56018 this.collapsedEl.setSize(box.width, box.height);
56021 this.tabs.autoSizeTabs();
56025 updateBody : function(w, h){
56027 this.el.setWidth(w);
56028 w -= this.el.getBorderWidth("rl");
56029 if(this.config.adjustments){
56030 w += this.config.adjustments[0];
56034 this.el.setHeight(h);
56035 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56036 h -= this.el.getBorderWidth("tb");
56037 if(this.config.adjustments){
56038 h += this.config.adjustments[1];
56040 this.bodyEl.setHeight(h);
56042 h = this.tabs.syncHeight(h);
56045 if(this.panelSize){
56046 w = w !== null ? w : this.panelSize.width;
56047 h = h !== null ? h : this.panelSize.height;
56049 if(this.activePanel){
56050 var el = this.activePanel.getEl();
56051 w = w !== null ? w : el.getWidth();
56052 h = h !== null ? h : el.getHeight();
56053 this.panelSize = {width: w, height: h};
56054 this.activePanel.setSize(w, h);
56056 if(Roo.isIE && this.tabs){
56057 this.tabs.el.repaint();
56062 * Returns the container element for this region.
56063 * @return {Roo.Element}
56065 getEl : function(){
56070 * Hides this region.
56073 if(!this.collapsed){
56074 this.el.dom.style.left = "-2000px";
56077 this.collapsedEl.dom.style.left = "-2000px";
56078 this.collapsedEl.hide();
56080 this.visible = false;
56081 this.fireEvent("visibilitychange", this, false);
56085 * Shows this region if it was previously hidden.
56088 if(!this.collapsed){
56091 this.collapsedEl.show();
56093 this.visible = true;
56094 this.fireEvent("visibilitychange", this, true);
56097 closeClicked : function(){
56098 if(this.activePanel){
56099 this.remove(this.activePanel);
56103 collapseClick : function(e){
56105 e.stopPropagation();
56108 e.stopPropagation();
56114 * Collapses this region.
56115 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56117 collapse : function(skipAnim, skipCheck){
56118 if(this.collapsed) {
56122 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56124 this.collapsed = true;
56126 this.split.el.hide();
56128 if(this.config.animate && skipAnim !== true){
56129 this.fireEvent("invalidated", this);
56130 this.animateCollapse();
56132 this.el.setLocation(-20000,-20000);
56134 this.collapsedEl.show();
56135 this.fireEvent("collapsed", this);
56136 this.fireEvent("invalidated", this);
56142 animateCollapse : function(){
56147 * Expands this region if it was previously collapsed.
56148 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56149 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56151 expand : function(e, skipAnim){
56153 e.stopPropagation();
56155 if(!this.collapsed || this.el.hasActiveFx()) {
56159 this.afterSlideIn();
56162 this.collapsed = false;
56163 if(this.config.animate && skipAnim !== true){
56164 this.animateExpand();
56168 this.split.el.show();
56170 this.collapsedEl.setLocation(-2000,-2000);
56171 this.collapsedEl.hide();
56172 this.fireEvent("invalidated", this);
56173 this.fireEvent("expanded", this);
56177 animateExpand : function(){
56181 initTabs : function()
56183 this.bodyEl.setStyle("overflow", "hidden");
56184 var ts = new Roo.TabPanel(
56187 tabPosition: this.bottomTabs ? 'bottom' : 'top',
56188 disableTooltips: this.config.disableTabTips,
56189 toolbar : this.config.toolbar
56192 if(this.config.hideTabs){
56193 ts.stripWrap.setDisplayed(false);
56196 ts.resizeTabs = this.config.resizeTabs === true;
56197 ts.minTabWidth = this.config.minTabWidth || 40;
56198 ts.maxTabWidth = this.config.maxTabWidth || 250;
56199 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56200 ts.monitorResize = false;
56201 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56202 ts.bodyEl.addClass('x-layout-tabs-body');
56203 this.panels.each(this.initPanelAsTab, this);
56206 initPanelAsTab : function(panel){
56207 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56208 this.config.closeOnTab && panel.isClosable());
56209 if(panel.tabTip !== undefined){
56210 ti.setTooltip(panel.tabTip);
56212 ti.on("activate", function(){
56213 this.setActivePanel(panel);
56215 if(this.config.closeOnTab){
56216 ti.on("beforeclose", function(t, e){
56218 this.remove(panel);
56224 updatePanelTitle : function(panel, title){
56225 if(this.activePanel == panel){
56226 this.updateTitle(title);
56229 var ti = this.tabs.getTab(panel.getEl().id);
56231 if(panel.tabTip !== undefined){
56232 ti.setTooltip(panel.tabTip);
56237 updateTitle : function(title){
56238 if(this.titleTextEl && !this.config.title){
56239 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
56243 setActivePanel : function(panel){
56244 panel = this.getPanel(panel);
56245 if(this.activePanel && this.activePanel != panel){
56246 this.activePanel.setActiveState(false);
56248 this.activePanel = panel;
56249 panel.setActiveState(true);
56250 if(this.panelSize){
56251 panel.setSize(this.panelSize.width, this.panelSize.height);
56254 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56256 this.updateTitle(panel.getTitle());
56258 this.fireEvent("invalidated", this);
56260 this.fireEvent("panelactivated", this, panel);
56264 * Shows the specified panel.
56265 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56266 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56268 showPanel : function(panel)
56270 panel = this.getPanel(panel);
56273 var tab = this.tabs.getTab(panel.getEl().id);
56274 if(tab.isHidden()){
56275 this.tabs.unhideTab(tab.id);
56279 this.setActivePanel(panel);
56286 * Get the active panel for this region.
56287 * @return {Roo.ContentPanel} The active panel or null
56289 getActivePanel : function(){
56290 return this.activePanel;
56293 validateVisibility : function(){
56294 if(this.panels.getCount() < 1){
56295 this.updateTitle(" ");
56296 this.closeBtn.hide();
56299 if(!this.isVisible()){
56306 * Adds the passed ContentPanel(s) to this region.
56307 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56308 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56310 add : function(panel){
56311 if(arguments.length > 1){
56312 for(var i = 0, len = arguments.length; i < len; i++) {
56313 this.add(arguments[i]);
56317 if(this.hasPanel(panel)){
56318 this.showPanel(panel);
56321 panel.setRegion(this);
56322 this.panels.add(panel);
56323 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56324 this.bodyEl.dom.appendChild(panel.getEl().dom);
56325 if(panel.background !== true){
56326 this.setActivePanel(panel);
56328 this.fireEvent("paneladded", this, panel);
56334 this.initPanelAsTab(panel);
56336 if(panel.background !== true){
56337 this.tabs.activate(panel.getEl().id);
56339 this.fireEvent("paneladded", this, panel);
56344 * Hides the tab for the specified panel.
56345 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56347 hidePanel : function(panel){
56348 if(this.tabs && (panel = this.getPanel(panel))){
56349 this.tabs.hideTab(panel.getEl().id);
56354 * Unhides the tab for a previously hidden panel.
56355 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56357 unhidePanel : function(panel){
56358 if(this.tabs && (panel = this.getPanel(panel))){
56359 this.tabs.unhideTab(panel.getEl().id);
56363 clearPanels : function(){
56364 while(this.panels.getCount() > 0){
56365 this.remove(this.panels.first());
56370 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56371 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56372 * @param {Boolean} preservePanel Overrides the config preservePanel option
56373 * @return {Roo.ContentPanel} The panel that was removed
56375 remove : function(panel, preservePanel){
56376 panel = this.getPanel(panel);
56381 this.fireEvent("beforeremove", this, panel, e);
56382 if(e.cancel === true){
56385 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56386 var panelId = panel.getId();
56387 this.panels.removeKey(panelId);
56389 document.body.appendChild(panel.getEl().dom);
56392 this.tabs.removeTab(panel.getEl().id);
56393 }else if (!preservePanel){
56394 this.bodyEl.dom.removeChild(panel.getEl().dom);
56396 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56397 var p = this.panels.first();
56398 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56399 tempEl.appendChild(p.getEl().dom);
56400 this.bodyEl.update("");
56401 this.bodyEl.dom.appendChild(p.getEl().dom);
56403 this.updateTitle(p.getTitle());
56405 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56406 this.setActivePanel(p);
56408 panel.setRegion(null);
56409 if(this.activePanel == panel){
56410 this.activePanel = null;
56412 if(this.config.autoDestroy !== false && preservePanel !== true){
56413 try{panel.destroy();}catch(e){}
56415 this.fireEvent("panelremoved", this, panel);
56420 * Returns the TabPanel component used by this region
56421 * @return {Roo.TabPanel}
56423 getTabs : function(){
56427 createTool : function(parentEl, className){
56428 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56429 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
56430 btn.addClassOnOver("x-layout-tools-button-over");
56435 * Ext JS Library 1.1.1
56436 * Copyright(c) 2006-2007, Ext JS, LLC.
56438 * Originally Released Under LGPL - original licence link has changed is not relivant.
56441 * <script type="text/javascript">
56447 * @class Roo.SplitLayoutRegion
56448 * @extends Roo.LayoutRegion
56449 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56451 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56452 this.cursor = cursor;
56453 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56456 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56457 splitTip : "Drag to resize.",
56458 collapsibleSplitTip : "Drag to resize. Double click to hide.",
56459 useSplitTips : false,
56461 applyConfig : function(config){
56462 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56465 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
56466 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
56467 /** The SplitBar for this region
56468 * @type Roo.SplitBar */
56469 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56470 this.split.on("moved", this.onSplitMove, this);
56471 this.split.useShim = config.useShim === true;
56472 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56473 if(this.useSplitTips){
56474 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56476 if(config.collapsible){
56477 this.split.el.on("dblclick", this.collapse, this);
56480 if(typeof config.minSize != "undefined"){
56481 this.split.minSize = config.minSize;
56483 if(typeof config.maxSize != "undefined"){
56484 this.split.maxSize = config.maxSize;
56486 if(config.hideWhenEmpty || config.hidden || config.collapsed){
56487 this.hideSplitter();
56492 getHMaxSize : function(){
56493 var cmax = this.config.maxSize || 10000;
56494 var center = this.mgr.getRegion("center");
56495 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56498 getVMaxSize : function(){
56499 var cmax = this.config.maxSize || 10000;
56500 var center = this.mgr.getRegion("center");
56501 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56504 onSplitMove : function(split, newSize){
56505 this.fireEvent("resized", this, newSize);
56509 * Returns the {@link Roo.SplitBar} for this region.
56510 * @return {Roo.SplitBar}
56512 getSplitBar : function(){
56517 this.hideSplitter();
56518 Roo.SplitLayoutRegion.superclass.hide.call(this);
56521 hideSplitter : function(){
56523 this.split.el.setLocation(-2000,-2000);
56524 this.split.el.hide();
56530 this.split.el.show();
56532 Roo.SplitLayoutRegion.superclass.show.call(this);
56535 beforeSlide: function(){
56536 if(Roo.isGecko){// firefox overflow auto bug workaround
56537 this.bodyEl.clip();
56539 this.tabs.bodyEl.clip();
56541 if(this.activePanel){
56542 this.activePanel.getEl().clip();
56544 if(this.activePanel.beforeSlide){
56545 this.activePanel.beforeSlide();
56551 afterSlide : function(){
56552 if(Roo.isGecko){// firefox overflow auto bug workaround
56553 this.bodyEl.unclip();
56555 this.tabs.bodyEl.unclip();
56557 if(this.activePanel){
56558 this.activePanel.getEl().unclip();
56559 if(this.activePanel.afterSlide){
56560 this.activePanel.afterSlide();
56566 initAutoHide : function(){
56567 if(this.autoHide !== false){
56568 if(!this.autoHideHd){
56569 var st = new Roo.util.DelayedTask(this.slideIn, this);
56570 this.autoHideHd = {
56571 "mouseout": function(e){
56572 if(!e.within(this.el, true)){
56576 "mouseover" : function(e){
56582 this.el.on(this.autoHideHd);
56586 clearAutoHide : function(){
56587 if(this.autoHide !== false){
56588 this.el.un("mouseout", this.autoHideHd.mouseout);
56589 this.el.un("mouseover", this.autoHideHd.mouseover);
56593 clearMonitor : function(){
56594 Roo.get(document).un("click", this.slideInIf, this);
56597 // these names are backwards but not changed for compat
56598 slideOut : function(){
56599 if(this.isSlid || this.el.hasActiveFx()){
56602 this.isSlid = true;
56603 if(this.collapseBtn){
56604 this.collapseBtn.hide();
56606 this.closeBtnState = this.closeBtn.getStyle('display');
56607 this.closeBtn.hide();
56609 this.stickBtn.show();
56612 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56613 this.beforeSlide();
56614 this.el.setStyle("z-index", 10001);
56615 this.el.slideIn(this.getSlideAnchor(), {
56616 callback: function(){
56618 this.initAutoHide();
56619 Roo.get(document).on("click", this.slideInIf, this);
56620 this.fireEvent("slideshow", this);
56627 afterSlideIn : function(){
56628 this.clearAutoHide();
56629 this.isSlid = false;
56630 this.clearMonitor();
56631 this.el.setStyle("z-index", "");
56632 if(this.collapseBtn){
56633 this.collapseBtn.show();
56635 this.closeBtn.setStyle('display', this.closeBtnState);
56637 this.stickBtn.hide();
56639 this.fireEvent("slidehide", this);
56642 slideIn : function(cb){
56643 if(!this.isSlid || this.el.hasActiveFx()){
56647 this.isSlid = false;
56648 this.beforeSlide();
56649 this.el.slideOut(this.getSlideAnchor(), {
56650 callback: function(){
56651 this.el.setLeftTop(-10000, -10000);
56653 this.afterSlideIn();
56661 slideInIf : function(e){
56662 if(!e.within(this.el)){
56667 animateCollapse : function(){
56668 this.beforeSlide();
56669 this.el.setStyle("z-index", 20000);
56670 var anchor = this.getSlideAnchor();
56671 this.el.slideOut(anchor, {
56672 callback : function(){
56673 this.el.setStyle("z-index", "");
56674 this.collapsedEl.slideIn(anchor, {duration:.3});
56676 this.el.setLocation(-10000,-10000);
56678 this.fireEvent("collapsed", this);
56685 animateExpand : function(){
56686 this.beforeSlide();
56687 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56688 this.el.setStyle("z-index", 20000);
56689 this.collapsedEl.hide({
56692 this.el.slideIn(this.getSlideAnchor(), {
56693 callback : function(){
56694 this.el.setStyle("z-index", "");
56697 this.split.el.show();
56699 this.fireEvent("invalidated", this);
56700 this.fireEvent("expanded", this);
56728 getAnchor : function(){
56729 return this.anchors[this.position];
56732 getCollapseAnchor : function(){
56733 return this.canchors[this.position];
56736 getSlideAnchor : function(){
56737 return this.sanchors[this.position];
56740 getAlignAdj : function(){
56741 var cm = this.cmargins;
56742 switch(this.position){
56758 getExpandAdj : function(){
56759 var c = this.collapsedEl, cm = this.cmargins;
56760 switch(this.position){
56762 return [-(cm.right+c.getWidth()+cm.left), 0];
56765 return [cm.right+c.getWidth()+cm.left, 0];
56768 return [0, -(cm.top+cm.bottom+c.getHeight())];
56771 return [0, cm.top+cm.bottom+c.getHeight()];
56777 * Ext JS Library 1.1.1
56778 * Copyright(c) 2006-2007, Ext JS, LLC.
56780 * Originally Released Under LGPL - original licence link has changed is not relivant.
56783 * <script type="text/javascript">
56786 * These classes are private internal classes
56788 Roo.CenterLayoutRegion = function(mgr, config){
56789 Roo.LayoutRegion.call(this, mgr, config, "center");
56790 this.visible = true;
56791 this.minWidth = config.minWidth || 20;
56792 this.minHeight = config.minHeight || 20;
56795 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56797 // center panel can't be hidden
56801 // center panel can't be hidden
56804 getMinWidth: function(){
56805 return this.minWidth;
56808 getMinHeight: function(){
56809 return this.minHeight;
56814 Roo.NorthLayoutRegion = function(mgr, config){
56815 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56817 this.split.placement = Roo.SplitBar.TOP;
56818 this.split.orientation = Roo.SplitBar.VERTICAL;
56819 this.split.el.addClass("x-layout-split-v");
56821 var size = config.initialSize || config.height;
56822 if(typeof size != "undefined"){
56823 this.el.setHeight(size);
56826 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56827 orientation: Roo.SplitBar.VERTICAL,
56828 getBox : function(){
56829 if(this.collapsed){
56830 return this.collapsedEl.getBox();
56832 var box = this.el.getBox();
56834 box.height += this.split.el.getHeight();
56839 updateBox : function(box){
56840 if(this.split && !this.collapsed){
56841 box.height -= this.split.el.getHeight();
56842 this.split.el.setLeft(box.x);
56843 this.split.el.setTop(box.y+box.height);
56844 this.split.el.setWidth(box.width);
56846 if(this.collapsed){
56847 this.updateBody(box.width, null);
56849 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56853 Roo.SouthLayoutRegion = function(mgr, config){
56854 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56856 this.split.placement = Roo.SplitBar.BOTTOM;
56857 this.split.orientation = Roo.SplitBar.VERTICAL;
56858 this.split.el.addClass("x-layout-split-v");
56860 var size = config.initialSize || config.height;
56861 if(typeof size != "undefined"){
56862 this.el.setHeight(size);
56865 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56866 orientation: Roo.SplitBar.VERTICAL,
56867 getBox : function(){
56868 if(this.collapsed){
56869 return this.collapsedEl.getBox();
56871 var box = this.el.getBox();
56873 var sh = this.split.el.getHeight();
56880 updateBox : function(box){
56881 if(this.split && !this.collapsed){
56882 var sh = this.split.el.getHeight();
56885 this.split.el.setLeft(box.x);
56886 this.split.el.setTop(box.y-sh);
56887 this.split.el.setWidth(box.width);
56889 if(this.collapsed){
56890 this.updateBody(box.width, null);
56892 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56896 Roo.EastLayoutRegion = function(mgr, config){
56897 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56899 this.split.placement = Roo.SplitBar.RIGHT;
56900 this.split.orientation = Roo.SplitBar.HORIZONTAL;
56901 this.split.el.addClass("x-layout-split-h");
56903 var size = config.initialSize || config.width;
56904 if(typeof size != "undefined"){
56905 this.el.setWidth(size);
56908 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56909 orientation: Roo.SplitBar.HORIZONTAL,
56910 getBox : function(){
56911 if(this.collapsed){
56912 return this.collapsedEl.getBox();
56914 var box = this.el.getBox();
56916 var sw = this.split.el.getWidth();
56923 updateBox : function(box){
56924 if(this.split && !this.collapsed){
56925 var sw = this.split.el.getWidth();
56927 this.split.el.setLeft(box.x);
56928 this.split.el.setTop(box.y);
56929 this.split.el.setHeight(box.height);
56932 if(this.collapsed){
56933 this.updateBody(null, box.height);
56935 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56939 Roo.WestLayoutRegion = function(mgr, config){
56940 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56942 this.split.placement = Roo.SplitBar.LEFT;
56943 this.split.orientation = Roo.SplitBar.HORIZONTAL;
56944 this.split.el.addClass("x-layout-split-h");
56946 var size = config.initialSize || config.width;
56947 if(typeof size != "undefined"){
56948 this.el.setWidth(size);
56951 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56952 orientation: Roo.SplitBar.HORIZONTAL,
56953 getBox : function(){
56954 if(this.collapsed){
56955 return this.collapsedEl.getBox();
56957 var box = this.el.getBox();
56959 box.width += this.split.el.getWidth();
56964 updateBox : function(box){
56965 if(this.split && !this.collapsed){
56966 var sw = this.split.el.getWidth();
56968 this.split.el.setLeft(box.x+box.width);
56969 this.split.el.setTop(box.y);
56970 this.split.el.setHeight(box.height);
56972 if(this.collapsed){
56973 this.updateBody(null, box.height);
56975 Roo.LayoutRegion.prototype.updateBox.call(this, box);
56980 * Ext JS Library 1.1.1
56981 * Copyright(c) 2006-2007, Ext JS, LLC.
56983 * Originally Released Under LGPL - original licence link has changed is not relivant.
56986 * <script type="text/javascript">
56991 * Private internal class for reading and applying state
56993 Roo.LayoutStateManager = function(layout){
56994 // default empty state
57003 Roo.LayoutStateManager.prototype = {
57004 init : function(layout, provider){
57005 this.provider = provider;
57006 var state = provider.get(layout.id+"-layout-state");
57008 var wasUpdating = layout.isUpdating();
57010 layout.beginUpdate();
57012 for(var key in state){
57013 if(typeof state[key] != "function"){
57014 var rstate = state[key];
57015 var r = layout.getRegion(key);
57018 r.resizeTo(rstate.size);
57020 if(rstate.collapsed == true){
57023 r.expand(null, true);
57029 layout.endUpdate();
57031 this.state = state;
57033 this.layout = layout;
57034 layout.on("regionresized", this.onRegionResized, this);
57035 layout.on("regioncollapsed", this.onRegionCollapsed, this);
57036 layout.on("regionexpanded", this.onRegionExpanded, this);
57039 storeState : function(){
57040 this.provider.set(this.layout.id+"-layout-state", this.state);
57043 onRegionResized : function(region, newSize){
57044 this.state[region.getPosition()].size = newSize;
57048 onRegionCollapsed : function(region){
57049 this.state[region.getPosition()].collapsed = true;
57053 onRegionExpanded : function(region){
57054 this.state[region.getPosition()].collapsed = false;
57059 * Ext JS Library 1.1.1
57060 * Copyright(c) 2006-2007, Ext JS, LLC.
57062 * Originally Released Under LGPL - original licence link has changed is not relivant.
57065 * <script type="text/javascript">
57068 * @class Roo.ContentPanel
57069 * @extends Roo.util.Observable
57070 * @children Roo.form.Form Roo.JsonView Roo.View
57071 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57072 * A basic ContentPanel element.
57073 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
57074 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
57075 * @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
57076 * @cfg {Boolean} closable True if the panel can be closed/removed
57077 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
57078 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57079 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
57080 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
57081 * @cfg {String} title The title for this panel
57082 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57083 * @cfg {String} url Calls {@link #setUrl} with this value
57084 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57085 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
57086 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
57087 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
57088 * @cfg {String} style Extra style to add to the content panel
57089 * @cfg {Roo.menu.Menu} menu popup menu
57092 * Create a new ContentPanel.
57093 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57094 * @param {String/Object} config A string to set only the title or a config object
57095 * @param {String} content (optional) Set the HTML content for this panel
57096 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57098 Roo.ContentPanel = function(el, config, content){
57102 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57106 if (config && config.parentLayout) {
57107 el = config.parentLayout.el.createChild();
57110 if(el.autoCreate){ // xtype is available if this is called from factory
57114 this.el = Roo.get(el);
57115 if(!this.el && config && config.autoCreate){
57116 if(typeof config.autoCreate == "object"){
57117 if(!config.autoCreate.id){
57118 config.autoCreate.id = config.id||el;
57120 this.el = Roo.DomHelper.append(document.body,
57121 config.autoCreate, true);
57123 this.el = Roo.DomHelper.append(document.body,
57124 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57129 this.closable = false;
57130 this.loaded = false;
57131 this.active = false;
57132 if(typeof config == "string"){
57133 this.title = config;
57135 Roo.apply(this, config);
57138 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57139 this.wrapEl = this.el.wrap();
57140 this.toolbar.container = this.el.insertSibling(false, 'before');
57141 this.toolbar = new Roo.Toolbar(this.toolbar);
57144 // xtype created footer. - not sure if will work as we normally have to render first..
57145 if (this.footer && !this.footer.el && this.footer.xtype) {
57146 if (!this.wrapEl) {
57147 this.wrapEl = this.el.wrap();
57150 this.footer.container = this.wrapEl.createChild();
57152 this.footer = Roo.factory(this.footer, Roo);
57157 this.resizeEl = Roo.get(this.resizeEl, true);
57159 this.resizeEl = this.el;
57161 // handle view.xtype
57169 * Fires when this panel is activated.
57170 * @param {Roo.ContentPanel} this
57174 * @event deactivate
57175 * Fires when this panel is activated.
57176 * @param {Roo.ContentPanel} this
57178 "deactivate" : true,
57182 * Fires when this panel is resized if fitToFrame is true.
57183 * @param {Roo.ContentPanel} this
57184 * @param {Number} width The width after any component adjustments
57185 * @param {Number} height The height after any component adjustments
57191 * Fires when this tab is created
57192 * @param {Roo.ContentPanel} this
57202 if(this.autoScroll){
57203 this.resizeEl.setStyle("overflow", "auto");
57205 // fix randome scrolling
57206 this.el.on('scroll', function() {
57207 Roo.log('fix random scolling');
57208 this.scrollTo('top',0);
57211 content = content || this.content;
57213 this.setContent(content);
57215 if(config && config.url){
57216 this.setUrl(this.url, this.params, this.loadOnce);
57221 Roo.ContentPanel.superclass.constructor.call(this);
57223 if (this.view && typeof(this.view.xtype) != 'undefined') {
57224 this.view.el = this.el.appendChild(document.createElement("div"));
57225 this.view = Roo.factory(this.view);
57226 this.view.render && this.view.render(false, '');
57230 this.fireEvent('render', this);
57233 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57235 setRegion : function(region){
57236 this.region = region;
57238 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57240 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57245 * Returns the toolbar for this Panel if one was configured.
57246 * @return {Roo.Toolbar}
57248 getToolbar : function(){
57249 return this.toolbar;
57252 setActiveState : function(active){
57253 this.active = active;
57255 this.fireEvent("deactivate", this);
57257 this.fireEvent("activate", this);
57261 * Updates this panel's element
57262 * @param {String} content The new content
57263 * @param {Boolean} loadScripts (optional) true to look for and process scripts
57265 setContent : function(content, loadScripts){
57266 this.el.update(content, loadScripts);
57269 ignoreResize : function(w, h){
57270 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57273 this.lastSize = {width: w, height: h};
57278 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57279 * @return {Roo.UpdateManager} The UpdateManager
57281 getUpdateManager : function(){
57282 return this.el.getUpdateManager();
57285 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57286 * @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:
57289 url: "your-url.php",
57290 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57291 callback: yourFunction,
57292 scope: yourObject, //(optional scope)
57295 text: "Loading...",
57300 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57301 * 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.
57302 * @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}
57303 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57304 * @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.
57305 * @return {Roo.ContentPanel} this
57308 var um = this.el.getUpdateManager();
57309 um.update.apply(um, arguments);
57315 * 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.
57316 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57317 * @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)
57318 * @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)
57319 * @return {Roo.UpdateManager} The UpdateManager
57321 setUrl : function(url, params, loadOnce){
57322 if(this.refreshDelegate){
57323 this.removeListener("activate", this.refreshDelegate);
57325 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57326 this.on("activate", this.refreshDelegate);
57327 return this.el.getUpdateManager();
57330 _handleRefresh : function(url, params, loadOnce){
57331 if(!loadOnce || !this.loaded){
57332 var updater = this.el.getUpdateManager();
57333 updater.update(url, params, this._setLoaded.createDelegate(this));
57337 _setLoaded : function(){
57338 this.loaded = true;
57342 * Returns this panel's id
57345 getId : function(){
57350 * Returns this panel's element - used by regiosn to add.
57351 * @return {Roo.Element}
57353 getEl : function(){
57354 return this.wrapEl || this.el;
57357 adjustForComponents : function(width, height)
57359 //Roo.log('adjustForComponents ');
57360 if(this.resizeEl != this.el){
57361 width -= this.el.getFrameWidth('lr');
57362 height -= this.el.getFrameWidth('tb');
57365 var te = this.toolbar.getEl();
57366 height -= te.getHeight();
57367 te.setWidth(width);
57370 var te = this.footer.getEl();
57371 //Roo.log("footer:" + te.getHeight());
57373 height -= te.getHeight();
57374 te.setWidth(width);
57378 if(this.adjustments){
57379 width += this.adjustments[0];
57380 height += this.adjustments[1];
57382 return {"width": width, "height": height};
57385 setSize : function(width, height){
57386 if(this.fitToFrame && !this.ignoreResize(width, height)){
57387 if(this.fitContainer && this.resizeEl != this.el){
57388 this.el.setSize(width, height);
57390 var size = this.adjustForComponents(width, height);
57391 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57392 this.fireEvent('resize', this, size.width, size.height);
57397 * Returns this panel's title
57400 getTitle : function(){
57405 * Set this panel's title
57406 * @param {String} title
57408 setTitle : function(title){
57409 this.title = title;
57411 this.region.updatePanelTitle(this, title);
57416 * Returns true is this panel was configured to be closable
57417 * @return {Boolean}
57419 isClosable : function(){
57420 return this.closable;
57423 beforeSlide : function(){
57425 this.resizeEl.clip();
57428 afterSlide : function(){
57430 this.resizeEl.unclip();
57434 * Force a content refresh from the URL specified in the {@link #setUrl} method.
57435 * Will fail silently if the {@link #setUrl} method has not been called.
57436 * This does not activate the panel, just updates its content.
57438 refresh : function(){
57439 if(this.refreshDelegate){
57440 this.loaded = false;
57441 this.refreshDelegate();
57446 * Destroys this panel
57448 destroy : function(){
57449 this.el.removeAllListeners();
57450 var tempEl = document.createElement("span");
57451 tempEl.appendChild(this.el.dom);
57452 tempEl.innerHTML = "";
57458 * form - if the content panel contains a form - this is a reference to it.
57459 * @type {Roo.form.Form}
57463 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57464 * This contains a reference to it.
57470 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57480 * @param {Object} cfg Xtype definition of item to add.
57483 addxtype : function(cfg) {
57485 if (cfg.xtype.match(/^Form$/)) {
57488 //if (this.footer) {
57489 // el = this.footer.container.insertSibling(false, 'before');
57491 el = this.el.createChild();
57494 this.form = new Roo.form.Form(cfg);
57497 if ( this.form.allItems.length) {
57498 this.form.render(el.dom);
57502 // should only have one of theses..
57503 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57504 // views.. should not be just added - used named prop 'view''
57506 cfg.el = this.el.appendChild(document.createElement("div"));
57509 var ret = new Roo.factory(cfg);
57511 ret.render && ret.render(false, ''); // render blank..
57520 * @class Roo.GridPanel
57521 * @extends Roo.ContentPanel
57522 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57524 * Create a new GridPanel.
57525 * @cfg {Roo.grid.Grid} grid The grid for this panel
57527 Roo.GridPanel = function(grid, config){
57529 // universal ctor...
57530 if (typeof(grid.grid) != 'undefined') {
57532 grid = config.grid;
57534 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57535 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57537 this.wrapper.dom.appendChild(grid.getGridEl().dom);
57539 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57542 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57544 // xtype created footer. - not sure if will work as we normally have to render first..
57545 if (this.footer && !this.footer.el && this.footer.xtype) {
57547 this.footer.container = this.grid.getView().getFooterPanel(true);
57548 this.footer.dataSource = this.grid.dataSource;
57549 this.footer = Roo.factory(this.footer, Roo);
57553 grid.monitorWindowResize = false; // turn off autosizing
57554 grid.autoHeight = false;
57555 grid.autoWidth = false;
57557 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57560 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57561 getId : function(){
57562 return this.grid.id;
57566 * Returns the grid for this panel
57567 * @return {Roo.grid.Grid}
57569 getGrid : function(){
57573 setSize : function(width, height){
57574 if(!this.ignoreResize(width, height)){
57575 var grid = this.grid;
57576 var size = this.adjustForComponents(width, height);
57577 grid.getGridEl().setSize(size.width, size.height);
57582 beforeSlide : function(){
57583 this.grid.getView().scroller.clip();
57586 afterSlide : function(){
57587 this.grid.getView().scroller.unclip();
57590 destroy : function(){
57591 this.grid.destroy();
57593 Roo.GridPanel.superclass.destroy.call(this);
57599 * @class Roo.NestedLayoutPanel
57600 * @extends Roo.ContentPanel
57601 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57602 * @cfg Roo.BorderLayout} layout [required] The layout for this panel
57606 * Create a new NestedLayoutPanel.
57609 * @param {Roo.BorderLayout} layout [required] The layout for this panel
57610 * @param {String/Object} config A string to set only the title or a config object
57612 Roo.NestedLayoutPanel = function(layout, config)
57614 // construct with only one argument..
57615 /* FIXME - implement nicer consturctors
57616 if (layout.layout) {
57618 layout = config.layout;
57619 delete config.layout;
57621 if (layout.xtype && !layout.getEl) {
57622 // then layout needs constructing..
57623 layout = Roo.factory(layout, Roo);
57628 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57630 layout.monitorWindowResize = false; // turn off autosizing
57631 this.layout = layout;
57632 this.layout.getEl().addClass("x-layout-nested-layout");
57639 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57641 setSize : function(width, height){
57642 if(!this.ignoreResize(width, height)){
57643 var size = this.adjustForComponents(width, height);
57644 var el = this.layout.getEl();
57645 el.setSize(size.width, size.height);
57646 var touch = el.dom.offsetWidth;
57647 this.layout.layout();
57648 // ie requires a double layout on the first pass
57649 if(Roo.isIE && !this.initialized){
57650 this.initialized = true;
57651 this.layout.layout();
57656 // activate all subpanels if not currently active..
57658 setActiveState : function(active){
57659 this.active = active;
57661 this.fireEvent("deactivate", this);
57665 this.fireEvent("activate", this);
57666 // not sure if this should happen before or after..
57667 if (!this.layout) {
57668 return; // should not happen..
57671 for (var r in this.layout.regions) {
57672 reg = this.layout.getRegion(r);
57673 if (reg.getActivePanel()) {
57674 //reg.showPanel(reg.getActivePanel()); // force it to activate..
57675 reg.setActivePanel(reg.getActivePanel());
57678 if (!reg.panels.length) {
57681 reg.showPanel(reg.getPanel(0));
57690 * Returns the nested BorderLayout for this panel
57691 * @return {Roo.BorderLayout}
57693 getLayout : function(){
57694 return this.layout;
57698 * Adds a xtype elements to the layout of the nested panel
57702 xtype : 'ContentPanel',
57709 xtype : 'NestedLayoutPanel',
57715 items : [ ... list of content panels or nested layout panels.. ]
57719 * @param {Object} cfg Xtype definition of item to add.
57721 addxtype : function(cfg) {
57722 return this.layout.addxtype(cfg);
57727 Roo.ScrollPanel = function(el, config, content){
57728 config = config || {};
57729 config.fitToFrame = true;
57730 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57732 this.el.dom.style.overflow = "hidden";
57733 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57734 this.el.removeClass("x-layout-inactive-content");
57735 this.el.on("mousewheel", this.onWheel, this);
57737 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
57738 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
57739 up.unselectable(); down.unselectable();
57740 up.on("click", this.scrollUp, this);
57741 down.on("click", this.scrollDown, this);
57742 up.addClassOnOver("x-scroller-btn-over");
57743 down.addClassOnOver("x-scroller-btn-over");
57744 up.addClassOnClick("x-scroller-btn-click");
57745 down.addClassOnClick("x-scroller-btn-click");
57746 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57748 this.resizeEl = this.el;
57749 this.el = wrap; this.up = up; this.down = down;
57752 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57754 wheelIncrement : 5,
57755 scrollUp : function(){
57756 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57759 scrollDown : function(){
57760 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57763 afterScroll : function(){
57764 var el = this.resizeEl;
57765 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57766 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57767 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57770 setSize : function(){
57771 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57772 this.afterScroll();
57775 onWheel : function(e){
57776 var d = e.getWheelDelta();
57777 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57778 this.afterScroll();
57782 setContent : function(content, loadScripts){
57783 this.resizeEl.update(content, loadScripts);
57791 * @class Roo.TreePanel
57792 * @extends Roo.ContentPanel
57793 * @parent Roo.BorderLayout Roo.LayoutDialog builder
57794 * Treepanel component
57797 * Create a new TreePanel. - defaults to fit/scoll contents.
57798 * @param {String/Object} config A string to set only the panel's title, or a config object
57800 Roo.TreePanel = function(config){
57801 var el = config.el;
57802 var tree = config.tree;
57803 delete config.tree;
57804 delete config.el; // hopefull!
57806 // wrapper for IE7 strict & safari scroll issue
57808 var treeEl = el.createChild();
57809 config.resizeEl = treeEl;
57813 Roo.TreePanel.superclass.constructor.call(this, el, config);
57816 this.tree = new Roo.tree.TreePanel(treeEl , tree);
57817 //console.log(tree);
57818 this.on('activate', function()
57820 if (this.tree.rendered) {
57823 //console.log('render tree');
57824 this.tree.render();
57826 // this should not be needed.. - it's actually the 'el' that resizes?
57827 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57829 //this.on('resize', function (cp, w, h) {
57830 // this.tree.innerCt.setWidth(w);
57831 // this.tree.innerCt.setHeight(h);
57832 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
57839 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
57843 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57861 * Ext JS Library 1.1.1
57862 * Copyright(c) 2006-2007, Ext JS, LLC.
57864 * Originally Released Under LGPL - original licence link has changed is not relivant.
57867 * <script type="text/javascript">
57872 * @class Roo.ReaderLayout
57873 * @extends Roo.BorderLayout
57874 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
57875 * center region containing two nested regions (a top one for a list view and one for item preview below),
57876 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57877 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57878 * expedites the setup of the overall layout and regions for this common application style.
57881 var reader = new Roo.ReaderLayout();
57882 var CP = Roo.ContentPanel; // shortcut for adding
57884 reader.beginUpdate();
57885 reader.add("north", new CP("north", "North"));
57886 reader.add("west", new CP("west", {title: "West"}));
57887 reader.add("east", new CP("east", {title: "East"}));
57889 reader.regions.listView.add(new CP("listView", "List"));
57890 reader.regions.preview.add(new CP("preview", "Preview"));
57891 reader.endUpdate();
57894 * Create a new ReaderLayout
57895 * @param {Object} config Configuration options
57896 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57897 * document.body if omitted)
57899 Roo.ReaderLayout = function(config, renderTo){
57900 var c = config || {size:{}};
57901 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57902 north: c.north !== false ? Roo.apply({
57906 }, c.north) : false,
57907 west: c.west !== false ? Roo.apply({
57915 margins:{left:5,right:0,bottom:5,top:5},
57916 cmargins:{left:5,right:5,bottom:5,top:5}
57917 }, c.west) : false,
57918 east: c.east !== false ? Roo.apply({
57926 margins:{left:0,right:5,bottom:5,top:5},
57927 cmargins:{left:5,right:5,bottom:5,top:5}
57928 }, c.east) : false,
57929 center: Roo.apply({
57930 tabPosition: 'top',
57934 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57938 this.el.addClass('x-reader');
57940 this.beginUpdate();
57942 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57943 south: c.preview !== false ? Roo.apply({
57950 cmargins:{top:5,left:0, right:0, bottom:0}
57951 }, c.preview) : false,
57952 center: Roo.apply({
57958 this.add('center', new Roo.NestedLayoutPanel(inner,
57959 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57963 this.regions.preview = inner.getRegion('south');
57964 this.regions.listView = inner.getRegion('center');
57967 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57969 * Ext JS Library 1.1.1
57970 * Copyright(c) 2006-2007, Ext JS, LLC.
57972 * Originally Released Under LGPL - original licence link has changed is not relivant.
57975 * <script type="text/javascript">
57979 * @class Roo.grid.Grid
57980 * @extends Roo.util.Observable
57981 * This class represents the primary interface of a component based grid control.
57982 * <br><br>Usage:<pre><code>
57983 var grid = new Roo.grid.Grid("my-container-id", {
57986 selModel: mySelectionModel,
57987 autoSizeColumns: true,
57988 monitorWindowResize: false,
57989 trackMouseOver: true
57994 * <b>Common Problems:</b><br/>
57995 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57996 * element will correct this<br/>
57997 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57998 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57999 * are unpredictable.<br/>
58000 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
58001 * grid to calculate dimensions/offsets.<br/>
58003 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58004 * The container MUST have some type of size defined for the grid to fill. The container will be
58005 * automatically set to position relative if it isn't already.
58006 * @param {Object} config A config object that sets properties on this grid.
58008 Roo.grid.Grid = function(container, config){
58009 // initialize the container
58010 this.container = Roo.get(container);
58011 this.container.update("");
58012 this.container.setStyle("overflow", "hidden");
58013 this.container.addClass('x-grid-container');
58015 this.id = this.container.id;
58017 Roo.apply(this, config);
58018 // check and correct shorthanded configs
58020 this.dataSource = this.ds;
58024 this.colModel = this.cm;
58028 this.selModel = this.sm;
58032 if (this.selModel) {
58033 this.selModel = Roo.factory(this.selModel, Roo.grid);
58034 this.sm = this.selModel;
58035 this.sm.xmodule = this.xmodule || false;
58037 if (typeof(this.colModel.config) == 'undefined') {
58038 this.colModel = new Roo.grid.ColumnModel(this.colModel);
58039 this.cm = this.colModel;
58040 this.cm.xmodule = this.xmodule || false;
58042 if (this.dataSource) {
58043 this.dataSource= Roo.factory(this.dataSource, Roo.data);
58044 this.ds = this.dataSource;
58045 this.ds.xmodule = this.xmodule || false;
58052 this.container.setWidth(this.width);
58056 this.container.setHeight(this.height);
58063 * The raw click event for the entire grid.
58064 * @param {Roo.EventObject} e
58069 * The raw dblclick event for the entire grid.
58070 * @param {Roo.EventObject} e
58074 * @event contextmenu
58075 * The raw contextmenu event for the entire grid.
58076 * @param {Roo.EventObject} e
58078 "contextmenu" : true,
58081 * The raw mousedown event for the entire grid.
58082 * @param {Roo.EventObject} e
58084 "mousedown" : true,
58087 * The raw mouseup event for the entire grid.
58088 * @param {Roo.EventObject} e
58093 * The raw mouseover event for the entire grid.
58094 * @param {Roo.EventObject} e
58096 "mouseover" : true,
58099 * The raw mouseout event for the entire grid.
58100 * @param {Roo.EventObject} e
58105 * The raw keypress event for the entire grid.
58106 * @param {Roo.EventObject} e
58111 * The raw keydown event for the entire grid.
58112 * @param {Roo.EventObject} e
58120 * Fires when a cell is clicked
58121 * @param {Grid} this
58122 * @param {Number} rowIndex
58123 * @param {Number} columnIndex
58124 * @param {Roo.EventObject} e
58126 "cellclick" : true,
58128 * @event celldblclick
58129 * Fires when a cell is double clicked
58130 * @param {Grid} this
58131 * @param {Number} rowIndex
58132 * @param {Number} columnIndex
58133 * @param {Roo.EventObject} e
58135 "celldblclick" : true,
58138 * Fires when a row is clicked
58139 * @param {Grid} this
58140 * @param {Number} rowIndex
58141 * @param {Roo.EventObject} e
58145 * @event rowdblclick
58146 * Fires when a row is double clicked
58147 * @param {Grid} this
58148 * @param {Number} rowIndex
58149 * @param {Roo.EventObject} e
58151 "rowdblclick" : true,
58153 * @event headerclick
58154 * Fires when a header is clicked
58155 * @param {Grid} this
58156 * @param {Number} columnIndex
58157 * @param {Roo.EventObject} e
58159 "headerclick" : true,
58161 * @event headerdblclick
58162 * Fires when a header cell is double clicked
58163 * @param {Grid} this
58164 * @param {Number} columnIndex
58165 * @param {Roo.EventObject} e
58167 "headerdblclick" : true,
58169 * @event rowcontextmenu
58170 * Fires when a row is right clicked
58171 * @param {Grid} this
58172 * @param {Number} rowIndex
58173 * @param {Roo.EventObject} e
58175 "rowcontextmenu" : true,
58177 * @event cellcontextmenu
58178 * Fires when a cell is right clicked
58179 * @param {Grid} this
58180 * @param {Number} rowIndex
58181 * @param {Number} cellIndex
58182 * @param {Roo.EventObject} e
58184 "cellcontextmenu" : true,
58186 * @event headercontextmenu
58187 * Fires when a header is right clicked
58188 * @param {Grid} this
58189 * @param {Number} columnIndex
58190 * @param {Roo.EventObject} e
58192 "headercontextmenu" : true,
58194 * @event bodyscroll
58195 * Fires when the body element is scrolled
58196 * @param {Number} scrollLeft
58197 * @param {Number} scrollTop
58199 "bodyscroll" : true,
58201 * @event columnresize
58202 * Fires when the user resizes a column
58203 * @param {Number} columnIndex
58204 * @param {Number} newSize
58206 "columnresize" : true,
58208 * @event columnmove
58209 * Fires when the user moves a column
58210 * @param {Number} oldIndex
58211 * @param {Number} newIndex
58213 "columnmove" : true,
58216 * Fires when row(s) start being dragged
58217 * @param {Grid} this
58218 * @param {Roo.GridDD} dd The drag drop object
58219 * @param {event} e The raw browser event
58221 "startdrag" : true,
58224 * Fires when a drag operation is complete
58225 * @param {Grid} this
58226 * @param {Roo.GridDD} dd The drag drop object
58227 * @param {event} e The raw browser event
58232 * Fires when dragged row(s) are dropped on a valid DD target
58233 * @param {Grid} this
58234 * @param {Roo.GridDD} dd The drag drop object
58235 * @param {String} targetId The target drag drop object
58236 * @param {event} e The raw browser event
58241 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58242 * @param {Grid} this
58243 * @param {Roo.GridDD} dd The drag drop object
58244 * @param {String} targetId The target drag drop object
58245 * @param {event} e The raw browser event
58250 * Fires when the dragged row(s) first cross another DD target while being dragged
58251 * @param {Grid} this
58252 * @param {Roo.GridDD} dd The drag drop object
58253 * @param {String} targetId The target drag drop object
58254 * @param {event} e The raw browser event
58256 "dragenter" : true,
58259 * Fires when the dragged row(s) leave another DD target while being dragged
58260 * @param {Grid} this
58261 * @param {Roo.GridDD} dd The drag drop object
58262 * @param {String} targetId The target drag drop object
58263 * @param {event} e The raw browser event
58268 * Fires when a row is rendered, so you can change add a style to it.
58269 * @param {GridView} gridview The grid view
58270 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
58276 * Fires when the grid is rendered
58277 * @param {Grid} grid
58282 Roo.grid.Grid.superclass.constructor.call(this);
58284 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58287 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58290 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
58293 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58296 * @cfg {Roo.grid.Store} ds The data store for the grid
58299 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58302 * @cfg {String} ddGroup - drag drop group.
58305 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58309 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58311 minColumnWidth : 25,
58314 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58315 * <b>on initial render.</b> It is more efficient to explicitly size the columns
58316 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
58318 autoSizeColumns : false,
58321 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58323 autoSizeHeaders : true,
58326 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58328 monitorWindowResize : true,
58331 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58332 * rows measured to get a columns size. Default is 0 (all rows).
58334 maxRowsToMeasure : 0,
58337 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58339 trackMouseOver : true,
58342 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
58345 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
58349 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58351 enableDragDrop : false,
58354 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58356 enableColumnMove : true,
58359 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58361 enableColumnHide : true,
58364 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58366 enableRowHeightSync : false,
58369 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
58374 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58376 autoHeight : false,
58379 * @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.
58381 autoExpandColumn : false,
58384 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58387 autoExpandMin : 50,
58390 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58392 autoExpandMax : 1000,
58395 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58400 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58404 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58408 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58410 sortColMenu : false,
58416 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58417 * of a fixed width. Default is false.
58420 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58425 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58426 * %0 is replaced with the number of selected rows.
58428 ddText : "{0} selected row{1}",
58432 * Called once after all setup has been completed and the grid is ready to be rendered.
58433 * @return {Roo.grid.Grid} this
58435 render : function()
58437 var c = this.container;
58438 // try to detect autoHeight/width mode
58439 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58440 this.autoHeight = true;
58442 var view = this.getView();
58445 c.on("click", this.onClick, this);
58446 c.on("dblclick", this.onDblClick, this);
58447 c.on("contextmenu", this.onContextMenu, this);
58448 c.on("keydown", this.onKeyDown, this);
58450 c.on("touchstart", this.onTouchStart, this);
58453 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58455 this.getSelectionModel().init(this);
58460 this.loadMask = new Roo.LoadMask(this.container,
58461 Roo.apply({store:this.dataSource}, this.loadMask));
58465 if (this.toolbar && this.toolbar.xtype) {
58466 this.toolbar.container = this.getView().getHeaderPanel(true);
58467 this.toolbar = new Roo.Toolbar(this.toolbar);
58469 if (this.footer && this.footer.xtype) {
58470 this.footer.dataSource = this.getDataSource();
58471 this.footer.container = this.getView().getFooterPanel(true);
58472 this.footer = Roo.factory(this.footer, Roo);
58474 if (this.dropTarget && this.dropTarget.xtype) {
58475 delete this.dropTarget.xtype;
58476 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58480 this.rendered = true;
58481 this.fireEvent('render', this);
58486 * Reconfigures the grid to use a different Store and Column Model.
58487 * The View will be bound to the new objects and refreshed.
58488 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58489 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58491 reconfigure : function(dataSource, colModel){
58493 this.loadMask.destroy();
58494 this.loadMask = new Roo.LoadMask(this.container,
58495 Roo.apply({store:dataSource}, this.loadMask));
58497 this.view.bind(dataSource, colModel);
58498 this.dataSource = dataSource;
58499 this.colModel = colModel;
58500 this.view.refresh(true);
58504 * Add's a column, default at the end..
58506 * @param {int} position to add (default end)
58507 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
58509 addColumns : function(pos, ar)
58512 for (var i =0;i< ar.length;i++) {
58514 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58515 this.cm.lookup[cfg.id] = cfg;
58519 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58520 pos = this.cm.config.length; //this.cm.config.push(cfg);
58522 pos = Math.max(0,pos);
58525 this.cm.config.splice.apply(this.cm.config, ar);
58529 this.view.generateRules(this.cm);
58530 this.view.refresh(true);
58538 onKeyDown : function(e){
58539 this.fireEvent("keydown", e);
58543 * Destroy this grid.
58544 * @param {Boolean} removeEl True to remove the element
58546 destroy : function(removeEl, keepListeners){
58548 this.loadMask.destroy();
58550 var c = this.container;
58551 c.removeAllListeners();
58552 this.view.destroy();
58553 this.colModel.purgeListeners();
58554 if(!keepListeners){
58555 this.purgeListeners();
58558 if(removeEl === true){
58564 processEvent : function(name, e){
58565 // does this fire select???
58566 //Roo.log('grid:processEvent ' + name);
58568 if (name != 'touchstart' ) {
58569 this.fireEvent(name, e);
58572 var t = e.getTarget();
58574 var header = v.findHeaderIndex(t);
58575 if(header !== false){
58576 var ename = name == 'touchstart' ? 'click' : name;
58578 this.fireEvent("header" + ename, this, header, e);
58580 var row = v.findRowIndex(t);
58581 var cell = v.findCellIndex(t);
58582 if (name == 'touchstart') {
58583 // first touch is always a click.
58584 // hopefull this happens after selection is updated.?
58587 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58588 var cs = this.selModel.getSelectedCell();
58589 if (row == cs[0] && cell == cs[1]){
58593 if (typeof(this.selModel.getSelections) != 'undefined') {
58594 var cs = this.selModel.getSelections();
58595 var ds = this.dataSource;
58596 if (cs.length == 1 && ds.getAt(row) == cs[0]){
58607 this.fireEvent("row" + name, this, row, e);
58608 if(cell !== false){
58609 this.fireEvent("cell" + name, this, row, cell, e);
58616 onClick : function(e){
58617 this.processEvent("click", e);
58620 onTouchStart : function(e){
58621 this.processEvent("touchstart", e);
58625 onContextMenu : function(e, t){
58626 this.processEvent("contextmenu", e);
58630 onDblClick : function(e){
58631 this.processEvent("dblclick", e);
58635 walkCells : function(row, col, step, fn, scope){
58636 var cm = this.colModel, clen = cm.getColumnCount();
58637 var ds = this.dataSource, rlen = ds.getCount(), first = true;
58649 if(fn.call(scope || this, row, col, cm) === true){
58667 if(fn.call(scope || this, row, col, cm) === true){
58679 getSelections : function(){
58680 return this.selModel.getSelections();
58684 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58685 * but if manual update is required this method will initiate it.
58687 autoSize : function(){
58689 this.view.layout();
58690 if(this.view.adjustForScroll){
58691 this.view.adjustForScroll();
58697 * Returns the grid's underlying element.
58698 * @return {Element} The element
58700 getGridEl : function(){
58701 return this.container;
58704 // private for compatibility, overridden by editor grid
58705 stopEditing : function(){},
58708 * Returns the grid's SelectionModel.
58709 * @return {SelectionModel}
58711 getSelectionModel : function(){
58712 if(!this.selModel){
58713 this.selModel = new Roo.grid.RowSelectionModel();
58715 return this.selModel;
58719 * Returns the grid's DataSource.
58720 * @return {DataSource}
58722 getDataSource : function(){
58723 return this.dataSource;
58727 * Returns the grid's ColumnModel.
58728 * @return {ColumnModel}
58730 getColumnModel : function(){
58731 return this.colModel;
58735 * Returns the grid's GridView object.
58736 * @return {GridView}
58738 getView : function(){
58740 this.view = new Roo.grid.GridView(this.viewConfig);
58741 this.relayEvents(this.view, [
58742 "beforerowremoved", "beforerowsinserted",
58743 "beforerefresh", "rowremoved",
58744 "rowsinserted", "rowupdated" ,"refresh"
58750 * Called to get grid's drag proxy text, by default returns this.ddText.
58751 * Override this to put something different in the dragged text.
58754 getDragDropText : function(){
58755 var count = this.selModel.getCount();
58756 return String.format(this.ddText, count, count == 1 ? '' : 's');
58761 * Ext JS Library 1.1.1
58762 * Copyright(c) 2006-2007, Ext JS, LLC.
58764 * Originally Released Under LGPL - original licence link has changed is not relivant.
58767 * <script type="text/javascript">
58770 * @class Roo.grid.AbstractGridView
58771 * @extends Roo.util.Observable
58773 * Abstract base class for grid Views
58776 Roo.grid.AbstractGridView = function(){
58780 "beforerowremoved" : true,
58781 "beforerowsinserted" : true,
58782 "beforerefresh" : true,
58783 "rowremoved" : true,
58784 "rowsinserted" : true,
58785 "rowupdated" : true,
58788 Roo.grid.AbstractGridView.superclass.constructor.call(this);
58791 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58792 rowClass : "x-grid-row",
58793 cellClass : "x-grid-cell",
58794 tdClass : "x-grid-td",
58795 hdClass : "x-grid-hd",
58796 splitClass : "x-grid-hd-split",
58798 init: function(grid){
58800 var cid = this.grid.getGridEl().id;
58801 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58802 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58803 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58804 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58807 getColumnRenderers : function(){
58808 var renderers = [];
58809 var cm = this.grid.colModel;
58810 var colCount = cm.getColumnCount();
58811 for(var i = 0; i < colCount; i++){
58812 renderers[i] = cm.getRenderer(i);
58817 getColumnIds : function(){
58819 var cm = this.grid.colModel;
58820 var colCount = cm.getColumnCount();
58821 for(var i = 0; i < colCount; i++){
58822 ids[i] = cm.getColumnId(i);
58827 getDataIndexes : function(){
58828 if(!this.indexMap){
58829 this.indexMap = this.buildIndexMap();
58831 return this.indexMap.colToData;
58834 getColumnIndexByDataIndex : function(dataIndex){
58835 if(!this.indexMap){
58836 this.indexMap = this.buildIndexMap();
58838 return this.indexMap.dataToCol[dataIndex];
58842 * Set a css style for a column dynamically.
58843 * @param {Number} colIndex The index of the column
58844 * @param {String} name The css property name
58845 * @param {String} value The css value
58847 setCSSStyle : function(colIndex, name, value){
58848 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58849 Roo.util.CSS.updateRule(selector, name, value);
58852 generateRules : function(cm){
58853 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58854 Roo.util.CSS.removeStyleSheet(rulesId);
58855 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58856 var cid = cm.getColumnId(i);
58857 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58858 this.tdSelector, cid, " {\n}\n",
58859 this.hdSelector, cid, " {\n}\n",
58860 this.splitSelector, cid, " {\n}\n");
58862 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58866 * Ext JS Library 1.1.1
58867 * Copyright(c) 2006-2007, Ext JS, LLC.
58869 * Originally Released Under LGPL - original licence link has changed is not relivant.
58872 * <script type="text/javascript">
58876 // This is a support class used internally by the Grid components
58877 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58879 this.view = grid.getView();
58880 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58881 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58883 this.setHandleElId(Roo.id(hd));
58884 this.setOuterHandleElId(Roo.id(hd2));
58886 this.scroll = false;
58888 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58890 getDragData : function(e){
58891 var t = Roo.lib.Event.getTarget(e);
58892 var h = this.view.findHeaderCell(t);
58894 return {ddel: h.firstChild, header:h};
58899 onInitDrag : function(e){
58900 this.view.headersDisabled = true;
58901 var clone = this.dragData.ddel.cloneNode(true);
58902 clone.id = Roo.id();
58903 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58904 this.proxy.update(clone);
58908 afterValidDrop : function(){
58910 setTimeout(function(){
58911 v.headersDisabled = false;
58915 afterInvalidDrop : function(){
58917 setTimeout(function(){
58918 v.headersDisabled = false;
58924 * Ext JS Library 1.1.1
58925 * Copyright(c) 2006-2007, Ext JS, LLC.
58927 * Originally Released Under LGPL - original licence link has changed is not relivant.
58930 * <script type="text/javascript">
58933 // This is a support class used internally by the Grid components
58934 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58936 this.view = grid.getView();
58937 // split the proxies so they don't interfere with mouse events
58938 this.proxyTop = Roo.DomHelper.append(document.body, {
58939 cls:"col-move-top", html:" "
58941 this.proxyBottom = Roo.DomHelper.append(document.body, {
58942 cls:"col-move-bottom", html:" "
58944 this.proxyTop.hide = this.proxyBottom.hide = function(){
58945 this.setLeftTop(-100,-100);
58946 this.setStyle("visibility", "hidden");
58948 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58949 // temporarily disabled
58950 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58951 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58953 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58954 proxyOffsets : [-4, -9],
58955 fly: Roo.Element.fly,
58957 getTargetFromEvent : function(e){
58958 var t = Roo.lib.Event.getTarget(e);
58959 var cindex = this.view.findCellIndex(t);
58960 if(cindex !== false){
58961 return this.view.getHeaderCell(cindex);
58966 nextVisible : function(h){
58967 var v = this.view, cm = this.grid.colModel;
58970 if(!cm.isHidden(v.getCellIndex(h))){
58978 prevVisible : function(h){
58979 var v = this.view, cm = this.grid.colModel;
58982 if(!cm.isHidden(v.getCellIndex(h))){
58990 positionIndicator : function(h, n, e){
58991 var x = Roo.lib.Event.getPageX(e);
58992 var r = Roo.lib.Dom.getRegion(n.firstChild);
58993 var px, pt, py = r.top + this.proxyOffsets[1];
58994 if((r.right - x) <= (r.right-r.left)/2){
58995 px = r.right+this.view.borderWidth;
59001 var oldIndex = this.view.getCellIndex(h);
59002 var newIndex = this.view.getCellIndex(n);
59004 if(this.grid.colModel.isFixed(newIndex)){
59008 var locked = this.grid.colModel.isLocked(newIndex);
59013 if(oldIndex < newIndex){
59016 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59019 px += this.proxyOffsets[0];
59020 this.proxyTop.setLeftTop(px, py);
59021 this.proxyTop.show();
59022 if(!this.bottomOffset){
59023 this.bottomOffset = this.view.mainHd.getHeight();
59025 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59026 this.proxyBottom.show();
59030 onNodeEnter : function(n, dd, e, data){
59031 if(data.header != n){
59032 this.positionIndicator(data.header, n, e);
59036 onNodeOver : function(n, dd, e, data){
59037 var result = false;
59038 if(data.header != n){
59039 result = this.positionIndicator(data.header, n, e);
59042 this.proxyTop.hide();
59043 this.proxyBottom.hide();
59045 return result ? this.dropAllowed : this.dropNotAllowed;
59048 onNodeOut : function(n, dd, e, data){
59049 this.proxyTop.hide();
59050 this.proxyBottom.hide();
59053 onNodeDrop : function(n, dd, e, data){
59054 var h = data.header;
59056 var cm = this.grid.colModel;
59057 var x = Roo.lib.Event.getPageX(e);
59058 var r = Roo.lib.Dom.getRegion(n.firstChild);
59059 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59060 var oldIndex = this.view.getCellIndex(h);
59061 var newIndex = this.view.getCellIndex(n);
59062 var locked = cm.isLocked(newIndex);
59066 if(oldIndex < newIndex){
59069 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59072 cm.setLocked(oldIndex, locked, true);
59073 cm.moveColumn(oldIndex, newIndex);
59074 this.grid.fireEvent("columnmove", oldIndex, newIndex);
59082 * Ext JS Library 1.1.1
59083 * Copyright(c) 2006-2007, Ext JS, LLC.
59085 * Originally Released Under LGPL - original licence link has changed is not relivant.
59088 * <script type="text/javascript">
59092 * @class Roo.grid.GridView
59093 * @extends Roo.util.Observable
59096 * @param {Object} config
59098 Roo.grid.GridView = function(config){
59099 Roo.grid.GridView.superclass.constructor.call(this);
59102 Roo.apply(this, config);
59105 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59107 unselectable : 'unselectable="on"',
59108 unselectableCls : 'x-unselectable',
59111 rowClass : "x-grid-row",
59113 cellClass : "x-grid-col",
59115 tdClass : "x-grid-td",
59117 hdClass : "x-grid-hd",
59119 splitClass : "x-grid-split",
59121 sortClasses : ["sort-asc", "sort-desc"],
59123 enableMoveAnim : false,
59127 dh : Roo.DomHelper,
59129 fly : Roo.Element.fly,
59131 css : Roo.util.CSS,
59137 scrollIncrement : 22,
59139 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59141 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59143 bind : function(ds, cm){
59145 this.ds.un("load", this.onLoad, this);
59146 this.ds.un("datachanged", this.onDataChange, this);
59147 this.ds.un("add", this.onAdd, this);
59148 this.ds.un("remove", this.onRemove, this);
59149 this.ds.un("update", this.onUpdate, this);
59150 this.ds.un("clear", this.onClear, this);
59153 ds.on("load", this.onLoad, this);
59154 ds.on("datachanged", this.onDataChange, this);
59155 ds.on("add", this.onAdd, this);
59156 ds.on("remove", this.onRemove, this);
59157 ds.on("update", this.onUpdate, this);
59158 ds.on("clear", this.onClear, this);
59163 this.cm.un("widthchange", this.onColWidthChange, this);
59164 this.cm.un("headerchange", this.onHeaderChange, this);
59165 this.cm.un("hiddenchange", this.onHiddenChange, this);
59166 this.cm.un("columnmoved", this.onColumnMove, this);
59167 this.cm.un("columnlockchange", this.onColumnLock, this);
59170 this.generateRules(cm);
59171 cm.on("widthchange", this.onColWidthChange, this);
59172 cm.on("headerchange", this.onHeaderChange, this);
59173 cm.on("hiddenchange", this.onHiddenChange, this);
59174 cm.on("columnmoved", this.onColumnMove, this);
59175 cm.on("columnlockchange", this.onColumnLock, this);
59180 init: function(grid){
59181 Roo.grid.GridView.superclass.init.call(this, grid);
59183 this.bind(grid.dataSource, grid.colModel);
59185 grid.on("headerclick", this.handleHeaderClick, this);
59187 if(grid.trackMouseOver){
59188 grid.on("mouseover", this.onRowOver, this);
59189 grid.on("mouseout", this.onRowOut, this);
59191 grid.cancelTextSelection = function(){};
59192 this.gridId = grid.id;
59194 var tpls = this.templates || {};
59197 tpls.master = new Roo.Template(
59198 '<div class="x-grid" hidefocus="true">',
59199 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59200 '<div class="x-grid-topbar"></div>',
59201 '<div class="x-grid-scroller"><div></div></div>',
59202 '<div class="x-grid-locked">',
59203 '<div class="x-grid-header">{lockedHeader}</div>',
59204 '<div class="x-grid-body">{lockedBody}</div>',
59206 '<div class="x-grid-viewport">',
59207 '<div class="x-grid-header">{header}</div>',
59208 '<div class="x-grid-body">{body}</div>',
59210 '<div class="x-grid-bottombar"></div>',
59212 '<div class="x-grid-resize-proxy"> </div>',
59215 tpls.master.disableformats = true;
59219 tpls.header = new Roo.Template(
59220 '<table border="0" cellspacing="0" cellpadding="0">',
59221 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59224 tpls.header.disableformats = true;
59226 tpls.header.compile();
59229 tpls.hcell = new Roo.Template(
59230 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59231 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59234 tpls.hcell.disableFormats = true;
59236 tpls.hcell.compile();
59239 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59240 this.unselectableCls + '" ' + this.unselectable +'> </div>');
59241 tpls.hsplit.disableFormats = true;
59243 tpls.hsplit.compile();
59246 tpls.body = new Roo.Template(
59247 '<table border="0" cellspacing="0" cellpadding="0">',
59248 "<tbody>{rows}</tbody>",
59251 tpls.body.disableFormats = true;
59253 tpls.body.compile();
59256 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59257 tpls.row.disableFormats = true;
59259 tpls.row.compile();
59262 tpls.cell = new Roo.Template(
59263 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59264 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59265 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59268 tpls.cell.disableFormats = true;
59270 tpls.cell.compile();
59272 this.templates = tpls;
59275 // remap these for backwards compat
59276 onColWidthChange : function(){
59277 this.updateColumns.apply(this, arguments);
59279 onHeaderChange : function(){
59280 this.updateHeaders.apply(this, arguments);
59282 onHiddenChange : function(){
59283 this.handleHiddenChange.apply(this, arguments);
59285 onColumnMove : function(){
59286 this.handleColumnMove.apply(this, arguments);
59288 onColumnLock : function(){
59289 this.handleLockChange.apply(this, arguments);
59292 onDataChange : function(){
59294 this.updateHeaderSortState();
59297 onClear : function(){
59301 onUpdate : function(ds, record){
59302 this.refreshRow(record);
59305 refreshRow : function(record){
59306 var ds = this.ds, index;
59307 if(typeof record == 'number'){
59309 record = ds.getAt(index);
59311 index = ds.indexOf(record);
59313 this.insertRows(ds, index, index, true);
59314 this.onRemove(ds, record, index+1, true);
59315 this.syncRowHeights(index, index);
59317 this.fireEvent("rowupdated", this, index, record);
59320 onAdd : function(ds, records, index){
59321 this.insertRows(ds, index, index + (records.length-1));
59324 onRemove : function(ds, record, index, isUpdate){
59325 if(isUpdate !== true){
59326 this.fireEvent("beforerowremoved", this, index, record);
59328 var bt = this.getBodyTable(), lt = this.getLockedTable();
59329 if(bt.rows[index]){
59330 bt.firstChild.removeChild(bt.rows[index]);
59332 if(lt.rows[index]){
59333 lt.firstChild.removeChild(lt.rows[index]);
59335 if(isUpdate !== true){
59336 this.stripeRows(index);
59337 this.syncRowHeights(index, index);
59339 this.fireEvent("rowremoved", this, index, record);
59343 onLoad : function(){
59344 this.scrollToTop();
59348 * Scrolls the grid to the top
59350 scrollToTop : function(){
59352 this.scroller.dom.scrollTop = 0;
59358 * Gets a panel in the header of the grid that can be used for toolbars etc.
59359 * After modifying the contents of this panel a call to grid.autoSize() may be
59360 * required to register any changes in size.
59361 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59362 * @return Roo.Element
59364 getHeaderPanel : function(doShow){
59366 this.headerPanel.show();
59368 return this.headerPanel;
59372 * Gets a panel in the footer of the grid that can be used for toolbars etc.
59373 * After modifying the contents of this panel a call to grid.autoSize() may be
59374 * required to register any changes in size.
59375 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59376 * @return Roo.Element
59378 getFooterPanel : function(doShow){
59380 this.footerPanel.show();
59382 return this.footerPanel;
59385 initElements : function(){
59386 var E = Roo.Element;
59387 var el = this.grid.getGridEl().dom.firstChild;
59388 var cs = el.childNodes;
59390 this.el = new E(el);
59392 this.focusEl = new E(el.firstChild);
59393 this.focusEl.swallowEvent("click", true);
59395 this.headerPanel = new E(cs[1]);
59396 this.headerPanel.enableDisplayMode("block");
59398 this.scroller = new E(cs[2]);
59399 this.scrollSizer = new E(this.scroller.dom.firstChild);
59401 this.lockedWrap = new E(cs[3]);
59402 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59403 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59405 this.mainWrap = new E(cs[4]);
59406 this.mainHd = new E(this.mainWrap.dom.firstChild);
59407 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59409 this.footerPanel = new E(cs[5]);
59410 this.footerPanel.enableDisplayMode("block");
59412 this.resizeProxy = new E(cs[6]);
59414 this.headerSelector = String.format(
59415 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59416 this.lockedHd.id, this.mainHd.id
59419 this.splitterSelector = String.format(
59420 '#{0} div.x-grid-split, #{1} div.x-grid-split',
59421 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59424 idToCssName : function(s)
59426 return s.replace(/[^a-z0-9]+/ig, '-');
59429 getHeaderCell : function(index){
59430 return Roo.DomQuery.select(this.headerSelector)[index];
59433 getHeaderCellMeasure : function(index){
59434 return this.getHeaderCell(index).firstChild;
59437 getHeaderCellText : function(index){
59438 return this.getHeaderCell(index).firstChild.firstChild;
59441 getLockedTable : function(){
59442 return this.lockedBody.dom.firstChild;
59445 getBodyTable : function(){
59446 return this.mainBody.dom.firstChild;
59449 getLockedRow : function(index){
59450 return this.getLockedTable().rows[index];
59453 getRow : function(index){
59454 return this.getBodyTable().rows[index];
59457 getRowComposite : function(index){
59459 this.rowEl = new Roo.CompositeElementLite();
59461 var els = [], lrow, mrow;
59462 if(lrow = this.getLockedRow(index)){
59465 if(mrow = this.getRow(index)){
59468 this.rowEl.elements = els;
59472 * Gets the 'td' of the cell
59474 * @param {Integer} rowIndex row to select
59475 * @param {Integer} colIndex column to select
59479 getCell : function(rowIndex, colIndex){
59480 var locked = this.cm.getLockedCount();
59482 if(colIndex < locked){
59483 source = this.lockedBody.dom.firstChild;
59485 source = this.mainBody.dom.firstChild;
59486 colIndex -= locked;
59488 return source.rows[rowIndex].childNodes[colIndex];
59491 getCellText : function(rowIndex, colIndex){
59492 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59495 getCellBox : function(cell){
59496 var b = this.fly(cell).getBox();
59497 if(Roo.isOpera){ // opera fails to report the Y
59498 b.y = cell.offsetTop + this.mainBody.getY();
59503 getCellIndex : function(cell){
59504 var id = String(cell.className).match(this.cellRE);
59506 return parseInt(id[1], 10);
59511 findHeaderIndex : function(n){
59512 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59513 return r ? this.getCellIndex(r) : false;
59516 findHeaderCell : function(n){
59517 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59518 return r ? r : false;
59521 findRowIndex : function(n){
59525 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59526 return r ? r.rowIndex : false;
59529 findCellIndex : function(node){
59530 var stop = this.el.dom;
59531 while(node && node != stop){
59532 if(this.findRE.test(node.className)){
59533 return this.getCellIndex(node);
59535 node = node.parentNode;
59540 getColumnId : function(index){
59541 return this.cm.getColumnId(index);
59544 getSplitters : function()
59546 if(this.splitterSelector){
59547 return Roo.DomQuery.select(this.splitterSelector);
59553 getSplitter : function(index){
59554 return this.getSplitters()[index];
59557 onRowOver : function(e, t){
59559 if((row = this.findRowIndex(t)) !== false){
59560 this.getRowComposite(row).addClass("x-grid-row-over");
59564 onRowOut : function(e, t){
59566 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59567 this.getRowComposite(row).removeClass("x-grid-row-over");
59571 renderHeaders : function(){
59573 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59574 var cb = [], lb = [], sb = [], lsb = [], p = {};
59575 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59576 p.cellId = "x-grid-hd-0-" + i;
59577 p.splitId = "x-grid-csplit-0-" + i;
59578 p.id = cm.getColumnId(i);
59579 p.value = cm.getColumnHeader(i) || "";
59580 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
59581 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59582 if(!cm.isLocked(i)){
59583 cb[cb.length] = ct.apply(p);
59584 sb[sb.length] = st.apply(p);
59586 lb[lb.length] = ct.apply(p);
59587 lsb[lsb.length] = st.apply(p);
59590 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59591 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59594 updateHeaders : function(){
59595 var html = this.renderHeaders();
59596 this.lockedHd.update(html[0]);
59597 this.mainHd.update(html[1]);
59601 * Focuses the specified row.
59602 * @param {Number} row The row index
59604 focusRow : function(row)
59606 //Roo.log('GridView.focusRow');
59607 var x = this.scroller.dom.scrollLeft;
59608 this.focusCell(row, 0, false);
59609 this.scroller.dom.scrollLeft = x;
59613 * Focuses the specified cell.
59614 * @param {Number} row The row index
59615 * @param {Number} col The column index
59616 * @param {Boolean} hscroll false to disable horizontal scrolling
59618 focusCell : function(row, col, hscroll)
59620 //Roo.log('GridView.focusCell');
59621 var el = this.ensureVisible(row, col, hscroll);
59622 this.focusEl.alignTo(el, "tl-tl");
59624 this.focusEl.focus();
59626 this.focusEl.focus.defer(1, this.focusEl);
59631 * Scrolls the specified cell into view
59632 * @param {Number} row The row index
59633 * @param {Number} col The column index
59634 * @param {Boolean} hscroll false to disable horizontal scrolling
59636 ensureVisible : function(row, col, hscroll)
59638 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59639 //return null; //disable for testing.
59640 if(typeof row != "number"){
59641 row = row.rowIndex;
59643 if(row < 0 && row >= this.ds.getCount()){
59646 col = (col !== undefined ? col : 0);
59647 var cm = this.grid.colModel;
59648 while(cm.isHidden(col)){
59652 var el = this.getCell(row, col);
59656 var c = this.scroller.dom;
59658 var ctop = parseInt(el.offsetTop, 10);
59659 var cleft = parseInt(el.offsetLeft, 10);
59660 var cbot = ctop + el.offsetHeight;
59661 var cright = cleft + el.offsetWidth;
59663 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59664 var stop = parseInt(c.scrollTop, 10);
59665 var sleft = parseInt(c.scrollLeft, 10);
59666 var sbot = stop + ch;
59667 var sright = sleft + c.clientWidth;
59669 Roo.log('GridView.ensureVisible:' +
59671 ' c.clientHeight:' + c.clientHeight +
59672 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59680 c.scrollTop = ctop;
59681 //Roo.log("set scrolltop to ctop DISABLE?");
59682 }else if(cbot > sbot){
59683 //Roo.log("set scrolltop to cbot-ch");
59684 c.scrollTop = cbot-ch;
59687 if(hscroll !== false){
59689 c.scrollLeft = cleft;
59690 }else if(cright > sright){
59691 c.scrollLeft = cright-c.clientWidth;
59698 updateColumns : function(){
59699 this.grid.stopEditing();
59700 var cm = this.grid.colModel, colIds = this.getColumnIds();
59701 //var totalWidth = cm.getTotalWidth();
59703 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59704 //if(cm.isHidden(i)) continue;
59705 var w = cm.getColumnWidth(i);
59706 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59707 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59709 this.updateSplitters();
59712 generateRules : function(cm){
59713 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59714 Roo.util.CSS.removeStyleSheet(rulesId);
59715 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59716 var cid = cm.getColumnId(i);
59718 if(cm.config[i].align){
59719 align = 'text-align:'+cm.config[i].align+';';
59722 if(cm.isHidden(i)){
59723 hidden = 'display:none;';
59725 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59727 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59728 this.hdSelector, cid, " {\n", align, width, "}\n",
59729 this.tdSelector, cid, " {\n",hidden,"\n}\n",
59730 this.splitSelector, cid, " {\n", hidden , "\n}\n");
59732 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59735 updateSplitters : function(){
59736 var cm = this.cm, s = this.getSplitters();
59737 if(s){ // splitters not created yet
59738 var pos = 0, locked = true;
59739 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59740 if(cm.isHidden(i)) {
59743 var w = cm.getColumnWidth(i); // make sure it's a number
59744 if(!cm.isLocked(i) && locked){
59749 s[i].style.left = (pos-this.splitOffset) + "px";
59754 handleHiddenChange : function(colModel, colIndex, hidden){
59756 this.hideColumn(colIndex);
59758 this.unhideColumn(colIndex);
59762 hideColumn : function(colIndex){
59763 var cid = this.getColumnId(colIndex);
59764 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59765 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59767 this.updateHeaders();
59769 this.updateSplitters();
59773 unhideColumn : function(colIndex){
59774 var cid = this.getColumnId(colIndex);
59775 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59776 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59779 this.updateHeaders();
59781 this.updateSplitters();
59785 insertRows : function(dm, firstRow, lastRow, isUpdate){
59786 if(firstRow == 0 && lastRow == dm.getCount()-1){
59790 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59792 var s = this.getScrollState();
59793 var markup = this.renderRows(firstRow, lastRow);
59794 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59795 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59796 this.restoreScroll(s);
59798 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59799 this.syncRowHeights(firstRow, lastRow);
59800 this.stripeRows(firstRow);
59806 bufferRows : function(markup, target, index){
59807 var before = null, trows = target.rows, tbody = target.tBodies[0];
59808 if(index < trows.length){
59809 before = trows[index];
59811 var b = document.createElement("div");
59812 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59813 var rows = b.firstChild.rows;
59814 for(var i = 0, len = rows.length; i < len; i++){
59816 tbody.insertBefore(rows[0], before);
59818 tbody.appendChild(rows[0]);
59825 deleteRows : function(dm, firstRow, lastRow){
59826 if(dm.getRowCount()<1){
59827 this.fireEvent("beforerefresh", this);
59828 this.mainBody.update("");
59829 this.lockedBody.update("");
59830 this.fireEvent("refresh", this);
59832 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59833 var bt = this.getBodyTable();
59834 var tbody = bt.firstChild;
59835 var rows = bt.rows;
59836 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59837 tbody.removeChild(rows[firstRow]);
59839 this.stripeRows(firstRow);
59840 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59844 updateRows : function(dataSource, firstRow, lastRow){
59845 var s = this.getScrollState();
59847 this.restoreScroll(s);
59850 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59854 this.updateHeaderSortState();
59857 getScrollState : function(){
59859 var sb = this.scroller.dom;
59860 return {left: sb.scrollLeft, top: sb.scrollTop};
59863 stripeRows : function(startRow){
59864 if(!this.grid.stripeRows || this.ds.getCount() < 1){
59867 startRow = startRow || 0;
59868 var rows = this.getBodyTable().rows;
59869 var lrows = this.getLockedTable().rows;
59870 var cls = ' x-grid-row-alt ';
59871 for(var i = startRow, len = rows.length; i < len; i++){
59872 var row = rows[i], lrow = lrows[i];
59873 var isAlt = ((i+1) % 2 == 0);
59874 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59875 if(isAlt == hasAlt){
59879 row.className += " x-grid-row-alt";
59881 row.className = row.className.replace("x-grid-row-alt", "");
59884 lrow.className = row.className;
59889 restoreScroll : function(state){
59890 //Roo.log('GridView.restoreScroll');
59891 var sb = this.scroller.dom;
59892 sb.scrollLeft = state.left;
59893 sb.scrollTop = state.top;
59897 syncScroll : function(){
59898 //Roo.log('GridView.syncScroll');
59899 var sb = this.scroller.dom;
59900 var sh = this.mainHd.dom;
59901 var bs = this.mainBody.dom;
59902 var lv = this.lockedBody.dom;
59903 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59904 lv.scrollTop = bs.scrollTop = sb.scrollTop;
59907 handleScroll : function(e){
59909 var sb = this.scroller.dom;
59910 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59914 handleWheel : function(e){
59915 var d = e.getWheelDelta();
59916 this.scroller.dom.scrollTop -= d*22;
59917 // set this here to prevent jumpy scrolling on large tables
59918 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59922 renderRows : function(startRow, endRow){
59923 // pull in all the crap needed to render rows
59924 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59925 var colCount = cm.getColumnCount();
59927 if(ds.getCount() < 1){
59931 // build a map for all the columns
59933 for(var i = 0; i < colCount; i++){
59934 var name = cm.getDataIndex(i);
59936 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59937 renderer : cm.getRenderer(i),
59938 id : cm.getColumnId(i),
59939 locked : cm.isLocked(i),
59940 has_editor : cm.isCellEditable(i)
59944 startRow = startRow || 0;
59945 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59947 // records to render
59948 var rs = ds.getRange(startRow, endRow);
59950 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59953 // As much as I hate to duplicate code, this was branched because FireFox really hates
59954 // [].join("") on strings. The performance difference was substantial enough to
59955 // branch this function
59956 doRender : Roo.isGecko ?
59957 function(cs, rs, ds, startRow, colCount, stripe){
59958 var ts = this.templates, ct = ts.cell, rt = ts.row;
59960 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59962 var hasListener = this.grid.hasListener('rowclass');
59964 for(var j = 0, len = rs.length; j < len; j++){
59965 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59966 for(var i = 0; i < colCount; i++){
59968 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59970 p.css = p.attr = "";
59971 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59972 if(p.value == undefined || p.value === "") {
59973 p.value = " ";
59976 p.css += ' x-grid-editable-cell';
59978 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59979 p.css += ' x-grid-dirty-cell';
59981 var markup = ct.apply(p);
59989 if(stripe && ((rowIndex+1) % 2 == 0)){
59990 alt.push("x-grid-row-alt")
59993 alt.push( " x-grid-dirty-row");
59996 if(this.getRowClass){
59997 alt.push(this.getRowClass(r, rowIndex));
60003 rowIndex : rowIndex,
60006 this.grid.fireEvent('rowclass', this, rowcfg);
60007 alt.push(rowcfg.rowClass);
60009 rp.alt = alt.join(" ");
60010 lbuf+= rt.apply(rp);
60012 buf+= rt.apply(rp);
60014 return [lbuf, buf];
60016 function(cs, rs, ds, startRow, colCount, stripe){
60017 var ts = this.templates, ct = ts.cell, rt = ts.row;
60019 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60020 var hasListener = this.grid.hasListener('rowclass');
60023 for(var j = 0, len = rs.length; j < len; j++){
60024 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60025 for(var i = 0; i < colCount; i++){
60027 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60029 p.css = p.attr = "";
60030 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60031 if(p.value == undefined || p.value === "") {
60032 p.value = " ";
60036 p.css += ' x-grid-editable-cell';
60038 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60039 p.css += ' x-grid-dirty-cell'
60042 var markup = ct.apply(p);
60044 cb[cb.length] = markup;
60046 lcb[lcb.length] = markup;
60050 if(stripe && ((rowIndex+1) % 2 == 0)){
60051 alt.push( "x-grid-row-alt");
60054 alt.push(" x-grid-dirty-row");
60057 if(this.getRowClass){
60058 alt.push( this.getRowClass(r, rowIndex));
60064 rowIndex : rowIndex,
60067 this.grid.fireEvent('rowclass', this, rowcfg);
60068 alt.push(rowcfg.rowClass);
60071 rp.alt = alt.join(" ");
60072 rp.cells = lcb.join("");
60073 lbuf[lbuf.length] = rt.apply(rp);
60074 rp.cells = cb.join("");
60075 buf[buf.length] = rt.apply(rp);
60077 return [lbuf.join(""), buf.join("")];
60080 renderBody : function(){
60081 var markup = this.renderRows();
60082 var bt = this.templates.body;
60083 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60087 * Refreshes the grid
60088 * @param {Boolean} headersToo
60090 refresh : function(headersToo){
60091 this.fireEvent("beforerefresh", this);
60092 this.grid.stopEditing();
60093 var result = this.renderBody();
60094 this.lockedBody.update(result[0]);
60095 this.mainBody.update(result[1]);
60096 if(headersToo === true){
60097 this.updateHeaders();
60098 this.updateColumns();
60099 this.updateSplitters();
60100 this.updateHeaderSortState();
60102 this.syncRowHeights();
60104 this.fireEvent("refresh", this);
60107 handleColumnMove : function(cm, oldIndex, newIndex){
60108 this.indexMap = null;
60109 var s = this.getScrollState();
60110 this.refresh(true);
60111 this.restoreScroll(s);
60112 this.afterMove(newIndex);
60115 afterMove : function(colIndex){
60116 if(this.enableMoveAnim && Roo.enableFx){
60117 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60119 // if multisort - fix sortOrder, and reload..
60120 if (this.grid.dataSource.multiSort) {
60121 // the we can call sort again..
60122 var dm = this.grid.dataSource;
60123 var cm = this.grid.colModel;
60125 for(var i = 0; i < cm.config.length; i++ ) {
60127 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60128 continue; // dont' bother, it's not in sort list or being set.
60131 so.push(cm.config[i].dataIndex);
60134 dm.load(dm.lastOptions);
60141 updateCell : function(dm, rowIndex, dataIndex){
60142 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60143 if(typeof colIndex == "undefined"){ // not present in grid
60146 var cm = this.grid.colModel;
60147 var cell = this.getCell(rowIndex, colIndex);
60148 var cellText = this.getCellText(rowIndex, colIndex);
60151 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60152 id : cm.getColumnId(colIndex),
60153 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60155 var renderer = cm.getRenderer(colIndex);
60156 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60157 if(typeof val == "undefined" || val === "") {
60160 cellText.innerHTML = val;
60161 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60162 this.syncRowHeights(rowIndex, rowIndex);
60165 calcColumnWidth : function(colIndex, maxRowsToMeasure){
60167 if(this.grid.autoSizeHeaders){
60168 var h = this.getHeaderCellMeasure(colIndex);
60169 maxWidth = Math.max(maxWidth, h.scrollWidth);
60172 if(this.cm.isLocked(colIndex)){
60173 tb = this.getLockedTable();
60176 tb = this.getBodyTable();
60177 index = colIndex - this.cm.getLockedCount();
60180 var rows = tb.rows;
60181 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60182 for(var i = 0; i < stopIndex; i++){
60183 var cell = rows[i].childNodes[index].firstChild;
60184 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60187 return maxWidth + /*margin for error in IE*/ 5;
60190 * Autofit a column to its content.
60191 * @param {Number} colIndex
60192 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60194 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60195 if(this.cm.isHidden(colIndex)){
60196 return; // can't calc a hidden column
60199 var cid = this.cm.getColumnId(colIndex);
60200 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60201 if(this.grid.autoSizeHeaders){
60202 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60205 var newWidth = this.calcColumnWidth(colIndex);
60206 this.cm.setColumnWidth(colIndex,
60207 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60208 if(!suppressEvent){
60209 this.grid.fireEvent("columnresize", colIndex, newWidth);
60214 * Autofits all columns to their content and then expands to fit any extra space in the grid
60216 autoSizeColumns : function(){
60217 var cm = this.grid.colModel;
60218 var colCount = cm.getColumnCount();
60219 for(var i = 0; i < colCount; i++){
60220 this.autoSizeColumn(i, true, true);
60222 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60225 this.updateColumns();
60231 * Autofits all columns to the grid's width proportionate with their current size
60232 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60234 fitColumns : function(reserveScrollSpace){
60235 var cm = this.grid.colModel;
60236 var colCount = cm.getColumnCount();
60240 for (i = 0; i < colCount; i++){
60241 if(!cm.isHidden(i) && !cm.isFixed(i)){
60242 w = cm.getColumnWidth(i);
60248 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60249 if(reserveScrollSpace){
60252 var frac = (avail - cm.getTotalWidth())/width;
60253 while (cols.length){
60256 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60258 this.updateColumns();
60262 onRowSelect : function(rowIndex){
60263 var row = this.getRowComposite(rowIndex);
60264 row.addClass("x-grid-row-selected");
60267 onRowDeselect : function(rowIndex){
60268 var row = this.getRowComposite(rowIndex);
60269 row.removeClass("x-grid-row-selected");
60272 onCellSelect : function(row, col){
60273 var cell = this.getCell(row, col);
60275 Roo.fly(cell).addClass("x-grid-cell-selected");
60279 onCellDeselect : function(row, col){
60280 var cell = this.getCell(row, col);
60282 Roo.fly(cell).removeClass("x-grid-cell-selected");
60286 updateHeaderSortState : function(){
60288 // sort state can be single { field: xxx, direction : yyy}
60289 // or { xxx=>ASC , yyy : DESC ..... }
60292 if (!this.ds.multiSort) {
60293 var state = this.ds.getSortState();
60297 mstate[state.field] = state.direction;
60298 // FIXME... - this is not used here.. but might be elsewhere..
60299 this.sortState = state;
60302 mstate = this.ds.sortToggle;
60304 //remove existing sort classes..
60306 var sc = this.sortClasses;
60307 var hds = this.el.select(this.headerSelector).removeClass(sc);
60309 for(var f in mstate) {
60311 var sortColumn = this.cm.findColumnIndex(f);
60313 if(sortColumn != -1){
60314 var sortDir = mstate[f];
60315 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60324 handleHeaderClick : function(g, index,e){
60326 Roo.log("header click");
60329 // touch events on header are handled by context
60330 this.handleHdCtx(g,index,e);
60335 if(this.headersDisabled){
60338 var dm = g.dataSource, cm = g.colModel;
60339 if(!cm.isSortable(index)){
60344 if (dm.multiSort) {
60345 // update the sortOrder
60347 for(var i = 0; i < cm.config.length; i++ ) {
60349 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60350 continue; // dont' bother, it's not in sort list or being set.
60353 so.push(cm.config[i].dataIndex);
60359 dm.sort(cm.getDataIndex(index));
60363 destroy : function(){
60365 this.colMenu.removeAll();
60366 Roo.menu.MenuMgr.unregister(this.colMenu);
60367 this.colMenu.getEl().remove();
60368 delete this.colMenu;
60371 this.hmenu.removeAll();
60372 Roo.menu.MenuMgr.unregister(this.hmenu);
60373 this.hmenu.getEl().remove();
60376 if(this.grid.enableColumnMove){
60377 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60379 for(var dd in dds){
60380 if(!dds[dd].config.isTarget && dds[dd].dragElId){
60381 var elid = dds[dd].dragElId;
60383 Roo.get(elid).remove();
60384 } else if(dds[dd].config.isTarget){
60385 dds[dd].proxyTop.remove();
60386 dds[dd].proxyBottom.remove();
60389 if(Roo.dd.DDM.locationCache[dd]){
60390 delete Roo.dd.DDM.locationCache[dd];
60393 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60396 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60397 this.bind(null, null);
60398 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60401 handleLockChange : function(){
60402 this.refresh(true);
60405 onDenyColumnLock : function(){
60409 onDenyColumnHide : function(){
60413 handleHdMenuClick : function(item){
60414 var index = this.hdCtxIndex;
60415 var cm = this.cm, ds = this.ds;
60418 ds.sort(cm.getDataIndex(index), "ASC");
60421 ds.sort(cm.getDataIndex(index), "DESC");
60424 var lc = cm.getLockedCount();
60425 if(cm.getColumnCount(true) <= lc+1){
60426 this.onDenyColumnLock();
60430 cm.setLocked(index, true, true);
60431 cm.moveColumn(index, lc);
60432 this.grid.fireEvent("columnmove", index, lc);
60434 cm.setLocked(index, true);
60438 var lc = cm.getLockedCount();
60439 if((lc-1) != index){
60440 cm.setLocked(index, false, true);
60441 cm.moveColumn(index, lc-1);
60442 this.grid.fireEvent("columnmove", index, lc-1);
60444 cm.setLocked(index, false);
60447 case 'wider': // used to expand cols on touch..
60449 var cw = cm.getColumnWidth(index);
60450 cw += (item.id == 'wider' ? 1 : -1) * 50;
60451 cw = Math.max(0, cw);
60452 cw = Math.min(cw,4000);
60453 cm.setColumnWidth(index, cw);
60457 index = cm.getIndexById(item.id.substr(4));
60459 if(item.checked && cm.getColumnCount(true) <= 1){
60460 this.onDenyColumnHide();
60463 cm.setHidden(index, item.checked);
60469 beforeColMenuShow : function(){
60470 var cm = this.cm, colCount = cm.getColumnCount();
60471 this.colMenu.removeAll();
60474 for(var i = 0; i < colCount; i++){
60476 id: "col-"+cm.getColumnId(i),
60477 text: cm.getColumnHeader(i),
60478 checked: !cm.isHidden(i),
60483 if (this.grid.sortColMenu) {
60484 items.sort(function(a,b) {
60485 if (a.text == b.text) {
60488 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60492 for(var i = 0; i < colCount; i++){
60493 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60497 handleHdCtx : function(g, index, e){
60499 var hd = this.getHeaderCell(index);
60500 this.hdCtxIndex = index;
60501 var ms = this.hmenu.items, cm = this.cm;
60502 ms.get("asc").setDisabled(!cm.isSortable(index));
60503 ms.get("desc").setDisabled(!cm.isSortable(index));
60504 if(this.grid.enableColLock !== false){
60505 ms.get("lock").setDisabled(cm.isLocked(index));
60506 ms.get("unlock").setDisabled(!cm.isLocked(index));
60508 this.hmenu.show(hd, "tl-bl");
60511 handleHdOver : function(e){
60512 var hd = this.findHeaderCell(e.getTarget());
60513 if(hd && !this.headersDisabled){
60514 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60515 this.fly(hd).addClass("x-grid-hd-over");
60520 handleHdOut : function(e){
60521 var hd = this.findHeaderCell(e.getTarget());
60523 this.fly(hd).removeClass("x-grid-hd-over");
60527 handleSplitDblClick : function(e, t){
60528 var i = this.getCellIndex(t);
60529 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60530 this.autoSizeColumn(i, true);
60535 render : function(){
60538 var colCount = cm.getColumnCount();
60540 if(this.grid.monitorWindowResize === true){
60541 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60543 var header = this.renderHeaders();
60544 var body = this.templates.body.apply({rows:""});
60545 var html = this.templates.master.apply({
60548 lockedHeader: header[0],
60552 //this.updateColumns();
60554 this.grid.getGridEl().dom.innerHTML = html;
60556 this.initElements();
60558 // a kludge to fix the random scolling effect in webkit
60559 this.el.on("scroll", function() {
60560 this.el.dom.scrollTop=0; // hopefully not recursive..
60563 this.scroller.on("scroll", this.handleScroll, this);
60564 this.lockedBody.on("mousewheel", this.handleWheel, this);
60565 this.mainBody.on("mousewheel", this.handleWheel, this);
60567 this.mainHd.on("mouseover", this.handleHdOver, this);
60568 this.mainHd.on("mouseout", this.handleHdOut, this);
60569 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60570 {delegate: "."+this.splitClass});
60572 this.lockedHd.on("mouseover", this.handleHdOver, this);
60573 this.lockedHd.on("mouseout", this.handleHdOut, this);
60574 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60575 {delegate: "."+this.splitClass});
60577 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60578 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60581 this.updateSplitters();
60583 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60584 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60585 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60588 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60589 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60591 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60592 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60594 if(this.grid.enableColLock !== false){
60595 this.hmenu.add('-',
60596 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60597 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60601 this.hmenu.add('-',
60602 {id:"wider", text: this.columnsWiderText},
60603 {id:"narrow", text: this.columnsNarrowText }
60609 if(this.grid.enableColumnHide !== false){
60611 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60612 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60613 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60615 this.hmenu.add('-',
60616 {id:"columns", text: this.columnsText, menu: this.colMenu}
60619 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60621 this.grid.on("headercontextmenu", this.handleHdCtx, this);
60624 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60625 this.dd = new Roo.grid.GridDragZone(this.grid, {
60626 ddGroup : this.grid.ddGroup || 'GridDD'
60632 for(var i = 0; i < colCount; i++){
60633 if(cm.isHidden(i)){
60634 this.hideColumn(i);
60636 if(cm.config[i].align){
60637 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60638 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60642 this.updateHeaderSortState();
60644 this.beforeInitialResize();
60647 // two part rendering gives faster view to the user
60648 this.renderPhase2.defer(1, this);
60651 renderPhase2 : function(){
60652 // render the rows now
60654 if(this.grid.autoSizeColumns){
60655 this.autoSizeColumns();
60659 beforeInitialResize : function(){
60663 onColumnSplitterMoved : function(i, w){
60664 this.userResized = true;
60665 var cm = this.grid.colModel;
60666 cm.setColumnWidth(i, w, true);
60667 var cid = cm.getColumnId(i);
60668 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60669 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60670 this.updateSplitters();
60672 this.grid.fireEvent("columnresize", i, w);
60675 syncRowHeights : function(startIndex, endIndex){
60676 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60677 startIndex = startIndex || 0;
60678 var mrows = this.getBodyTable().rows;
60679 var lrows = this.getLockedTable().rows;
60680 var len = mrows.length-1;
60681 endIndex = Math.min(endIndex || len, len);
60682 for(var i = startIndex; i <= endIndex; i++){
60683 var m = mrows[i], l = lrows[i];
60684 var h = Math.max(m.offsetHeight, l.offsetHeight);
60685 m.style.height = l.style.height = h + "px";
60690 layout : function(initialRender, is2ndPass)
60693 var auto = g.autoHeight;
60694 var scrollOffset = 16;
60695 var c = g.getGridEl(), cm = this.cm,
60696 expandCol = g.autoExpandColumn,
60698 //c.beginMeasure();
60700 if(!c.dom.offsetWidth){ // display:none?
60702 this.lockedWrap.show();
60703 this.mainWrap.show();
60708 var hasLock = this.cm.isLocked(0);
60710 var tbh = this.headerPanel.getHeight();
60711 var bbh = this.footerPanel.getHeight();
60714 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60715 var newHeight = ch + c.getBorderWidth("tb");
60717 newHeight = Math.min(g.maxHeight, newHeight);
60719 c.setHeight(newHeight);
60723 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60726 var s = this.scroller;
60728 var csize = c.getSize(true);
60730 this.el.setSize(csize.width, csize.height);
60732 this.headerPanel.setWidth(csize.width);
60733 this.footerPanel.setWidth(csize.width);
60735 var hdHeight = this.mainHd.getHeight();
60736 var vw = csize.width;
60737 var vh = csize.height - (tbh + bbh);
60741 var bt = this.getBodyTable();
60743 if(cm.getLockedCount() == cm.config.length){
60744 bt = this.getLockedTable();
60747 var ltWidth = hasLock ?
60748 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60750 var scrollHeight = bt.offsetHeight;
60751 var scrollWidth = ltWidth + bt.offsetWidth;
60752 var vscroll = false, hscroll = false;
60754 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60756 var lw = this.lockedWrap, mw = this.mainWrap;
60757 var lb = this.lockedBody, mb = this.mainBody;
60759 setTimeout(function(){
60760 var t = s.dom.offsetTop;
60761 var w = s.dom.clientWidth,
60762 h = s.dom.clientHeight;
60765 lw.setSize(ltWidth, h);
60767 mw.setLeftTop(ltWidth, t);
60768 mw.setSize(w-ltWidth, h);
60770 lb.setHeight(h-hdHeight);
60771 mb.setHeight(h-hdHeight);
60773 if(is2ndPass !== true && !gv.userResized && expandCol){
60774 // high speed resize without full column calculation
60776 var ci = cm.getIndexById(expandCol);
60778 ci = cm.findColumnIndex(expandCol);
60780 ci = Math.max(0, ci); // make sure it's got at least the first col.
60781 var expandId = cm.getColumnId(ci);
60782 var tw = cm.getTotalWidth(false);
60783 var currentWidth = cm.getColumnWidth(ci);
60784 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60785 if(currentWidth != cw){
60786 cm.setColumnWidth(ci, cw, true);
60787 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60788 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60789 gv.updateSplitters();
60790 gv.layout(false, true);
60802 onWindowResize : function(){
60803 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60809 appendFooter : function(parentEl){
60813 sortAscText : "Sort Ascending",
60814 sortDescText : "Sort Descending",
60815 lockText : "Lock Column",
60816 unlockText : "Unlock Column",
60817 columnsText : "Columns",
60819 columnsWiderText : "Wider",
60820 columnsNarrowText : "Thinner"
60824 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60825 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60826 this.proxy.el.addClass('x-grid3-col-dd');
60829 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60830 handleMouseDown : function(e){
60834 callHandleMouseDown : function(e){
60835 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60840 * Ext JS Library 1.1.1
60841 * Copyright(c) 2006-2007, Ext JS, LLC.
60843 * Originally Released Under LGPL - original licence link has changed is not relivant.
60846 * <script type="text/javascript">
60849 * @extends Roo.dd.DDProxy
60850 * @class Roo.grid.SplitDragZone
60851 * Support for Column Header resizing
60853 * @param {Object} config
60856 // This is a support class used internally by the Grid components
60857 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60859 this.view = grid.getView();
60860 this.proxy = this.view.resizeProxy;
60861 Roo.grid.SplitDragZone.superclass.constructor.call(
60864 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60866 dragElId : Roo.id(this.proxy.dom),
60871 this.setHandleElId(Roo.id(hd));
60872 if (hd2 !== false) {
60873 this.setOuterHandleElId(Roo.id(hd2));
60876 this.scroll = false;
60878 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60879 fly: Roo.Element.fly,
60881 b4StartDrag : function(x, y){
60882 this.view.headersDisabled = true;
60883 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60884 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60886 this.proxy.setHeight(h);
60888 // for old system colWidth really stored the actual width?
60889 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60890 // which in reality did not work.. - it worked only for fixed sizes
60891 // for resizable we need to use actual sizes.
60892 var w = this.cm.getColumnWidth(this.cellIndex);
60893 if (!this.view.mainWrap) {
60895 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60900 // this was w-this.grid.minColumnWidth;
60901 // doesnt really make sense? - w = thie curren width or the rendered one?
60902 var minw = Math.max(w-this.grid.minColumnWidth, 0);
60903 this.resetConstraints();
60904 this.setXConstraint(minw, 1000);
60905 this.setYConstraint(0, 0);
60906 this.minX = x - minw;
60907 this.maxX = x + 1000;
60909 if (!this.view.mainWrap) { // this is Bootstrap code..
60910 this.getDragEl().style.display='block';
60913 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60917 handleMouseDown : function(e){
60918 ev = Roo.EventObject.setEvent(e);
60919 var t = this.fly(ev.getTarget());
60920 if(t.hasClass("x-grid-split")){
60921 this.cellIndex = this.view.getCellIndex(t.dom);
60922 this.split = t.dom;
60923 this.cm = this.grid.colModel;
60924 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60925 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60930 endDrag : function(e){
60931 this.view.headersDisabled = false;
60932 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60933 var diff = endX - this.startPos;
60935 var w = this.cm.getColumnWidth(this.cellIndex);
60936 if (!this.view.mainWrap) {
60939 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60942 autoOffset : function(){
60943 this.setDelta(0,0);
60947 * Ext JS Library 1.1.1
60948 * Copyright(c) 2006-2007, Ext JS, LLC.
60950 * Originally Released Under LGPL - original licence link has changed is not relivant.
60953 * <script type="text/javascript">
60957 // This is a support class used internally by the Grid components
60958 Roo.grid.GridDragZone = function(grid, config){
60959 this.view = grid.getView();
60960 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60961 if(this.view.lockedBody){
60962 this.setHandleElId(Roo.id(this.view.mainBody.dom));
60963 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60965 this.scroll = false;
60967 this.ddel = document.createElement('div');
60968 this.ddel.className = 'x-grid-dd-wrap';
60971 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60972 ddGroup : "GridDD",
60974 getDragData : function(e){
60975 var t = Roo.lib.Event.getTarget(e);
60976 var rowIndex = this.view.findRowIndex(t);
60977 var sm = this.grid.selModel;
60979 //Roo.log(rowIndex);
60981 if (sm.getSelectedCell) {
60982 // cell selection..
60983 if (!sm.getSelectedCell()) {
60986 if (rowIndex != sm.getSelectedCell()[0]) {
60991 if (sm.getSelections && sm.getSelections().length < 1) {
60996 // before it used to all dragging of unseleted... - now we dont do that.
60997 if(rowIndex !== false){
61002 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
61004 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
61007 if (e.hasModifier()){
61008 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
61011 Roo.log("getDragData");
61016 rowIndex: rowIndex,
61017 selections: sm.getSelections ? sm.getSelections() : (
61018 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61025 onInitDrag : function(e){
61026 var data = this.dragData;
61027 this.ddel.innerHTML = this.grid.getDragDropText();
61028 this.proxy.update(this.ddel);
61029 // fire start drag?
61032 afterRepair : function(){
61033 this.dragging = false;
61036 getRepairXY : function(e, data){
61040 onEndDrag : function(data, e){
61044 onValidDrop : function(dd, e, id){
61049 beforeInvalidDrop : function(e, id){
61054 * Ext JS Library 1.1.1
61055 * Copyright(c) 2006-2007, Ext JS, LLC.
61057 * Originally Released Under LGPL - original licence link has changed is not relivant.
61060 * <script type="text/javascript">
61065 * @class Roo.grid.ColumnModel
61066 * @extends Roo.util.Observable
61067 * This is the default implementation of a ColumnModel used by the Grid. It defines
61068 * the columns in the grid.
61071 var colModel = new Roo.grid.ColumnModel([
61072 {header: "Ticker", width: 60, sortable: true, locked: true},
61073 {header: "Company Name", width: 150, sortable: true},
61074 {header: "Market Cap.", width: 100, sortable: true},
61075 {header: "$ Sales", width: 100, sortable: true, renderer: money},
61076 {header: "Employees", width: 100, sortable: true, resizable: false}
61081 * The config options listed for this class are options which may appear in each
61082 * individual column definition.
61083 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61085 * @param {Object} config An Array of column config objects. See this class's
61086 * config objects for details.
61088 Roo.grid.ColumnModel = function(config){
61090 * The config passed into the constructor
61092 this.config = []; //config;
61095 // if no id, create one
61096 // if the column does not have a dataIndex mapping,
61097 // map it to the order it is in the config
61098 for(var i = 0, len = config.length; i < len; i++){
61099 this.addColumn(config[i]);
61104 * The width of columns which have no width specified (defaults to 100)
61107 this.defaultWidth = 100;
61110 * Default sortable of columns which have no sortable specified (defaults to false)
61113 this.defaultSortable = false;
61117 * @event widthchange
61118 * Fires when the width of a column changes.
61119 * @param {ColumnModel} this
61120 * @param {Number} columnIndex The column index
61121 * @param {Number} newWidth The new width
61123 "widthchange": true,
61125 * @event headerchange
61126 * Fires when the text of a header changes.
61127 * @param {ColumnModel} this
61128 * @param {Number} columnIndex The column index
61129 * @param {Number} newText The new header text
61131 "headerchange": true,
61133 * @event hiddenchange
61134 * Fires when a column is hidden or "unhidden".
61135 * @param {ColumnModel} this
61136 * @param {Number} columnIndex The column index
61137 * @param {Boolean} hidden true if hidden, false otherwise
61139 "hiddenchange": true,
61141 * @event columnmoved
61142 * Fires when a column is moved.
61143 * @param {ColumnModel} this
61144 * @param {Number} oldIndex
61145 * @param {Number} newIndex
61147 "columnmoved" : true,
61149 * @event columlockchange
61150 * Fires when a column's locked state is changed
61151 * @param {ColumnModel} this
61152 * @param {Number} colIndex
61153 * @param {Boolean} locked true if locked
61155 "columnlockchange" : true
61157 Roo.grid.ColumnModel.superclass.constructor.call(this);
61159 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61161 * @cfg {String} header The header text to display in the Grid view.
61164 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61167 * @cfg {String} smHeader Header at Bootsrap Small width
61170 * @cfg {String} mdHeader Header at Bootsrap Medium width
61173 * @cfg {String} lgHeader Header at Bootsrap Large width
61176 * @cfg {String} xlHeader Header at Bootsrap extra Large width
61179 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61180 * {@link Roo.data.Record} definition from which to draw the column's value. If not
61181 * specified, the column's index is used as an index into the Record's data Array.
61184 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61185 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61188 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61189 * Defaults to the value of the {@link #defaultSortable} property.
61190 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61193 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
61196 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
61199 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61202 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61205 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61206 * given the cell's data value. See {@link #setRenderer}. If not specified, the
61207 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61208 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61211 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
61214 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
61217 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
61220 * @cfg {String} cursor (Optional)
61223 * @cfg {String} tooltip (Optional)
61226 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61229 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61232 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61235 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61238 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61241 * Returns the id of the column at the specified index.
61242 * @param {Number} index The column index
61243 * @return {String} the id
61245 getColumnId : function(index){
61246 return this.config[index].id;
61250 * Returns the column for a specified id.
61251 * @param {String} id The column id
61252 * @return {Object} the column
61254 getColumnById : function(id){
61255 return this.lookup[id];
61260 * Returns the column Object for a specified dataIndex.
61261 * @param {String} dataIndex The column dataIndex
61262 * @return {Object|Boolean} the column or false if not found
61264 getColumnByDataIndex: function(dataIndex){
61265 var index = this.findColumnIndex(dataIndex);
61266 return index > -1 ? this.config[index] : false;
61270 * Returns the index for a specified column id.
61271 * @param {String} id The column id
61272 * @return {Number} the index, or -1 if not found
61274 getIndexById : function(id){
61275 for(var i = 0, len = this.config.length; i < len; i++){
61276 if(this.config[i].id == id){
61284 * Returns the index for a specified column dataIndex.
61285 * @param {String} dataIndex The column dataIndex
61286 * @return {Number} the index, or -1 if not found
61289 findColumnIndex : function(dataIndex){
61290 for(var i = 0, len = this.config.length; i < len; i++){
61291 if(this.config[i].dataIndex == dataIndex){
61299 moveColumn : function(oldIndex, newIndex){
61300 var c = this.config[oldIndex];
61301 this.config.splice(oldIndex, 1);
61302 this.config.splice(newIndex, 0, c);
61303 this.dataMap = null;
61304 this.fireEvent("columnmoved", this, oldIndex, newIndex);
61307 isLocked : function(colIndex){
61308 return this.config[colIndex].locked === true;
61311 setLocked : function(colIndex, value, suppressEvent){
61312 if(this.isLocked(colIndex) == value){
61315 this.config[colIndex].locked = value;
61316 if(!suppressEvent){
61317 this.fireEvent("columnlockchange", this, colIndex, value);
61321 getTotalLockedWidth : function(){
61322 var totalWidth = 0;
61323 for(var i = 0; i < this.config.length; i++){
61324 if(this.isLocked(i) && !this.isHidden(i)){
61325 this.totalWidth += this.getColumnWidth(i);
61331 getLockedCount : function(){
61332 for(var i = 0, len = this.config.length; i < len; i++){
61333 if(!this.isLocked(i)){
61338 return this.config.length;
61342 * Returns the number of columns.
61345 getColumnCount : function(visibleOnly){
61346 if(visibleOnly === true){
61348 for(var i = 0, len = this.config.length; i < len; i++){
61349 if(!this.isHidden(i)){
61355 return this.config.length;
61359 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61360 * @param {Function} fn
61361 * @param {Object} scope (optional)
61362 * @return {Array} result
61364 getColumnsBy : function(fn, scope){
61366 for(var i = 0, len = this.config.length; i < len; i++){
61367 var c = this.config[i];
61368 if(fn.call(scope||this, c, i) === true){
61376 * Returns true if the specified column is sortable.
61377 * @param {Number} col The column index
61378 * @return {Boolean}
61380 isSortable : function(col){
61381 if(typeof this.config[col].sortable == "undefined"){
61382 return this.defaultSortable;
61384 return this.config[col].sortable;
61388 * Returns the rendering (formatting) function defined for the column.
61389 * @param {Number} col The column index.
61390 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61392 getRenderer : function(col){
61393 if(!this.config[col].renderer){
61394 return Roo.grid.ColumnModel.defaultRenderer;
61396 return this.config[col].renderer;
61400 * Sets the rendering (formatting) function for a column.
61401 * @param {Number} col The column index
61402 * @param {Function} fn The function to use to process the cell's raw data
61403 * to return HTML markup for the grid view. The render function is called with
61404 * the following parameters:<ul>
61405 * <li>Data value.</li>
61406 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61407 * <li>css A CSS style string to apply to the table cell.</li>
61408 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61409 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61410 * <li>Row index</li>
61411 * <li>Column index</li>
61412 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61414 setRenderer : function(col, fn){
61415 this.config[col].renderer = fn;
61419 * Returns the width for the specified column.
61420 * @param {Number} col The column index
61421 * @param (optional) {String} gridSize bootstrap width size.
61424 getColumnWidth : function(col, gridSize)
61426 var cfg = this.config[col];
61428 if (typeof(gridSize) == 'undefined') {
61429 return cfg.width * 1 || this.defaultWidth;
61431 if (gridSize === false) { // if we set it..
61432 return cfg.width || false;
61434 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61436 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61437 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61440 return cfg[ sizes[i] ];
61447 * Sets the width for a column.
61448 * @param {Number} col The column index
61449 * @param {Number} width The new width
61451 setColumnWidth : function(col, width, suppressEvent){
61452 this.config[col].width = width;
61453 this.totalWidth = null;
61454 if(!suppressEvent){
61455 this.fireEvent("widthchange", this, col, width);
61460 * Returns the total width of all columns.
61461 * @param {Boolean} includeHidden True to include hidden column widths
61464 getTotalWidth : function(includeHidden){
61465 if(!this.totalWidth){
61466 this.totalWidth = 0;
61467 for(var i = 0, len = this.config.length; i < len; i++){
61468 if(includeHidden || !this.isHidden(i)){
61469 this.totalWidth += this.getColumnWidth(i);
61473 return this.totalWidth;
61477 * Returns the header for the specified column.
61478 * @param {Number} col The column index
61481 getColumnHeader : function(col){
61482 return this.config[col].header;
61486 * Sets the header for a column.
61487 * @param {Number} col The column index
61488 * @param {String} header The new header
61490 setColumnHeader : function(col, header){
61491 this.config[col].header = header;
61492 this.fireEvent("headerchange", this, col, header);
61496 * Returns the tooltip for the specified column.
61497 * @param {Number} col The column index
61500 getColumnTooltip : function(col){
61501 return this.config[col].tooltip;
61504 * Sets the tooltip for a column.
61505 * @param {Number} col The column index
61506 * @param {String} tooltip The new tooltip
61508 setColumnTooltip : function(col, tooltip){
61509 this.config[col].tooltip = tooltip;
61513 * Returns the dataIndex for the specified column.
61514 * @param {Number} col The column index
61517 getDataIndex : function(col){
61518 return this.config[col].dataIndex;
61522 * Sets the dataIndex for a column.
61523 * @param {Number} col The column index
61524 * @param {Number} dataIndex The new dataIndex
61526 setDataIndex : function(col, dataIndex){
61527 this.config[col].dataIndex = dataIndex;
61533 * Returns true if the cell is editable.
61534 * @param {Number} colIndex The column index
61535 * @param {Number} rowIndex The row index - this is nto actually used..?
61536 * @return {Boolean}
61538 isCellEditable : function(colIndex, rowIndex){
61539 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61543 * Returns the editor defined for the cell/column.
61544 * return false or null to disable editing.
61545 * @param {Number} colIndex The column index
61546 * @param {Number} rowIndex The row index
61549 getCellEditor : function(colIndex, rowIndex){
61550 return this.config[colIndex].editor;
61554 * Sets if a column is editable.
61555 * @param {Number} col The column index
61556 * @param {Boolean} editable True if the column is editable
61558 setEditable : function(col, editable){
61559 this.config[col].editable = editable;
61564 * Returns true if the column is hidden.
61565 * @param {Number} colIndex The column index
61566 * @return {Boolean}
61568 isHidden : function(colIndex){
61569 return this.config[colIndex].hidden;
61574 * Returns true if the column width cannot be changed
61576 isFixed : function(colIndex){
61577 return this.config[colIndex].fixed;
61581 * Returns true if the column can be resized
61582 * @return {Boolean}
61584 isResizable : function(colIndex){
61585 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61588 * Sets if a column is hidden.
61589 * @param {Number} colIndex The column index
61590 * @param {Boolean} hidden True if the column is hidden
61592 setHidden : function(colIndex, hidden){
61593 this.config[colIndex].hidden = hidden;
61594 this.totalWidth = null;
61595 this.fireEvent("hiddenchange", this, colIndex, hidden);
61599 * Sets the editor for a column.
61600 * @param {Number} col The column index
61601 * @param {Object} editor The editor object
61603 setEditor : function(col, editor){
61604 this.config[col].editor = editor;
61607 * Add a column (experimental...) - defaults to adding to the end..
61608 * @param {Object} config
61610 addColumn : function(c)
61613 var i = this.config.length;
61614 this.config[i] = c;
61616 if(typeof c.dataIndex == "undefined"){
61619 if(typeof c.renderer == "string"){
61620 c.renderer = Roo.util.Format[c.renderer];
61622 if(typeof c.id == "undefined"){
61625 if(c.editor && c.editor.xtype){
61626 c.editor = Roo.factory(c.editor, Roo.grid);
61628 if(c.editor && c.editor.isFormField){
61629 c.editor = new Roo.grid.GridEditor(c.editor);
61631 this.lookup[c.id] = c;
61636 Roo.grid.ColumnModel.defaultRenderer = function(value)
61638 if(typeof value == "object") {
61641 if(typeof value == "string" && value.length < 1){
61645 return String.format("{0}", value);
61648 // Alias for backwards compatibility
61649 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61652 * Ext JS Library 1.1.1
61653 * Copyright(c) 2006-2007, Ext JS, LLC.
61655 * Originally Released Under LGPL - original licence link has changed is not relivant.
61658 * <script type="text/javascript">
61662 * @class Roo.grid.AbstractSelectionModel
61663 * @extends Roo.util.Observable
61665 * Abstract base class for grid SelectionModels. It provides the interface that should be
61666 * implemented by descendant classes. This class should not be directly instantiated.
61669 Roo.grid.AbstractSelectionModel = function(){
61670 this.locked = false;
61671 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61674 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
61675 /** @ignore Called by the grid automatically. Do not call directly. */
61676 init : function(grid){
61682 * Locks the selections.
61685 this.locked = true;
61689 * Unlocks the selections.
61691 unlock : function(){
61692 this.locked = false;
61696 * Returns true if the selections are locked.
61697 * @return {Boolean}
61699 isLocked : function(){
61700 return this.locked;
61704 * Ext JS Library 1.1.1
61705 * Copyright(c) 2006-2007, Ext JS, LLC.
61707 * Originally Released Under LGPL - original licence link has changed is not relivant.
61710 * <script type="text/javascript">
61713 * @extends Roo.grid.AbstractSelectionModel
61714 * @class Roo.grid.RowSelectionModel
61715 * The default SelectionModel used by {@link Roo.grid.Grid}.
61716 * It supports multiple selections and keyboard selection/navigation.
61718 * @param {Object} config
61720 Roo.grid.RowSelectionModel = function(config){
61721 Roo.apply(this, config);
61722 this.selections = new Roo.util.MixedCollection(false, function(o){
61727 this.lastActive = false;
61731 * @event selectionchange
61732 * Fires when the selection changes
61733 * @param {SelectionModel} this
61735 "selectionchange" : true,
61737 * @event afterselectionchange
61738 * Fires after the selection changes (eg. by key press or clicking)
61739 * @param {SelectionModel} this
61741 "afterselectionchange" : true,
61743 * @event beforerowselect
61744 * Fires when a row is selected being selected, return false to cancel.
61745 * @param {SelectionModel} this
61746 * @param {Number} rowIndex The selected index
61747 * @param {Boolean} keepExisting False if other selections will be cleared
61749 "beforerowselect" : true,
61752 * Fires when a row is selected.
61753 * @param {SelectionModel} this
61754 * @param {Number} rowIndex The selected index
61755 * @param {Roo.data.Record} r The record
61757 "rowselect" : true,
61759 * @event rowdeselect
61760 * Fires when a row is deselected.
61761 * @param {SelectionModel} this
61762 * @param {Number} rowIndex The selected index
61764 "rowdeselect" : true
61766 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61767 this.locked = false;
61770 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
61772 * @cfg {Boolean} singleSelect
61773 * True to allow selection of only one row at a time (defaults to false)
61775 singleSelect : false,
61778 initEvents : function(){
61780 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61781 this.grid.on("mousedown", this.handleMouseDown, this);
61782 }else{ // allow click to work like normal
61783 this.grid.on("rowclick", this.handleDragableRowClick, this);
61785 // bootstrap does not have a view..
61786 var view = this.grid.view ? this.grid.view : this.grid;
61787 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61788 "up" : function(e){
61790 this.selectPrevious(e.shiftKey);
61791 }else if(this.last !== false && this.lastActive !== false){
61792 var last = this.last;
61793 this.selectRange(this.last, this.lastActive-1);
61794 view.focusRow(this.lastActive);
61795 if(last !== false){
61799 this.selectFirstRow();
61801 this.fireEvent("afterselectionchange", this);
61803 "down" : function(e){
61805 this.selectNext(e.shiftKey);
61806 }else if(this.last !== false && this.lastActive !== false){
61807 var last = this.last;
61808 this.selectRange(this.last, this.lastActive+1);
61809 view.focusRow(this.lastActive);
61810 if(last !== false){
61814 this.selectFirstRow();
61816 this.fireEvent("afterselectionchange", this);
61822 view.on("refresh", this.onRefresh, this);
61823 view.on("rowupdated", this.onRowUpdated, this);
61824 view.on("rowremoved", this.onRemove, this);
61828 onRefresh : function(){
61829 var ds = this.grid.ds, i, v = this.grid.view;
61830 var s = this.selections;
61831 s.each(function(r){
61832 if((i = ds.indexOfId(r.id)) != -1){
61834 s.add(ds.getAt(i)); // updating the selection relate data
61842 onRemove : function(v, index, r){
61843 this.selections.remove(r);
61847 onRowUpdated : function(v, index, r){
61848 if(this.isSelected(r)){
61849 v.onRowSelect(index);
61855 * @param {Array} records The records to select
61856 * @param {Boolean} keepExisting (optional) True to keep existing selections
61858 selectRecords : function(records, keepExisting){
61860 this.clearSelections();
61862 var ds = this.grid.ds;
61863 for(var i = 0, len = records.length; i < len; i++){
61864 this.selectRow(ds.indexOf(records[i]), true);
61869 * Gets the number of selected rows.
61872 getCount : function(){
61873 return this.selections.length;
61877 * Selects the first row in the grid.
61879 selectFirstRow : function(){
61884 * Select the last row.
61885 * @param {Boolean} keepExisting (optional) True to keep existing selections
61887 selectLastRow : function(keepExisting){
61888 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61892 * Selects the row immediately following the last selected row.
61893 * @param {Boolean} keepExisting (optional) True to keep existing selections
61895 selectNext : function(keepExisting){
61896 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61897 this.selectRow(this.last+1, keepExisting);
61898 var view = this.grid.view ? this.grid.view : this.grid;
61899 view.focusRow(this.last);
61904 * Selects the row that precedes the last selected row.
61905 * @param {Boolean} keepExisting (optional) True to keep existing selections
61907 selectPrevious : function(keepExisting){
61909 this.selectRow(this.last-1, keepExisting);
61910 var view = this.grid.view ? this.grid.view : this.grid;
61911 view.focusRow(this.last);
61916 * Returns the selected records
61917 * @return {Array} Array of selected records
61919 getSelections : function(){
61920 return [].concat(this.selections.items);
61924 * Returns the first selected record.
61927 getSelected : function(){
61928 return this.selections.itemAt(0);
61933 * Clears all selections.
61935 clearSelections : function(fast){
61940 var ds = this.grid.ds;
61941 var s = this.selections;
61942 s.each(function(r){
61943 this.deselectRow(ds.indexOfId(r.id));
61947 this.selections.clear();
61954 * Selects all rows.
61956 selectAll : function(){
61960 this.selections.clear();
61961 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61962 this.selectRow(i, true);
61967 * Returns True if there is a selection.
61968 * @return {Boolean}
61970 hasSelection : function(){
61971 return this.selections.length > 0;
61975 * Returns True if the specified row is selected.
61976 * @param {Number/Record} record The record or index of the record to check
61977 * @return {Boolean}
61979 isSelected : function(index){
61980 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61981 return (r && this.selections.key(r.id) ? true : false);
61985 * Returns True if the specified record id is selected.
61986 * @param {String} id The id of record to check
61987 * @return {Boolean}
61989 isIdSelected : function(id){
61990 return (this.selections.key(id) ? true : false);
61994 handleMouseDown : function(e, t)
61996 var view = this.grid.view ? this.grid.view : this.grid;
61998 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
62001 if(e.shiftKey && this.last !== false){
62002 var last = this.last;
62003 this.selectRange(last, rowIndex, e.ctrlKey);
62004 this.last = last; // reset the last
62005 view.focusRow(rowIndex);
62007 var isSelected = this.isSelected(rowIndex);
62008 if(e.button !== 0 && isSelected){
62009 view.focusRow(rowIndex);
62010 }else if(e.ctrlKey && isSelected){
62011 this.deselectRow(rowIndex);
62012 }else if(!isSelected){
62013 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
62014 view.focusRow(rowIndex);
62017 this.fireEvent("afterselectionchange", this);
62020 handleDragableRowClick : function(grid, rowIndex, e)
62022 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62023 this.selectRow(rowIndex, false);
62024 var view = this.grid.view ? this.grid.view : this.grid;
62025 view.focusRow(rowIndex);
62026 this.fireEvent("afterselectionchange", this);
62031 * Selects multiple rows.
62032 * @param {Array} rows Array of the indexes of the row to select
62033 * @param {Boolean} keepExisting (optional) True to keep existing selections
62035 selectRows : function(rows, keepExisting){
62037 this.clearSelections();
62039 for(var i = 0, len = rows.length; i < len; i++){
62040 this.selectRow(rows[i], true);
62045 * Selects a range of rows. All rows in between startRow and endRow are also selected.
62046 * @param {Number} startRow The index of the first row in the range
62047 * @param {Number} endRow The index of the last row in the range
62048 * @param {Boolean} keepExisting (optional) True to retain existing selections
62050 selectRange : function(startRow, endRow, keepExisting){
62055 this.clearSelections();
62057 if(startRow <= endRow){
62058 for(var i = startRow; i <= endRow; i++){
62059 this.selectRow(i, true);
62062 for(var i = startRow; i >= endRow; i--){
62063 this.selectRow(i, true);
62069 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62070 * @param {Number} startRow The index of the first row in the range
62071 * @param {Number} endRow The index of the last row in the range
62073 deselectRange : function(startRow, endRow, preventViewNotify){
62077 for(var i = startRow; i <= endRow; i++){
62078 this.deselectRow(i, preventViewNotify);
62084 * @param {Number} row The index of the row to select
62085 * @param {Boolean} keepExisting (optional) True to keep existing selections
62087 selectRow : function(index, keepExisting, preventViewNotify){
62088 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62091 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62092 if(!keepExisting || this.singleSelect){
62093 this.clearSelections();
62095 var r = this.grid.ds.getAt(index);
62096 this.selections.add(r);
62097 this.last = this.lastActive = index;
62098 if(!preventViewNotify){
62099 var view = this.grid.view ? this.grid.view : this.grid;
62100 view.onRowSelect(index);
62102 this.fireEvent("rowselect", this, index, r);
62103 this.fireEvent("selectionchange", this);
62109 * @param {Number} row The index of the row to deselect
62111 deselectRow : function(index, preventViewNotify){
62115 if(this.last == index){
62118 if(this.lastActive == index){
62119 this.lastActive = false;
62121 var r = this.grid.ds.getAt(index);
62122 this.selections.remove(r);
62123 if(!preventViewNotify){
62124 var view = this.grid.view ? this.grid.view : this.grid;
62125 view.onRowDeselect(index);
62127 this.fireEvent("rowdeselect", this, index);
62128 this.fireEvent("selectionchange", this);
62132 restoreLast : function(){
62134 this.last = this._last;
62139 acceptsNav : function(row, col, cm){
62140 return !cm.isHidden(col) && cm.isCellEditable(col, row);
62144 onEditorKey : function(field, e){
62145 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62150 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62152 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62154 }else if(k == e.ENTER && !e.ctrlKey){
62158 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62160 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62162 }else if(k == e.ESC){
62166 g.startEditing(newCell[0], newCell[1]);
62171 * Ext JS Library 1.1.1
62172 * Copyright(c) 2006-2007, Ext JS, LLC.
62174 * Originally Released Under LGPL - original licence link has changed is not relivant.
62177 * <script type="text/javascript">
62180 * @class Roo.grid.CellSelectionModel
62181 * @extends Roo.grid.AbstractSelectionModel
62182 * This class provides the basic implementation for cell selection in a grid.
62184 * @param {Object} config The object containing the configuration of this model.
62185 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62187 Roo.grid.CellSelectionModel = function(config){
62188 Roo.apply(this, config);
62190 this.selection = null;
62194 * @event beforerowselect
62195 * Fires before a cell is selected.
62196 * @param {SelectionModel} this
62197 * @param {Number} rowIndex The selected row index
62198 * @param {Number} colIndex The selected cell index
62200 "beforecellselect" : true,
62202 * @event cellselect
62203 * Fires when a cell is selected.
62204 * @param {SelectionModel} this
62205 * @param {Number} rowIndex The selected row index
62206 * @param {Number} colIndex The selected cell index
62208 "cellselect" : true,
62210 * @event selectionchange
62211 * Fires when the active selection changes.
62212 * @param {SelectionModel} this
62213 * @param {Object} selection null for no selection or an object (o) with two properties
62215 <li>o.record: the record object for the row the selection is in</li>
62216 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62219 "selectionchange" : true,
62222 * Fires when the tab (or enter) was pressed on the last editable cell
62223 * You can use this to trigger add new row.
62224 * @param {SelectionModel} this
62228 * @event beforeeditnext
62229 * Fires before the next editable sell is made active
62230 * You can use this to skip to another cell or fire the tabend
62231 * if you set cell to false
62232 * @param {Object} eventdata object : { cell : [ row, col ] }
62234 "beforeeditnext" : true
62236 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62239 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
62241 enter_is_tab: false,
62244 initEvents : function(){
62245 this.grid.on("mousedown", this.handleMouseDown, this);
62246 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62247 var view = this.grid.view;
62248 view.on("refresh", this.onViewChange, this);
62249 view.on("rowupdated", this.onRowUpdated, this);
62250 view.on("beforerowremoved", this.clearSelections, this);
62251 view.on("beforerowsinserted", this.clearSelections, this);
62252 if(this.grid.isEditor){
62253 this.grid.on("beforeedit", this.beforeEdit, this);
62258 beforeEdit : function(e){
62259 this.select(e.row, e.column, false, true, e.record);
62263 onRowUpdated : function(v, index, r){
62264 if(this.selection && this.selection.record == r){
62265 v.onCellSelect(index, this.selection.cell[1]);
62270 onViewChange : function(){
62271 this.clearSelections(true);
62275 * Returns the currently selected cell,.
62276 * @return {Array} The selected cell (row, column) or null if none selected.
62278 getSelectedCell : function(){
62279 return this.selection ? this.selection.cell : null;
62283 * Clears all selections.
62284 * @param {Boolean} true to prevent the gridview from being notified about the change.
62286 clearSelections : function(preventNotify){
62287 var s = this.selection;
62289 if(preventNotify !== true){
62290 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62292 this.selection = null;
62293 this.fireEvent("selectionchange", this, null);
62298 * Returns true if there is a selection.
62299 * @return {Boolean}
62301 hasSelection : function(){
62302 return this.selection ? true : false;
62306 handleMouseDown : function(e, t){
62307 var v = this.grid.getView();
62308 if(this.isLocked()){
62311 var row = v.findRowIndex(t);
62312 var cell = v.findCellIndex(t);
62313 if(row !== false && cell !== false){
62314 this.select(row, cell);
62320 * @param {Number} rowIndex
62321 * @param {Number} collIndex
62323 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62324 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62325 this.clearSelections();
62326 r = r || this.grid.dataSource.getAt(rowIndex);
62329 cell : [rowIndex, colIndex]
62331 if(!preventViewNotify){
62332 var v = this.grid.getView();
62333 v.onCellSelect(rowIndex, colIndex);
62334 if(preventFocus !== true){
62335 v.focusCell(rowIndex, colIndex);
62338 this.fireEvent("cellselect", this, rowIndex, colIndex);
62339 this.fireEvent("selectionchange", this, this.selection);
62344 isSelectable : function(rowIndex, colIndex, cm){
62345 return !cm.isHidden(colIndex);
62349 handleKeyDown : function(e){
62350 //Roo.log('Cell Sel Model handleKeyDown');
62351 if(!e.isNavKeyPress()){
62354 var g = this.grid, s = this.selection;
62357 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
62359 this.select(cell[0], cell[1]);
62364 var walk = function(row, col, step){
62365 return g.walkCells(row, col, step, sm.isSelectable, sm);
62367 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62374 // handled by onEditorKey
62375 if (g.isEditor && g.editing) {
62379 newCell = walk(r, c-1, -1);
62381 newCell = walk(r, c+1, 1);
62386 newCell = walk(r+1, c, 1);
62390 newCell = walk(r-1, c, -1);
62394 newCell = walk(r, c+1, 1);
62398 newCell = walk(r, c-1, -1);
62403 if(g.isEditor && !g.editing){
62404 g.startEditing(r, c);
62413 this.select(newCell[0], newCell[1]);
62419 acceptsNav : function(row, col, cm){
62420 return !cm.isHidden(col) && cm.isCellEditable(col, row);
62424 * @param {Number} field (not used) - as it's normally used as a listener
62425 * @param {Number} e - event - fake it by using
62427 * var e = Roo.EventObjectImpl.prototype;
62428 * e.keyCode = e.TAB
62432 onEditorKey : function(field, e){
62434 var k = e.getKey(),
62437 ed = g.activeEditor,
62439 ///Roo.log('onEditorKey' + k);
62442 if (this.enter_is_tab && k == e.ENTER) {
62448 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62450 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62456 } else if(k == e.ENTER && !e.ctrlKey){
62459 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62461 } else if(k == e.ESC){
62466 var ecall = { cell : newCell, forward : forward };
62467 this.fireEvent('beforeeditnext', ecall );
62468 newCell = ecall.cell;
62469 forward = ecall.forward;
62473 //Roo.log('next cell after edit');
62474 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62475 } else if (forward) {
62476 // tabbed past last
62477 this.fireEvent.defer(100, this, ['tabend',this]);
62482 * Ext JS Library 1.1.1
62483 * Copyright(c) 2006-2007, Ext JS, LLC.
62485 * Originally Released Under LGPL - original licence link has changed is not relivant.
62488 * <script type="text/javascript">
62492 * @class Roo.grid.EditorGrid
62493 * @extends Roo.grid.Grid
62494 * Class for creating and editable grid.
62495 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62496 * The container MUST have some type of size defined for the grid to fill. The container will be
62497 * automatically set to position relative if it isn't already.
62498 * @param {Object} dataSource The data model to bind to
62499 * @param {Object} colModel The column model with info about this grid's columns
62501 Roo.grid.EditorGrid = function(container, config){
62502 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62503 this.getGridEl().addClass("xedit-grid");
62505 if(!this.selModel){
62506 this.selModel = new Roo.grid.CellSelectionModel();
62509 this.activeEditor = null;
62513 * @event beforeedit
62514 * Fires before cell editing is triggered. The edit event object has the following properties <br />
62515 * <ul style="padding:5px;padding-left:16px;">
62516 * <li>grid - This grid</li>
62517 * <li>record - The record being edited</li>
62518 * <li>field - The field name being edited</li>
62519 * <li>value - The value for the field being edited.</li>
62520 * <li>row - The grid row index</li>
62521 * <li>column - The grid column index</li>
62522 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62524 * @param {Object} e An edit event (see above for description)
62526 "beforeedit" : true,
62529 * Fires after a cell is edited. <br />
62530 * <ul style="padding:5px;padding-left:16px;">
62531 * <li>grid - This grid</li>
62532 * <li>record - The record being edited</li>
62533 * <li>field - The field name being edited</li>
62534 * <li>value - The value being set</li>
62535 * <li>originalValue - The original value for the field, before the edit.</li>
62536 * <li>row - The grid row index</li>
62537 * <li>column - The grid column index</li>
62539 * @param {Object} e An edit event (see above for description)
62541 "afteredit" : true,
62543 * @event validateedit
62544 * Fires after a cell is edited, but before the value is set in the record.
62545 * You can use this to modify the value being set in the field, Return false
62546 * to cancel the change. The edit event object has the following properties <br />
62547 * <ul style="padding:5px;padding-left:16px;">
62548 * <li>editor - This editor</li>
62549 * <li>grid - This grid</li>
62550 * <li>record - The record being edited</li>
62551 * <li>field - The field name being edited</li>
62552 * <li>value - The value being set</li>
62553 * <li>originalValue - The original value for the field, before the edit.</li>
62554 * <li>row - The grid row index</li>
62555 * <li>column - The grid column index</li>
62556 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62558 * @param {Object} e An edit event (see above for description)
62560 "validateedit" : true
62562 this.on("bodyscroll", this.stopEditing, this);
62563 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
62566 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62568 * @cfg {Number} clicksToEdit
62569 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62576 trackMouseOver: false, // causes very odd FF errors
62578 onCellDblClick : function(g, row, col){
62579 this.startEditing(row, col);
62582 onEditComplete : function(ed, value, startValue){
62583 this.editing = false;
62584 this.activeEditor = null;
62585 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62587 var field = this.colModel.getDataIndex(ed.col);
62592 originalValue: startValue,
62599 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62602 if(String(value) !== String(startValue)){
62604 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62605 r.set(field, e.value);
62606 // if we are dealing with a combo box..
62607 // then we also set the 'name' colum to be the displayField
62608 if (ed.field.displayField && ed.field.name) {
62609 r.set(ed.field.name, ed.field.el.dom.value);
62612 delete e.cancel; //?? why!!!
62613 this.fireEvent("afteredit", e);
62616 this.fireEvent("afteredit", e); // always fire it!
62618 this.view.focusCell(ed.row, ed.col);
62622 * Starts editing the specified for the specified row/column
62623 * @param {Number} rowIndex
62624 * @param {Number} colIndex
62626 startEditing : function(row, col){
62627 this.stopEditing();
62628 if(this.colModel.isCellEditable(col, row)){
62629 this.view.ensureVisible(row, col, true);
62631 var r = this.dataSource.getAt(row);
62632 var field = this.colModel.getDataIndex(col);
62633 var cell = Roo.get(this.view.getCell(row,col));
62638 value: r.data[field],
62643 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62644 this.editing = true;
62645 var ed = this.colModel.getCellEditor(col, row);
62651 ed.render(ed.parentEl || document.body);
62657 (function(){ // complex but required for focus issues in safari, ie and opera
62661 ed.on("complete", this.onEditComplete, this, {single: true});
62662 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
62663 this.activeEditor = ed;
62664 var v = r.data[field];
62665 ed.startEdit(this.view.getCell(row, col), v);
62666 // combo's with 'displayField and name set
62667 if (ed.field.displayField && ed.field.name) {
62668 ed.field.el.dom.value = r.data[ed.field.name];
62672 }).defer(50, this);
62678 * Stops any active editing
62680 stopEditing : function(){
62681 if(this.activeEditor){
62682 this.activeEditor.completeEdit();
62684 this.activeEditor = null;
62688 * Called to get grid's drag proxy text, by default returns this.ddText.
62691 getDragDropText : function(){
62692 var count = this.selModel.getSelectedCell() ? 1 : 0;
62693 return String.format(this.ddText, count, count == 1 ? '' : 's');
62698 * Ext JS Library 1.1.1
62699 * Copyright(c) 2006-2007, Ext JS, LLC.
62701 * Originally Released Under LGPL - original licence link has changed is not relivant.
62704 * <script type="text/javascript">
62707 // private - not really -- you end up using it !
62708 // This is a support class used internally by the Grid components
62711 * @class Roo.grid.GridEditor
62712 * @extends Roo.Editor
62713 * Class for creating and editable grid elements.
62714 * @param {Object} config any settings (must include field)
62716 Roo.grid.GridEditor = function(field, config){
62717 if (!config && field.field) {
62719 field = Roo.factory(config.field, Roo.form);
62721 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62722 field.monitorTab = false;
62725 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62728 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62731 alignment: "tl-tl",
62734 cls: "x-small-editor x-grid-editor",
62739 * Ext JS Library 1.1.1
62740 * Copyright(c) 2006-2007, Ext JS, LLC.
62742 * Originally Released Under LGPL - original licence link has changed is not relivant.
62745 * <script type="text/javascript">
62750 Roo.grid.PropertyRecord = Roo.data.Record.create([
62751 {name:'name',type:'string'}, 'value'
62755 Roo.grid.PropertyStore = function(grid, source){
62757 this.store = new Roo.data.Store({
62758 recordType : Roo.grid.PropertyRecord
62760 this.store.on('update', this.onUpdate, this);
62762 this.setSource(source);
62764 Roo.grid.PropertyStore.superclass.constructor.call(this);
62769 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62770 setSource : function(o){
62772 this.store.removeAll();
62775 if(this.isEditableValue(o[k])){
62776 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62779 this.store.loadRecords({records: data}, {}, true);
62782 onUpdate : function(ds, record, type){
62783 if(type == Roo.data.Record.EDIT){
62784 var v = record.data['value'];
62785 var oldValue = record.modified['value'];
62786 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62787 this.source[record.id] = v;
62789 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62796 getProperty : function(row){
62797 return this.store.getAt(row);
62800 isEditableValue: function(val){
62801 if(val && val instanceof Date){
62803 }else if(typeof val == 'object' || typeof val == 'function'){
62809 setValue : function(prop, value){
62810 this.source[prop] = value;
62811 this.store.getById(prop).set('value', value);
62814 getSource : function(){
62815 return this.source;
62819 Roo.grid.PropertyColumnModel = function(grid, store){
62822 g.PropertyColumnModel.superclass.constructor.call(this, [
62823 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62824 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62826 this.store = store;
62827 this.bselect = Roo.DomHelper.append(document.body, {
62828 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62829 {tag: 'option', value: 'true', html: 'true'},
62830 {tag: 'option', value: 'false', html: 'false'}
62833 Roo.id(this.bselect);
62836 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62837 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62838 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62839 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62840 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62842 this.renderCellDelegate = this.renderCell.createDelegate(this);
62843 this.renderPropDelegate = this.renderProp.createDelegate(this);
62846 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62850 valueText : 'Value',
62852 dateFormat : 'm/j/Y',
62855 renderDate : function(dateVal){
62856 return dateVal.dateFormat(this.dateFormat);
62859 renderBool : function(bVal){
62860 return bVal ? 'true' : 'false';
62863 isCellEditable : function(colIndex, rowIndex){
62864 return colIndex == 1;
62867 getRenderer : function(col){
62869 this.renderCellDelegate : this.renderPropDelegate;
62872 renderProp : function(v){
62873 return this.getPropertyName(v);
62876 renderCell : function(val){
62878 if(val instanceof Date){
62879 rv = this.renderDate(val);
62880 }else if(typeof val == 'boolean'){
62881 rv = this.renderBool(val);
62883 return Roo.util.Format.htmlEncode(rv);
62886 getPropertyName : function(name){
62887 var pn = this.grid.propertyNames;
62888 return pn && pn[name] ? pn[name] : name;
62891 getCellEditor : function(colIndex, rowIndex){
62892 var p = this.store.getProperty(rowIndex);
62893 var n = p.data['name'], val = p.data['value'];
62895 if(typeof(this.grid.customEditors[n]) == 'string'){
62896 return this.editors[this.grid.customEditors[n]];
62898 if(typeof(this.grid.customEditors[n]) != 'undefined'){
62899 return this.grid.customEditors[n];
62901 if(val instanceof Date){
62902 return this.editors['date'];
62903 }else if(typeof val == 'number'){
62904 return this.editors['number'];
62905 }else if(typeof val == 'boolean'){
62906 return this.editors['boolean'];
62908 return this.editors['string'];
62914 * @class Roo.grid.PropertyGrid
62915 * @extends Roo.grid.EditorGrid
62916 * This class represents the interface of a component based property grid control.
62917 * <br><br>Usage:<pre><code>
62918 var grid = new Roo.grid.PropertyGrid("my-container-id", {
62926 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62927 * The container MUST have some type of size defined for the grid to fill. The container will be
62928 * automatically set to position relative if it isn't already.
62929 * @param {Object} config A config object that sets properties on this grid.
62931 Roo.grid.PropertyGrid = function(container, config){
62932 config = config || {};
62933 var store = new Roo.grid.PropertyStore(this);
62934 this.store = store;
62935 var cm = new Roo.grid.PropertyColumnModel(this, store);
62936 store.store.sort('name', 'ASC');
62937 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62940 enableColLock:false,
62941 enableColumnMove:false,
62943 trackMouseOver: false,
62946 this.getGridEl().addClass('x-props-grid');
62947 this.lastEditRow = null;
62948 this.on('columnresize', this.onColumnResize, this);
62951 * @event beforepropertychange
62952 * Fires before a property changes (return false to stop?)
62953 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62954 * @param {String} id Record Id
62955 * @param {String} newval New Value
62956 * @param {String} oldval Old Value
62958 "beforepropertychange": true,
62960 * @event propertychange
62961 * Fires after a property changes
62962 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62963 * @param {String} id Record Id
62964 * @param {String} newval New Value
62965 * @param {String} oldval Old Value
62967 "propertychange": true
62969 this.customEditors = this.customEditors || {};
62971 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62974 * @cfg {Object} customEditors map of colnames=> custom editors.
62975 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62976 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62977 * false disables editing of the field.
62981 * @cfg {Object} propertyNames map of property Names to their displayed value
62984 render : function(){
62985 Roo.grid.PropertyGrid.superclass.render.call(this);
62986 this.autoSize.defer(100, this);
62989 autoSize : function(){
62990 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62992 this.view.fitColumns();
62996 onColumnResize : function(){
62997 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
63001 * Sets the data for the Grid
63002 * accepts a Key => Value object of all the elements avaiable.
63003 * @param {Object} data to appear in grid.
63005 setSource : function(source){
63006 this.store.setSource(source);
63010 * Gets all the data from the grid.
63011 * @return {Object} data data stored in grid
63013 getSource : function(){
63014 return this.store.getSource();
63023 * @class Roo.grid.Calendar
63024 * @extends Roo.grid.Grid
63025 * This class extends the Grid to provide a calendar widget
63026 * <br><br>Usage:<pre><code>
63027 var grid = new Roo.grid.Calendar("my-container-id", {
63030 selModel: mySelectionModel,
63031 autoSizeColumns: true,
63032 monitorWindowResize: false,
63033 trackMouseOver: true
63034 eventstore : real data store..
63040 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63041 * The container MUST have some type of size defined for the grid to fill. The container will be
63042 * automatically set to position relative if it isn't already.
63043 * @param {Object} config A config object that sets properties on this grid.
63045 Roo.grid.Calendar = function(container, config){
63046 // initialize the container
63047 this.container = Roo.get(container);
63048 this.container.update("");
63049 this.container.setStyle("overflow", "hidden");
63050 this.container.addClass('x-grid-container');
63052 this.id = this.container.id;
63054 Roo.apply(this, config);
63055 // check and correct shorthanded configs
63059 for (var r = 0;r < 6;r++) {
63062 for (var c =0;c < 7;c++) {
63066 if (this.eventStore) {
63067 this.eventStore= Roo.factory(this.eventStore, Roo.data);
63068 this.eventStore.on('load',this.onLoad, this);
63069 this.eventStore.on('beforeload',this.clearEvents, this);
63073 this.dataSource = new Roo.data.Store({
63074 proxy: new Roo.data.MemoryProxy(rows),
63075 reader: new Roo.data.ArrayReader({}, [
63076 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63079 this.dataSource.load();
63080 this.ds = this.dataSource;
63081 this.ds.xmodule = this.xmodule || false;
63084 var cellRender = function(v,x,r)
63086 return String.format(
63087 '<div class="fc-day fc-widget-content"><div>' +
63088 '<div class="fc-event-container"></div>' +
63089 '<div class="fc-day-number">{0}</div>'+
63091 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63092 '</div></div>', v);
63097 this.colModel = new Roo.grid.ColumnModel( [
63099 xtype: 'ColumnModel',
63101 dataIndex : 'weekday0',
63103 renderer : cellRender
63106 xtype: 'ColumnModel',
63108 dataIndex : 'weekday1',
63110 renderer : cellRender
63113 xtype: 'ColumnModel',
63115 dataIndex : 'weekday2',
63116 header : 'Tuesday',
63117 renderer : cellRender
63120 xtype: 'ColumnModel',
63122 dataIndex : 'weekday3',
63123 header : 'Wednesday',
63124 renderer : cellRender
63127 xtype: 'ColumnModel',
63129 dataIndex : 'weekday4',
63130 header : 'Thursday',
63131 renderer : cellRender
63134 xtype: 'ColumnModel',
63136 dataIndex : 'weekday5',
63138 renderer : cellRender
63141 xtype: 'ColumnModel',
63143 dataIndex : 'weekday6',
63144 header : 'Saturday',
63145 renderer : cellRender
63148 this.cm = this.colModel;
63149 this.cm.xmodule = this.xmodule || false;
63153 //this.selModel = new Roo.grid.CellSelectionModel();
63154 //this.sm = this.selModel;
63155 //this.selModel.init(this);
63159 this.container.setWidth(this.width);
63163 this.container.setHeight(this.height);
63170 * The raw click event for the entire grid.
63171 * @param {Roo.EventObject} e
63176 * The raw dblclick event for the entire grid.
63177 * @param {Roo.EventObject} e
63181 * @event contextmenu
63182 * The raw contextmenu event for the entire grid.
63183 * @param {Roo.EventObject} e
63185 "contextmenu" : true,
63188 * The raw mousedown event for the entire grid.
63189 * @param {Roo.EventObject} e
63191 "mousedown" : true,
63194 * The raw mouseup event for the entire grid.
63195 * @param {Roo.EventObject} e
63200 * The raw mouseover event for the entire grid.
63201 * @param {Roo.EventObject} e
63203 "mouseover" : true,
63206 * The raw mouseout event for the entire grid.
63207 * @param {Roo.EventObject} e
63212 * The raw keypress event for the entire grid.
63213 * @param {Roo.EventObject} e
63218 * The raw keydown event for the entire grid.
63219 * @param {Roo.EventObject} e
63227 * Fires when a cell is clicked
63228 * @param {Grid} this
63229 * @param {Number} rowIndex
63230 * @param {Number} columnIndex
63231 * @param {Roo.EventObject} e
63233 "cellclick" : true,
63235 * @event celldblclick
63236 * Fires when a cell is double clicked
63237 * @param {Grid} this
63238 * @param {Number} rowIndex
63239 * @param {Number} columnIndex
63240 * @param {Roo.EventObject} e
63242 "celldblclick" : true,
63245 * Fires when a row is clicked
63246 * @param {Grid} this
63247 * @param {Number} rowIndex
63248 * @param {Roo.EventObject} e
63252 * @event rowdblclick
63253 * Fires when a row is double clicked
63254 * @param {Grid} this
63255 * @param {Number} rowIndex
63256 * @param {Roo.EventObject} e
63258 "rowdblclick" : true,
63260 * @event headerclick
63261 * Fires when a header is clicked
63262 * @param {Grid} this
63263 * @param {Number} columnIndex
63264 * @param {Roo.EventObject} e
63266 "headerclick" : true,
63268 * @event headerdblclick
63269 * Fires when a header cell is double clicked
63270 * @param {Grid} this
63271 * @param {Number} columnIndex
63272 * @param {Roo.EventObject} e
63274 "headerdblclick" : true,
63276 * @event rowcontextmenu
63277 * Fires when a row is right clicked
63278 * @param {Grid} this
63279 * @param {Number} rowIndex
63280 * @param {Roo.EventObject} e
63282 "rowcontextmenu" : true,
63284 * @event cellcontextmenu
63285 * Fires when a cell is right clicked
63286 * @param {Grid} this
63287 * @param {Number} rowIndex
63288 * @param {Number} cellIndex
63289 * @param {Roo.EventObject} e
63291 "cellcontextmenu" : true,
63293 * @event headercontextmenu
63294 * Fires when a header is right clicked
63295 * @param {Grid} this
63296 * @param {Number} columnIndex
63297 * @param {Roo.EventObject} e
63299 "headercontextmenu" : true,
63301 * @event bodyscroll
63302 * Fires when the body element is scrolled
63303 * @param {Number} scrollLeft
63304 * @param {Number} scrollTop
63306 "bodyscroll" : true,
63308 * @event columnresize
63309 * Fires when the user resizes a column
63310 * @param {Number} columnIndex
63311 * @param {Number} newSize
63313 "columnresize" : true,
63315 * @event columnmove
63316 * Fires when the user moves a column
63317 * @param {Number} oldIndex
63318 * @param {Number} newIndex
63320 "columnmove" : true,
63323 * Fires when row(s) start being dragged
63324 * @param {Grid} this
63325 * @param {Roo.GridDD} dd The drag drop object
63326 * @param {event} e The raw browser event
63328 "startdrag" : true,
63331 * Fires when a drag operation is complete
63332 * @param {Grid} this
63333 * @param {Roo.GridDD} dd The drag drop object
63334 * @param {event} e The raw browser event
63339 * Fires when dragged row(s) are dropped on a valid DD target
63340 * @param {Grid} this
63341 * @param {Roo.GridDD} dd The drag drop object
63342 * @param {String} targetId The target drag drop object
63343 * @param {event} e The raw browser event
63348 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63349 * @param {Grid} this
63350 * @param {Roo.GridDD} dd The drag drop object
63351 * @param {String} targetId The target drag drop object
63352 * @param {event} e The raw browser event
63357 * Fires when the dragged row(s) first cross another DD target while being dragged
63358 * @param {Grid} this
63359 * @param {Roo.GridDD} dd The drag drop object
63360 * @param {String} targetId The target drag drop object
63361 * @param {event} e The raw browser event
63363 "dragenter" : true,
63366 * Fires when the dragged row(s) leave another DD target while being dragged
63367 * @param {Grid} this
63368 * @param {Roo.GridDD} dd The drag drop object
63369 * @param {String} targetId The target drag drop object
63370 * @param {event} e The raw browser event
63375 * Fires when a row is rendered, so you can change add a style to it.
63376 * @param {GridView} gridview The grid view
63377 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
63383 * Fires when the grid is rendered
63384 * @param {Grid} grid
63389 * Fires when a date is selected
63390 * @param {DatePicker} this
63391 * @param {Date} date The selected date
63395 * @event monthchange
63396 * Fires when the displayed month changes
63397 * @param {DatePicker} this
63398 * @param {Date} date The selected month
63400 'monthchange': true,
63402 * @event evententer
63403 * Fires when mouse over an event
63404 * @param {Calendar} this
63405 * @param {event} Event
63407 'evententer': true,
63409 * @event eventleave
63410 * Fires when the mouse leaves an
63411 * @param {Calendar} this
63414 'eventleave': true,
63416 * @event eventclick
63417 * Fires when the mouse click an
63418 * @param {Calendar} this
63421 'eventclick': true,
63423 * @event eventrender
63424 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63425 * @param {Calendar} this
63426 * @param {data} data to be modified
63428 'eventrender': true
63432 Roo.grid.Grid.superclass.constructor.call(this);
63433 this.on('render', function() {
63434 this.view.el.addClass('x-grid-cal');
63436 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63440 if (!Roo.grid.Calendar.style) {
63441 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63444 '.x-grid-cal .x-grid-col' : {
63445 height: 'auto !important',
63446 'vertical-align': 'top'
63448 '.x-grid-cal .fc-event-hori' : {
63459 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63461 * @cfg {Store} eventStore The store that loads events.
63466 activeDate : false,
63469 monitorWindowResize : false,
63472 resizeColumns : function() {
63473 var col = (this.view.el.getWidth() / 7) - 3;
63474 // loop through cols, and setWidth
63475 for(var i =0 ; i < 7 ; i++){
63476 this.cm.setColumnWidth(i, col);
63479 setDate :function(date) {
63481 Roo.log('setDate?');
63483 this.resizeColumns();
63484 var vd = this.activeDate;
63485 this.activeDate = date;
63486 // if(vd && this.el){
63487 // var t = date.getTime();
63488 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63489 // Roo.log('using add remove');
63491 // this.fireEvent('monthchange', this, date);
63493 // this.cells.removeClass("fc-state-highlight");
63494 // this.cells.each(function(c){
63495 // if(c.dateValue == t){
63496 // c.addClass("fc-state-highlight");
63497 // setTimeout(function(){
63498 // try{c.dom.firstChild.focus();}catch(e){}
63508 var days = date.getDaysInMonth();
63510 var firstOfMonth = date.getFirstDateOfMonth();
63511 var startingPos = firstOfMonth.getDay()-this.startDay;
63513 if(startingPos < this.startDay){
63517 var pm = date.add(Date.MONTH, -1);
63518 var prevStart = pm.getDaysInMonth()-startingPos;
63522 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63524 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63525 //this.cells.addClassOnOver('fc-state-hover');
63527 var cells = this.cells.elements;
63528 var textEls = this.textNodes;
63530 //Roo.each(cells, function(cell){
63531 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63534 days += startingPos;
63536 // convert everything to numbers so it's fast
63537 var day = 86400000;
63538 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63541 //Roo.log(prevStart);
63543 var today = new Date().clearTime().getTime();
63544 var sel = date.clearTime().getTime();
63545 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63546 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63547 var ddMatch = this.disabledDatesRE;
63548 var ddText = this.disabledDatesText;
63549 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63550 var ddaysText = this.disabledDaysText;
63551 var format = this.format;
63553 var setCellClass = function(cal, cell){
63555 //Roo.log('set Cell Class');
63557 var t = d.getTime();
63562 cell.dateValue = t;
63564 cell.className += " fc-today";
63565 cell.className += " fc-state-highlight";
63566 cell.title = cal.todayText;
63569 // disable highlight in other month..
63570 cell.className += " fc-state-highlight";
63575 //cell.className = " fc-state-disabled";
63576 cell.title = cal.minText;
63580 //cell.className = " fc-state-disabled";
63581 cell.title = cal.maxText;
63585 if(ddays.indexOf(d.getDay()) != -1){
63586 // cell.title = ddaysText;
63587 // cell.className = " fc-state-disabled";
63590 if(ddMatch && format){
63591 var fvalue = d.dateFormat(format);
63592 if(ddMatch.test(fvalue)){
63593 cell.title = ddText.replace("%0", fvalue);
63594 cell.className = " fc-state-disabled";
63598 if (!cell.initialClassName) {
63599 cell.initialClassName = cell.dom.className;
63602 cell.dom.className = cell.initialClassName + ' ' + cell.className;
63607 for(; i < startingPos; i++) {
63608 cells[i].dayName = (++prevStart);
63609 Roo.log(textEls[i]);
63610 d.setDate(d.getDate()+1);
63612 //cells[i].className = "fc-past fc-other-month";
63613 setCellClass(this, cells[i]);
63618 for(; i < days; i++){
63619 intDay = i - startingPos + 1;
63620 cells[i].dayName = (intDay);
63621 d.setDate(d.getDate()+1);
63623 cells[i].className = ''; // "x-date-active";
63624 setCellClass(this, cells[i]);
63628 for(; i < 42; i++) {
63629 //textEls[i].innerHTML = (++extraDays);
63631 d.setDate(d.getDate()+1);
63632 cells[i].dayName = (++extraDays);
63633 cells[i].className = "fc-future fc-other-month";
63634 setCellClass(this, cells[i]);
63637 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63639 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63641 // this will cause all the cells to mis
63644 for (var r = 0;r < 6;r++) {
63645 for (var c =0;c < 7;c++) {
63646 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63650 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63651 for(i=0;i<cells.length;i++) {
63653 this.cells.elements[i].dayName = cells[i].dayName ;
63654 this.cells.elements[i].className = cells[i].className;
63655 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63656 this.cells.elements[i].title = cells[i].title ;
63657 this.cells.elements[i].dateValue = cells[i].dateValue ;
63663 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63664 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63666 ////if(totalRows != 6){
63667 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63668 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63671 this.fireEvent('monthchange', this, date);
63676 * Returns the grid's SelectionModel.
63677 * @return {SelectionModel}
63679 getSelectionModel : function(){
63680 if(!this.selModel){
63681 this.selModel = new Roo.grid.CellSelectionModel();
63683 return this.selModel;
63687 this.eventStore.load()
63693 findCell : function(dt) {
63694 dt = dt.clearTime().getTime();
63696 this.cells.each(function(c){
63697 //Roo.log("check " +c.dateValue + '?=' + dt);
63698 if(c.dateValue == dt){
63708 findCells : function(rec) {
63709 var s = rec.data.start_dt.clone().clearTime().getTime();
63711 var e= rec.data.end_dt.clone().clearTime().getTime();
63714 this.cells.each(function(c){
63715 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63717 if(c.dateValue > e){
63720 if(c.dateValue < s){
63729 findBestRow: function(cells)
63733 for (var i =0 ; i < cells.length;i++) {
63734 ret = Math.max(cells[i].rows || 0,ret);
63741 addItem : function(rec)
63743 // look for vertical location slot in
63744 var cells = this.findCells(rec);
63746 rec.row = this.findBestRow(cells);
63748 // work out the location.
63752 for(var i =0; i < cells.length; i++) {
63760 if (crow.start.getY() == cells[i].getY()) {
63762 crow.end = cells[i];
63778 for (var i = 0; i < cells.length;i++) {
63779 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63786 clearEvents: function() {
63788 if (!this.eventStore.getCount()) {
63791 // reset number of rows in cells.
63792 Roo.each(this.cells.elements, function(c){
63796 this.eventStore.each(function(e) {
63797 this.clearEvent(e);
63802 clearEvent : function(ev)
63805 Roo.each(ev.els, function(el) {
63806 el.un('mouseenter' ,this.onEventEnter, this);
63807 el.un('mouseleave' ,this.onEventLeave, this);
63815 renderEvent : function(ev,ctr) {
63817 ctr = this.view.el.select('.fc-event-container',true).first();
63821 this.clearEvent(ev);
63827 var cells = ev.cells;
63828 var rows = ev.rows;
63829 this.fireEvent('eventrender', this, ev);
63831 for(var i =0; i < rows.length; i++) {
63835 cls += ' fc-event-start';
63837 if ((i+1) == rows.length) {
63838 cls += ' fc-event-end';
63841 //Roo.log(ev.data);
63842 // how many rows should it span..
63843 var cg = this.eventTmpl.append(ctr,Roo.apply({
63846 }, ev.data) , true);
63849 cg.on('mouseenter' ,this.onEventEnter, this, ev);
63850 cg.on('mouseleave' ,this.onEventLeave, this, ev);
63851 cg.on('click', this.onEventClick, this, ev);
63855 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63856 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63859 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
63860 cg.setWidth(ebox.right - sbox.x -2);
63864 renderEvents: function()
63866 // first make sure there is enough space..
63868 if (!this.eventTmpl) {
63869 this.eventTmpl = new Roo.Template(
63870 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
63871 '<div class="fc-event-inner">' +
63872 '<span class="fc-event-time">{time}</span>' +
63873 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63875 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
63883 this.cells.each(function(c) {
63884 //Roo.log(c.select('.fc-day-content div',true).first());
63885 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63888 var ctr = this.view.el.select('.fc-event-container',true).first();
63891 this.eventStore.each(function(ev){
63893 this.renderEvent(ev);
63897 this.view.layout();
63901 onEventEnter: function (e, el,event,d) {
63902 this.fireEvent('evententer', this, el, event);
63905 onEventLeave: function (e, el,event,d) {
63906 this.fireEvent('eventleave', this, el, event);
63909 onEventClick: function (e, el,event,d) {
63910 this.fireEvent('eventclick', this, el, event);
63913 onMonthChange: function () {
63917 onLoad: function () {
63919 //Roo.log('calendar onload');
63921 if(this.eventStore.getCount() > 0){
63925 this.eventStore.each(function(d){
63930 if (typeof(add.end_dt) == 'undefined') {
63931 Roo.log("Missing End time in calendar data: ");
63935 if (typeof(add.start_dt) == 'undefined') {
63936 Roo.log("Missing Start time in calendar data: ");
63940 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63941 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63942 add.id = add.id || d.id;
63943 add.title = add.title || '??';
63951 this.renderEvents();
63961 render : function ()
63965 if (!this.view.el.hasClass('course-timesheet')) {
63966 this.view.el.addClass('course-timesheet');
63968 if (this.tsStyle) {
63973 Roo.log(_this.grid.view.el.getWidth());
63976 this.tsStyle = Roo.util.CSS.createStyleSheet({
63977 '.course-timesheet .x-grid-row' : {
63980 '.x-grid-row td' : {
63981 'vertical-align' : 0
63983 '.course-edit-link' : {
63985 'text-overflow' : 'ellipsis',
63986 'overflow' : 'hidden',
63987 'white-space' : 'nowrap',
63988 'cursor' : 'pointer'
63993 '.de-act-sup-link' : {
63994 'color' : 'purple',
63995 'text-decoration' : 'line-through'
63999 'text-decoration' : 'line-through'
64001 '.course-timesheet .course-highlight' : {
64002 'border-top-style': 'dashed !important',
64003 'border-bottom-bottom': 'dashed !important'
64005 '.course-timesheet .course-item' : {
64006 'font-family' : 'tahoma, arial, helvetica',
64007 'font-size' : '11px',
64008 'overflow' : 'hidden',
64009 'padding-left' : '10px',
64010 'padding-right' : '10px',
64011 'padding-top' : '10px'
64019 monitorWindowResize : false,
64020 cellrenderer : function(v,x,r)
64025 xtype: 'CellSelectionModel',
64032 beforeload : function (_self, options)
64034 options.params = options.params || {};
64035 options.params._month = _this.monthField.getValue();
64036 options.params.limit = 9999;
64037 options.params['sort'] = 'when_dt';
64038 options.params['dir'] = 'ASC';
64039 this.proxy.loadResponse = this.loadResponse;
64041 //this.addColumns();
64043 load : function (_self, records, options)
64045 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64046 // if you click on the translation.. you can edit it...
64047 var el = Roo.get(this);
64048 var id = el.dom.getAttribute('data-id');
64049 var d = el.dom.getAttribute('data-date');
64050 var t = el.dom.getAttribute('data-time');
64051 //var id = this.child('span').dom.textContent;
64054 Pman.Dialog.CourseCalendar.show({
64058 productitem_active : id ? 1 : 0
64060 _this.grid.ds.load({});
64065 _this.panel.fireEvent('resize', [ '', '' ]);
64068 loadResponse : function(o, success, response){
64069 // this is overridden on before load..
64071 Roo.log("our code?");
64072 //Roo.log(success);
64073 //Roo.log(response)
64074 delete this.activeRequest;
64076 this.fireEvent("loadexception", this, o, response);
64077 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64082 result = o.reader.read(response);
64084 Roo.log("load exception?");
64085 this.fireEvent("loadexception", this, o, response, e);
64086 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64089 Roo.log("ready...");
64090 // loop through result.records;
64091 // and set this.tdate[date] = [] << array of records..
64093 Roo.each(result.records, function(r){
64095 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64096 _this.tdata[r.data.when_dt.format('j')] = [];
64098 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64101 //Roo.log(_this.tdata);
64103 result.records = [];
64104 result.totalRecords = 6;
64106 // let's generate some duumy records for the rows.
64107 //var st = _this.dateField.getValue();
64109 // work out monday..
64110 //st = st.add(Date.DAY, -1 * st.format('w'));
64112 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64114 var firstOfMonth = date.getFirstDayOfMonth();
64115 var days = date.getDaysInMonth();
64117 var firstAdded = false;
64118 for (var i = 0; i < result.totalRecords ; i++) {
64119 //var d= st.add(Date.DAY, i);
64122 for(var w = 0 ; w < 7 ; w++){
64123 if(!firstAdded && firstOfMonth != w){
64130 var dd = (d > 0 && d < 10) ? "0"+d : d;
64131 row['weekday'+w] = String.format(
64132 '<span style="font-size: 16px;"><b>{0}</b></span>'+
64133 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64135 date.format('Y-m-')+dd
64138 if(typeof(_this.tdata[d]) != 'undefined'){
64139 Roo.each(_this.tdata[d], function(r){
64143 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64144 if(r.parent_id*1>0){
64145 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64148 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64149 deactive = 'de-act-link';
64152 row['weekday'+w] += String.format(
64153 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64155 r.product_id_name, //1
64156 r.when_dt.format('h:ia'), //2
64166 // only do this if something added..
64168 result.records.push(_this.grid.dataSource.reader.newRow(row));
64172 // push it twice. (second one with an hour..
64176 this.fireEvent("load", this, o, o.request.arg);
64177 o.request.callback.call(o.request.scope, result, o.request.arg, true);
64179 sortInfo : {field: 'when_dt', direction : 'ASC' },
64181 xtype: 'HttpProxy',
64184 url : baseURL + '/Roo/Shop_course.php'
64187 xtype: 'JsonReader',
64204 'name': 'parent_id',
64208 'name': 'product_id',
64212 'name': 'productitem_id',
64230 click : function (_self, e)
64232 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64233 sd.setMonth(sd.getMonth()-1);
64234 _this.monthField.setValue(sd.format('Y-m-d'));
64235 _this.grid.ds.load({});
64241 xtype: 'Separator',
64245 xtype: 'MonthField',
64248 render : function (_self)
64250 _this.monthField = _self;
64251 // _this.monthField.set today
64253 select : function (combo, date)
64255 _this.grid.ds.load({});
64258 value : (function() { return new Date(); })()
64261 xtype: 'Separator',
64267 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64277 click : function (_self, e)
64279 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64280 sd.setMonth(sd.getMonth()+1);
64281 _this.monthField.setValue(sd.format('Y-m-d'));
64282 _this.grid.ds.load({});
64295 * Ext JS Library 1.1.1
64296 * Copyright(c) 2006-2007, Ext JS, LLC.
64298 * Originally Released Under LGPL - original licence link has changed is not relivant.
64301 * <script type="text/javascript">
64305 * @class Roo.LoadMask
64306 * A simple utility class for generically masking elements while loading data. If the element being masked has
64307 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64308 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
64309 * element's UpdateManager load indicator and will be destroyed after the initial load.
64311 * Create a new LoadMask
64312 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64313 * @param {Object} config The config object
64315 Roo.LoadMask = function(el, config){
64316 this.el = Roo.get(el);
64317 Roo.apply(this, config);
64319 this.store.on('beforeload', this.onBeforeLoad, this);
64320 this.store.on('load', this.onLoad, this);
64321 this.store.on('loadexception', this.onLoadException, this);
64322 this.removeMask = false;
64324 var um = this.el.getUpdateManager();
64325 um.showLoadIndicator = false; // disable the default indicator
64326 um.on('beforeupdate', this.onBeforeLoad, this);
64327 um.on('update', this.onLoad, this);
64328 um.on('failure', this.onLoad, this);
64329 this.removeMask = true;
64333 Roo.LoadMask.prototype = {
64335 * @cfg {Boolean} removeMask
64336 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64337 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
64339 removeMask : false,
64341 * @cfg {String} msg
64342 * The text to display in a centered loading message box (defaults to 'Loading...')
64344 msg : 'Loading...',
64346 * @cfg {String} msgCls
64347 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64349 msgCls : 'x-mask-loading',
64352 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64358 * Disables the mask to prevent it from being displayed
64360 disable : function(){
64361 this.disabled = true;
64365 * Enables the mask so that it can be displayed
64367 enable : function(){
64368 this.disabled = false;
64371 onLoadException : function()
64373 Roo.log(arguments);
64375 if (typeof(arguments[3]) != 'undefined') {
64376 Roo.MessageBox.alert("Error loading",arguments[3]);
64380 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64381 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64388 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64391 onLoad : function()
64393 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64397 onBeforeLoad : function(){
64398 if(!this.disabled){
64399 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64404 destroy : function(){
64406 this.store.un('beforeload', this.onBeforeLoad, this);
64407 this.store.un('load', this.onLoad, this);
64408 this.store.un('loadexception', this.onLoadException, this);
64410 var um = this.el.getUpdateManager();
64411 um.un('beforeupdate', this.onBeforeLoad, this);
64412 um.un('update', this.onLoad, this);
64413 um.un('failure', this.onLoad, this);
64418 * Ext JS Library 1.1.1
64419 * Copyright(c) 2006-2007, Ext JS, LLC.
64421 * Originally Released Under LGPL - original licence link has changed is not relivant.
64424 * <script type="text/javascript">
64429 * @class Roo.XTemplate
64430 * @extends Roo.Template
64431 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64433 var t = new Roo.XTemplate(
64434 '<select name="{name}">',
64435 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
64439 // then append, applying the master template values
64442 * Supported features:
64447 {a_variable} - output encoded.
64448 {a_variable.format:("Y-m-d")} - call a method on the variable
64449 {a_variable:raw} - unencoded output
64450 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64451 {a_variable:this.method_on_template(...)} - call a method on the template object.
64456 <tpl for="a_variable or condition.."></tpl>
64457 <tpl if="a_variable or condition"></tpl>
64458 <tpl exec="some javascript"></tpl>
64459 <tpl name="named_template"></tpl> (experimental)
64461 <tpl for="."></tpl> - just iterate the property..
64462 <tpl for=".."></tpl> - iterates with the parent (probably the template)
64466 Roo.XTemplate = function()
64468 Roo.XTemplate.superclass.constructor.apply(this, arguments);
64475 Roo.extend(Roo.XTemplate, Roo.Template, {
64478 * The various sub templates
64483 * basic tag replacing syntax
64486 * // you can fake an object call by doing this
64490 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64493 * compile the template
64495 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64498 compile: function()
64502 s = ['<tpl>', s, '</tpl>'].join('');
64504 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64505 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64506 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
64507 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64508 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
64513 while(true == !!(m = s.match(re))){
64514 var forMatch = m[0].match(nameRe),
64515 ifMatch = m[0].match(ifRe),
64516 execMatch = m[0].match(execRe),
64517 namedMatch = m[0].match(namedRe),
64522 name = forMatch && forMatch[1] ? forMatch[1] : '';
64525 // if - puts fn into test..
64526 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64528 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64533 // exec - calls a function... returns empty if true is returned.
64534 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64536 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64544 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64545 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64546 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64549 var uid = namedMatch ? namedMatch[1] : id;
64553 id: namedMatch ? namedMatch[1] : id,
64560 s = s.replace(m[0], '');
64562 s = s.replace(m[0], '{xtpl'+ id + '}');
64567 for(var i = tpls.length-1; i >= 0; --i){
64568 this.compileTpl(tpls[i]);
64569 this.tpls[tpls[i].id] = tpls[i];
64571 this.master = tpls[tpls.length-1];
64575 * same as applyTemplate, except it's done to one of the subTemplates
64576 * when using named templates, you can do:
64578 * var str = pl.applySubTemplate('your-name', values);
64581 * @param {Number} id of the template
64582 * @param {Object} values to apply to template
64583 * @param {Object} parent (normaly the instance of this object)
64585 applySubTemplate : function(id, values, parent)
64589 var t = this.tpls[id];
64593 if(t.test && !t.test.call(this, values, parent)){
64597 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64598 Roo.log(e.toString());
64604 if(t.exec && t.exec.call(this, values, parent)){
64608 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64609 Roo.log(e.toString());
64614 var vs = t.target ? t.target.call(this, values, parent) : values;
64615 parent = t.target ? values : parent;
64616 if(t.target && vs instanceof Array){
64618 for(var i = 0, len = vs.length; i < len; i++){
64619 buf[buf.length] = t.compiled.call(this, vs[i], parent);
64621 return buf.join('');
64623 return t.compiled.call(this, vs, parent);
64625 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64626 Roo.log(e.toString());
64627 Roo.log(t.compiled);
64632 compileTpl : function(tpl)
64634 var fm = Roo.util.Format;
64635 var useF = this.disableFormats !== true;
64636 var sep = Roo.isGecko ? "+" : ",";
64637 var undef = function(str) {
64638 Roo.log("Property not found :" + str);
64642 var fn = function(m, name, format, args)
64644 //Roo.log(arguments);
64645 args = args ? args.replace(/\\'/g,"'") : args;
64646 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64647 if (typeof(format) == 'undefined') {
64648 format= 'htmlEncode';
64650 if (format == 'raw' ) {
64654 if(name.substr(0, 4) == 'xtpl'){
64655 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64658 // build an array of options to determine if value is undefined..
64660 // basically get 'xxxx.yyyy' then do
64661 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64662 // (function () { Roo.log("Property not found"); return ''; })() :
64667 Roo.each(name.split('.'), function(st) {
64668 lookfor += (lookfor.length ? '.': '') + st;
64669 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
64672 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64675 if(format && useF){
64677 args = args ? ',' + args : "";
64679 if(format.substr(0, 5) != "this."){
64680 format = "fm." + format + '(';
64682 format = 'this.call("'+ format.substr(5) + '", ';
64686 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
64690 // called with xxyx.yuu:(test,test)
64692 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
64694 // raw.. - :raw modifier..
64695 return "'"+ sep + udef_st + name + ")"+sep+"'";
64699 // branched to use + in gecko and [].join() in others
64701 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
64702 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64705 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
64706 body.push(tpl.body.replace(/(\r\n|\n)/g,
64707 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64708 body.push("'].join('');};};");
64709 body = body.join('');
64712 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64714 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
64720 applyTemplate : function(values){
64721 return this.master.compiled.call(this, values, {});
64722 //var s = this.subs;
64725 apply : function(){
64726 return this.applyTemplate.apply(this, arguments);
64731 Roo.XTemplate.from = function(el){
64732 el = Roo.getDom(el);
64733 return new Roo.XTemplate(el.value || el.innerHTML);