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 * Make the first letter of a string uppercase
961 * @return {String} The new string.
963 String.prototype.toUpperCaseFirst = function () {
964 return this.charAt(0).toUpperCase() + this.slice(1);
969 * Ext JS Library 1.1.1
970 * Copyright(c) 2006-2007, Ext JS, LLC.
972 * Originally Released Under LGPL - original licence link has changed is not relivant.
975 * <script type="text/javascript">
981 Roo.applyIf(Number.prototype, {
983 * Checks whether or not the current number is within a desired range. If the number is already within the
984 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985 * exceeded. Note that this method returns the constrained value but does not change the current number.
986 * @param {Number} min The minimum number in the range
987 * @param {Number} max The maximum number in the range
988 * @return {Number} The constrained value if outside the range, otherwise the current value
990 constrain : function(min, max){
991 return Math.min(Math.max(this, min), max);
995 * Ext JS Library 1.1.1
996 * Copyright(c) 2006-2007, Ext JS, LLC.
998 * Originally Released Under LGPL - original licence link has changed is not relivant.
1001 * <script type="text/javascript">
1006 Roo.applyIf(Array.prototype, {
1009 * Checks whether or not the specified object exists in the array.
1010 * @param {Object} o The object to check for
1011 * @return {Number} The index of o in the array (or -1 if it is not found)
1013 indexOf : function(o){
1014 for (var i = 0, len = this.length; i < len; i++){
1015 if(this[i] == o) { return i; }
1021 * Removes the specified object from the array. If the object is not found nothing happens.
1022 * @param {Object} o The object to remove
1024 remove : function(o){
1025 var index = this.indexOf(o);
1027 this.splice(index, 1);
1031 * Map (JS 1.6 compatibility)
1032 * @param {Function} function to call
1034 map : function(fun )
1036 var len = this.length >>> 0;
1037 if (typeof fun != "function") {
1038 throw new TypeError();
1040 var res = new Array(len);
1041 var thisp = arguments[1];
1042 for (var i = 0; i < len; i++)
1045 res[i] = fun.call(thisp, this[i], i, this);
1053 * @param {Array} o The array to compare to
1054 * @returns {Boolean} true if the same
1056 equals : function(b)
1058 // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1065 if (this.length !== b.length) {
1069 // sort?? a.sort().equals(b.sort());
1071 for (var i = 0; i < this.length; ++i) {
1072 if (this[i] !== b[i]) {
1084 Roo.applyIf(Array, {
1088 * @param {Array} o Or Array like object (eg. nodelist)
1095 for (var i =0; i < o.length; i++) {
1104 * Ext JS Library 1.1.1
1105 * Copyright(c) 2006-2007, Ext JS, LLC.
1107 * Originally Released Under LGPL - original licence link has changed is not relivant.
1110 * <script type="text/javascript">
1116 * The date parsing and format syntax is a subset of
1117 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118 * supported will provide results equivalent to their PHP versions.
1120 * Following is the list of all currently supported formats:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1125 Format Output Description
1126 ------ ---------- --------------------------------------------------------------
1127 d 10 Day of the month, 2 digits with leading zeros
1128 D Wed A textual representation of a day, three letters
1129 j 10 Day of the month without leading zeros
1130 l Wednesday A full textual representation of the day of the week
1131 S th English ordinal day of month suffix, 2 chars (use with j)
1132 w 3 Numeric representation of the day of the week
1133 z 9 The julian date, or day of the year (0-365)
1134 W 01 ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135 F January A full textual representation of the month
1136 m 01 Numeric representation of a month, with leading zeros
1137 M Jan Month name abbreviation, three letters
1138 n 1 Numeric representation of a month, without leading zeros
1139 t 31 Number of days in the given month
1140 L 0 Whether it's a leap year (1 if it is a leap year, else 0)
1141 Y 2007 A full numeric representation of a year, 4 digits
1142 y 07 A two digit representation of a year
1143 a pm Lowercase Ante meridiem and Post meridiem
1144 A PM Uppercase Ante meridiem and Post meridiem
1145 g 3 12-hour format of an hour without leading zeros
1146 G 15 24-hour format of an hour without leading zeros
1147 h 03 12-hour format of an hour with leading zeros
1148 H 15 24-hour format of an hour with leading zeros
1149 i 05 Minutes with leading zeros
1150 s 01 Seconds, with leading zeros
1151 O -0600 Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152 P -06:00 Difference to Greenwich time (GMT) with colon between hours and minutes
1153 T CST Timezone setting of the machine running the code
1154 Z -21600 Timezone offset in seconds (negative if west of UTC, positive if east)
1157 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d')); //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a')); //January 10, 2007, 3:05 pm
1162 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
1165 * Here are some standard date/time patterns that you might find helpful. They
1166 * are not part of the source of Date.js, but to use them you can simply copy this
1167 * block of code into any script that is included after Date.js and they will also become
1168 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
1171 ISO8601Long:"Y-m-d H:i:s",
1172 ISO8601Short:"Y-m-d",
1174 LongDate: "l, F d, Y",
1175 FullDateTime: "l, F d, Y g:i:s A",
1178 LongTime: "g:i:s A",
1179 SortableDateTime: "Y-m-d\\TH:i:s",
1180 UniversalSortableDateTime: "Y-m-d H:i:sO",
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1193 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194 * They generate precompiled functions from date formats instead of parsing and
1195 * processing the pattern every time you format a date. These functions are available
1196 * on every Date object (any javascript function).
1198 * The original article and download are here:
1199 * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1206 Returns the number of milliseconds between this date and date
1207 @param {Date} date (optional) Defaults to now
1208 @param {Date} date (optional) Defaults to now
1209 @param {String} interval A valid date interval enum value (eg. Date.DAY)
1210 @return {Number} The diff in milliseconds
1211 @member Date getElapsed
1213 Date.prototype.getElapsed = function(date, interval)
1215 date = date || new Date();
1216 var ret = Math.abs(date.getTime()-this.getTime());
1220 return Math.floor(ret / (1000));
1222 return Math.floor(ret / (100*60));
1224 return Math.floor(ret / (100*60*60));
1226 return Math.floor(ret / (100*60*60*24));
1227 case Date.MONTH: // this does not give exact number...??
1228 return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1229 case Date.YEAR: // this does not give exact number...??
1230 return (date.format("Y") - this.format("Y"));
1238 // was in date file..
1242 Date.parseFunctions = {count:0};
1244 Date.parseRegexes = [];
1246 Date.formatFunctions = {count:0};
1249 Date.prototype.dateFormat = function(format) {
1250 if (Date.formatFunctions[format] == null) {
1251 Date.createNewFormat(format);
1253 var func = Date.formatFunctions[format];
1254 return this[func]();
1259 * Formats a date given the supplied format string
1260 * @param {String} format The format string
1261 * @return {String} The formatted date
1264 Date.prototype.format = Date.prototype.dateFormat;
1267 Date.createNewFormat = function(format) {
1268 var funcName = "format" + Date.formatFunctions.count++;
1269 Date.formatFunctions[format] = funcName;
1270 var code = "Date.prototype." + funcName + " = function(){return ";
1271 var special = false;
1273 for (var i = 0; i < format.length; ++i) {
1274 ch = format.charAt(i);
1275 if (!special && ch == "\\") {
1280 code += "'" + String.escape(ch) + "' + ";
1283 code += Date.getFormatCode(ch);
1286 /** eval:var:zzzzzzzzzzzzz */
1287 eval(code.substring(0, code.length - 3) + ";}");
1291 Date.getFormatCode = function(character) {
1292 switch (character) {
1294 return "String.leftPad(this.getDate(), 2, '0') + ";
1296 return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1298 return "this.getDate() + ";
1300 return "Date.dayNames[this.getDay()] + ";
1302 return "this.getSuffix() + ";
1304 return "this.getDay() + ";
1306 return "this.getDayOfYear() + ";
1308 return "this.getWeekOfYear() + ";
1310 return "Date.monthNames[this.getMonth()] + ";
1312 return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1314 return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1316 return "(this.getMonth() + 1) + ";
1318 return "this.getDaysInMonth() + ";
1320 return "(this.isLeapYear() ? 1 : 0) + ";
1322 return "this.getFullYear() + ";
1324 return "('' + this.getFullYear()).substring(2, 4) + ";
1326 return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1328 return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1330 return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1332 return "this.getHours() + ";
1334 return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1336 return "String.leftPad(this.getHours(), 2, '0') + ";
1338 return "String.leftPad(this.getMinutes(), 2, '0') + ";
1340 return "String.leftPad(this.getSeconds(), 2, '0') + ";
1342 return "this.getGMTOffset() + ";
1344 return "this.getGMTColonOffset() + ";
1346 return "this.getTimezone() + ";
1348 return "(this.getTimezoneOffset() * -60) + ";
1350 return "'" + String.escape(character) + "' + ";
1355 * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1356 * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates. Any part of
1357 * the date format that is not specified will default to the current date value for that part. Time parts can also
1358 * be specified, but default to 0. Keep in mind that the input date string must precisely match the specified format
1359 * string or the parse operation will fail.
1362 //dt = Fri May 25 2007 (current date)
1363 var dt = new Date();
1365 //dt = Thu May 25 2006 (today's month/day in 2006)
1366 dt = Date.parseDate("2006", "Y");
1368 //dt = Sun Jan 15 2006 (all date parts specified)
1369 dt = Date.parseDate("2006-1-15", "Y-m-d");
1371 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1372 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1374 * @param {String} input The unparsed date as a string
1375 * @param {String} format The format the date is in
1376 * @return {Date} The parsed date
1379 Date.parseDate = function(input, format) {
1380 if (Date.parseFunctions[format] == null) {
1381 Date.createParser(format);
1383 var func = Date.parseFunctions[format];
1384 return Date[func](input);
1390 Date.createParser = function(format) {
1391 var funcName = "parse" + Date.parseFunctions.count++;
1392 var regexNum = Date.parseRegexes.length;
1393 var currentGroup = 1;
1394 Date.parseFunctions[format] = funcName;
1396 var code = "Date." + funcName + " = function(input){\n"
1397 + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1398 + "var d = new Date();\n"
1399 + "y = d.getFullYear();\n"
1400 + "m = d.getMonth();\n"
1401 + "d = d.getDate();\n"
1402 + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1403 + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1404 + "if (results && results.length > 0) {";
1407 var special = false;
1409 for (var i = 0; i < format.length; ++i) {
1410 ch = format.charAt(i);
1411 if (!special && ch == "\\") {
1416 regex += String.escape(ch);
1419 var obj = Date.formatCodeToRegex(ch, currentGroup);
1420 currentGroup += obj.g;
1422 if (obj.g && obj.c) {
1428 code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1429 + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1430 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1431 + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1432 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1433 + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1434 + "else if (y >= 0 && m >= 0 && d > 0)\n"
1435 + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1436 + "else if (y >= 0 && m >= 0)\n"
1437 + "{v = new Date(y, m); v.setFullYear(y);}\n"
1438 + "else if (y >= 0)\n"
1439 + "{v = new Date(y); v.setFullYear(y);}\n"
1440 + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1441 + " ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1442 + " v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1445 Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1446 /** eval:var:zzzzzzzzzzzzz */
1451 Date.formatCodeToRegex = function(character, currentGroup) {
1452 switch (character) {
1456 s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1459 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1460 s:"(\\d{1,2})"}; // day of month without leading zeroes
1463 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1464 s:"(\\d{2})"}; // day of month with leading zeroes
1468 s:"(?:" + Date.dayNames.join("|") + ")"};
1472 s:"(?:st|nd|rd|th)"};
1487 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1488 s:"(" + Date.monthNames.join("|") + ")"};
1491 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1492 s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1495 c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1496 s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1499 c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1500 s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1511 c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1515 c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1516 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1520 c:"if (results[" + currentGroup + "] == 'am') {\n"
1521 + "if (h == 12) { h = 0; }\n"
1522 + "} else { if (h < 12) { h += 12; }}",
1526 c:"if (results[" + currentGroup + "] == 'AM') {\n"
1527 + "if (h == 12) { h = 0; }\n"
1528 + "} else { if (h < 12) { h += 12; }}",
1533 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1534 s:"(\\d{1,2})"}; // 12/24-hr format format of an hour without leading zeroes
1538 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1539 s:"(\\d{2})"}; // 12/24-hr format format of an hour with leading zeroes
1542 c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1546 c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1551 "o = results[", currentGroup, "];\n",
1552 "var sn = o.substring(0,1);\n", // get + / - sign
1553 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1554 "var mn = o.substring(3,5) % 60;\n", // get minutes
1555 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1556 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1558 s:"([+\-]\\d{2,4})"};
1564 "o = results[", currentGroup, "];\n",
1565 "var sn = o.substring(0,1);\n",
1566 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1567 "var mn = o.substring(4,6) % 60;\n",
1568 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1569 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1575 s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1578 c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1579 + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1580 s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1584 s:String.escape(character)};
1589 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1590 * @return {String} The abbreviated timezone name (e.g. 'CST')
1592 Date.prototype.getTimezone = function() {
1593 return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1597 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1598 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1600 Date.prototype.getGMTOffset = function() {
1601 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1602 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1603 + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1607 * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1608 * @return {String} 2-characters representing hours and 2-characters representing minutes
1609 * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1611 Date.prototype.getGMTColonOffset = function() {
1612 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1613 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1615 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1619 * Get the numeric day number of the year, adjusted for leap year.
1620 * @return {Number} 0 through 364 (365 in leap years)
1622 Date.prototype.getDayOfYear = function() {
1624 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1625 for (var i = 0; i < this.getMonth(); ++i) {
1626 num += Date.daysInMonth[i];
1628 return num + this.getDate() - 1;
1632 * Get the string representation of the numeric week number of the year
1633 * (equivalent to the format specifier 'W').
1634 * @return {String} '00' through '52'
1636 Date.prototype.getWeekOfYear = function() {
1637 // Skip to Thursday of this week
1638 var now = this.getDayOfYear() + (4 - this.getDay());
1639 // Find the first Thursday of the year
1640 var jan1 = new Date(this.getFullYear(), 0, 1);
1641 var then = (7 - jan1.getDay() + 4);
1642 return String.leftPad(((now - then) / 7) + 1, 2, "0");
1646 * Whether or not the current date is in a leap year.
1647 * @return {Boolean} True if the current date is in a leap year, else false
1649 Date.prototype.isLeapYear = function() {
1650 var year = this.getFullYear();
1651 return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1655 * Get the first day of the current month, adjusted for leap year. The returned value
1656 * is the numeric day index within the week (0-6) which can be used in conjunction with
1657 * the {@link #monthNames} array to retrieve the textual day name.
1660 var dt = new Date('1/10/2007');
1661 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1663 * @return {Number} The day number (0-6)
1665 Date.prototype.getFirstDayOfMonth = function() {
1666 var day = (this.getDay() - (this.getDate() - 1)) % 7;
1667 return (day < 0) ? (day + 7) : day;
1671 * Get the last day of the current month, adjusted for leap year. The returned value
1672 * is the numeric day index within the week (0-6) which can be used in conjunction with
1673 * the {@link #monthNames} array to retrieve the textual day name.
1676 var dt = new Date('1/10/2007');
1677 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1679 * @return {Number} The day number (0-6)
1681 Date.prototype.getLastDayOfMonth = function() {
1682 var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1683 return (day < 0) ? (day + 7) : day;
1688 * Get the first date of this date's month
1691 Date.prototype.getFirstDateOfMonth = function() {
1692 return new Date(this.getFullYear(), this.getMonth(), 1);
1696 * Get the last date of this date's month
1699 Date.prototype.getLastDateOfMonth = function() {
1700 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1703 * Get the number of days in the current month, adjusted for leap year.
1704 * @return {Number} The number of days in the month
1706 Date.prototype.getDaysInMonth = function() {
1707 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1708 return Date.daysInMonth[this.getMonth()];
1712 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1713 * @return {String} 'st, 'nd', 'rd' or 'th'
1715 Date.prototype.getSuffix = function() {
1716 switch (this.getDate()) {
1733 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1736 * An array of textual month names.
1737 * Override these values for international dates, for example...
1738 * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1757 * An array of textual day names.
1758 * Override these values for international dates, for example...
1759 * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1775 Date.monthNumbers = {
1790 * Creates and returns a new Date instance with the exact same date value as the called instance.
1791 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1792 * variable will also be changed. When the intention is to create a new variable that will not
1793 * modify the original instance, you should create a clone.
1795 * Example of correctly cloning a date:
1798 var orig = new Date('10/1/2006');
1801 document.write(orig); //returns 'Thu Oct 05 2006'!
1804 var orig = new Date('10/1/2006');
1805 var copy = orig.clone();
1807 document.write(orig); //returns 'Thu Oct 01 2006'
1809 * @return {Date} The new Date instance
1811 Date.prototype.clone = function() {
1812 return new Date(this.getTime());
1816 * Clears any time information from this date
1817 @param {Boolean} clone true to create a clone of this date, clear the time and return it
1818 @return {Date} this or the clone
1820 Date.prototype.clearTime = function(clone){
1822 return this.clone().clearTime();
1827 this.setMilliseconds(0);
1832 // safari setMonth is broken -- check that this is only donw once...
1833 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1834 Date.brokenSetMonth = Date.prototype.setMonth;
1835 Date.prototype.setMonth = function(num){
1837 var n = Math.ceil(-num);
1838 var back_year = Math.ceil(n/12);
1839 var month = (n % 12) ? 12 - n % 12 : 0 ;
1840 this.setFullYear(this.getFullYear() - back_year);
1841 return Date.brokenSetMonth.call(this, month);
1843 return Date.brokenSetMonth.apply(this, arguments);
1848 /** Date interval constant
1852 /** Date interval constant
1856 /** Date interval constant
1860 /** Date interval constant
1864 /** Date interval constant
1868 /** Date interval constant
1872 /** Date interval constant
1878 * Provides a convenient method of performing basic date arithmetic. This method
1879 * does not modify the Date instance being called - it creates and returns
1880 * a new Date instance containing the resulting date value.
1885 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1886 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1888 //Negative values will subtract correctly:
1889 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1890 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1892 //You can even chain several calls together in one line!
1893 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1894 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1897 * @param {String} interval A valid date interval enum value
1898 * @param {Number} value The amount to add to the current date
1899 * @return {Date} The new Date instance
1901 Date.prototype.add = function(interval, value){
1902 var d = this.clone();
1903 if (!interval || value === 0) { return d; }
1904 switch(interval.toLowerCase()){
1906 d.setMilliseconds(this.getMilliseconds() + value);
1909 d.setSeconds(this.getSeconds() + value);
1912 d.setMinutes(this.getMinutes() + value);
1915 d.setHours(this.getHours() + value);
1918 d.setDate(this.getDate() + value);
1921 var day = this.getDate();
1923 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1926 d.setMonth(this.getMonth() + value);
1929 d.setFullYear(this.getFullYear() + value);
1935 * @class Roo.lib.Dom
1939 * Dom utils (from YIU afaik)
1945 * Get the view width
1946 * @param {Boolean} full True will get the full document, otherwise it's the view width
1947 * @return {Number} The width
1950 getViewWidth : function(full) {
1951 return full ? this.getDocumentWidth() : this.getViewportWidth();
1954 * Get the view height
1955 * @param {Boolean} full True will get the full document, otherwise it's the view height
1956 * @return {Number} The height
1958 getViewHeight : function(full) {
1959 return full ? this.getDocumentHeight() : this.getViewportHeight();
1962 * Get the Full Document height
1963 * @return {Number} The height
1965 getDocumentHeight: function() {
1966 var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1967 return Math.max(scrollHeight, this.getViewportHeight());
1970 * Get the Full Document width
1971 * @return {Number} The width
1973 getDocumentWidth: function() {
1974 var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1975 return Math.max(scrollWidth, this.getViewportWidth());
1978 * Get the Window Viewport height
1979 * @return {Number} The height
1981 getViewportHeight: function() {
1982 var height = self.innerHeight;
1983 var mode = document.compatMode;
1985 if ((mode || Roo.isIE) && !Roo.isOpera) {
1986 height = (mode == "CSS1Compat") ?
1987 document.documentElement.clientHeight :
1988 document.body.clientHeight;
1994 * Get the Window Viewport width
1995 * @return {Number} The width
1997 getViewportWidth: function() {
1998 var width = self.innerWidth;
1999 var mode = document.compatMode;
2001 if (mode || Roo.isIE) {
2002 width = (mode == "CSS1Compat") ?
2003 document.documentElement.clientWidth :
2004 document.body.clientWidth;
2009 isAncestor : function(p, c) {
2016 if (p.contains && !Roo.isSafari) {
2017 return p.contains(c);
2018 } else if (p.compareDocumentPosition) {
2019 return !!(p.compareDocumentPosition(c) & 16);
2021 var parent = c.parentNode;
2026 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2029 parent = parent.parentNode;
2035 getRegion : function(el) {
2036 return Roo.lib.Region.getRegion(el);
2039 getY : function(el) {
2040 return this.getXY(el)[1];
2043 getX : function(el) {
2044 return this.getXY(el)[0];
2047 getXY : function(el) {
2048 var p, pe, b, scroll, bd = document.body;
2049 el = Roo.getDom(el);
2050 var fly = Roo.lib.AnimBase.fly;
2051 if (el.getBoundingClientRect) {
2052 b = el.getBoundingClientRect();
2053 scroll = fly(document).getScroll();
2054 return [b.left + scroll.left, b.top + scroll.top];
2060 var hasAbsolute = fly(el).getStyle("position") == "absolute";
2067 if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2074 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2075 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2082 if (p != el && pe.getStyle('overflow') != 'visible') {
2090 if (Roo.isSafari && hasAbsolute) {
2095 if (Roo.isGecko && !hasAbsolute) {
2097 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2098 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2102 while (p && p != bd) {
2103 if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2115 setXY : function(el, xy) {
2116 el = Roo.fly(el, '_setXY');
2118 var pts = el.translatePoints(xy);
2119 if (xy[0] !== false) {
2120 el.dom.style.left = pts.left + "px";
2122 if (xy[1] !== false) {
2123 el.dom.style.top = pts.top + "px";
2127 setX : function(el, x) {
2128 this.setXY(el, [x, false]);
2131 setY : function(el, y) {
2132 this.setXY(el, [false, y]);
2136 * Portions of this file are based on pieces of Yahoo User Interface Library
2137 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2138 * YUI licensed under the BSD License:
2139 * http://developer.yahoo.net/yui/license.txt
2140 * <script type="text/javascript">
2144 Roo.lib.Event = function() {
2145 var loadComplete = false;
2147 var unloadListeners = [];
2149 var onAvailStack = [];
2151 var lastError = null;
2164 startInterval: function() {
2165 if (!this._interval) {
2167 var callback = function() {
2168 self._tryPreloadAttach();
2170 this._interval = setInterval(callback, this.POLL_INTERVAL);
2175 onAvailable: function(p_id, p_fn, p_obj, p_override) {
2176 onAvailStack.push({ id: p_id,
2179 override: p_override,
2180 checkReady: false });
2182 retryCount = this.POLL_RETRYS;
2183 this.startInterval();
2187 addListener: function(el, eventName, fn) {
2188 el = Roo.getDom(el);
2193 if ("unload" == eventName) {
2194 unloadListeners[unloadListeners.length] =
2195 [el, eventName, fn];
2199 var wrappedFn = function(e) {
2200 return fn(Roo.lib.Event.getEvent(e));
2203 var li = [el, eventName, fn, wrappedFn];
2205 var index = listeners.length;
2206 listeners[index] = li;
2208 this.doAdd(el, eventName, wrappedFn, false);
2214 removeListener: function(el, eventName, fn) {
2217 el = Roo.getDom(el);
2220 return this.purgeElement(el, false, eventName);
2224 if ("unload" == eventName) {
2226 for (i = 0,len = unloadListeners.length; i < len; i++) {
2227 var li = unloadListeners[i];
2230 li[1] == eventName &&
2232 unloadListeners.splice(i, 1);
2240 var cacheItem = null;
2243 var index = arguments[3];
2245 if ("undefined" == typeof index) {
2246 index = this._getCacheIndex(el, eventName, fn);
2250 cacheItem = listeners[index];
2253 if (!el || !cacheItem) {
2257 this.doRemove(el, eventName, cacheItem[this.WFN], false);
2259 delete listeners[index][this.WFN];
2260 delete listeners[index][this.FN];
2261 listeners.splice(index, 1);
2268 getTarget: function(ev, resolveTextNode) {
2269 ev = ev.browserEvent || ev;
2270 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2271 var t = ev.target || ev.srcElement;
2272 return this.resolveTextNode(t);
2276 resolveTextNode: function(node) {
2277 if (Roo.isSafari && node && 3 == node.nodeType) {
2278 return node.parentNode;
2285 getPageX: function(ev) {
2286 ev = ev.browserEvent || ev;
2287 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2289 if (!x && 0 !== x) {
2290 x = ev.clientX || 0;
2293 x += this.getScroll()[1];
2301 getPageY: function(ev) {
2302 ev = ev.browserEvent || ev;
2303 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2305 if (!y && 0 !== y) {
2306 y = ev.clientY || 0;
2309 y += this.getScroll()[0];
2318 getXY: function(ev) {
2319 ev = ev.browserEvent || ev;
2320 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2321 return [this.getPageX(ev), this.getPageY(ev)];
2325 getRelatedTarget: function(ev) {
2326 ev = ev.browserEvent || ev;
2327 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2328 var t = ev.relatedTarget;
2330 if (ev.type == "mouseout") {
2332 } else if (ev.type == "mouseover") {
2337 return this.resolveTextNode(t);
2341 getTime: function(ev) {
2342 ev = ev.browserEvent || ev;
2343 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2345 var t = new Date().getTime();
2349 this.lastError = ex;
2358 stopEvent: function(ev) {
2359 this.stopPropagation(ev);
2360 this.preventDefault(ev);
2364 stopPropagation: function(ev) {
2365 ev = ev.browserEvent || ev;
2366 if (ev.stopPropagation) {
2367 ev.stopPropagation();
2369 ev.cancelBubble = true;
2374 preventDefault: function(ev) {
2375 ev = ev.browserEvent || ev;
2376 if(ev.preventDefault) {
2377 ev.preventDefault();
2379 ev.returnValue = false;
2384 getEvent: function(e) {
2385 var ev = e || window.event;
2387 var c = this.getEvent.caller;
2389 ev = c.arguments[0];
2390 if (ev && Event == ev.constructor) {
2400 getCharCode: function(ev) {
2401 ev = ev.browserEvent || ev;
2402 return ev.charCode || ev.keyCode || 0;
2406 _getCacheIndex: function(el, eventName, fn) {
2407 for (var i = 0,len = listeners.length; i < len; ++i) {
2408 var li = listeners[i];
2410 li[this.FN] == fn &&
2411 li[this.EL] == el &&
2412 li[this.TYPE] == eventName) {
2424 getEl: function(id) {
2425 return document.getElementById(id);
2429 clearCache: function() {
2433 _load: function(e) {
2434 loadComplete = true;
2435 var EU = Roo.lib.Event;
2439 EU.doRemove(window, "load", EU._load);
2444 _tryPreloadAttach: function() {
2453 var tryAgain = !loadComplete;
2455 tryAgain = (retryCount > 0);
2460 for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2461 var item = onAvailStack[i];
2463 var el = this.getEl(item.id);
2466 if (!item.checkReady ||
2469 (document && document.body)) {
2472 if (item.override) {
2473 if (item.override === true) {
2476 scope = item.override;
2479 item.fn.call(scope, item.obj);
2480 onAvailStack[i] = null;
2483 notAvail.push(item);
2488 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2492 this.startInterval();
2494 clearInterval(this._interval);
2495 this._interval = null;
2498 this.locked = false;
2505 purgeElement: function(el, recurse, eventName) {
2506 var elListeners = this.getListeners(el, eventName);
2508 for (var i = 0,len = elListeners.length; i < len; ++i) {
2509 var l = elListeners[i];
2510 this.removeListener(el, l.type, l.fn);
2514 if (recurse && el && el.childNodes) {
2515 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2516 this.purgeElement(el.childNodes[i], recurse, eventName);
2522 getListeners: function(el, eventName) {
2523 var results = [], searchLists;
2525 searchLists = [listeners, unloadListeners];
2526 } else if (eventName == "unload") {
2527 searchLists = [unloadListeners];
2529 searchLists = [listeners];
2532 for (var j = 0; j < searchLists.length; ++j) {
2533 var searchList = searchLists[j];
2534 if (searchList && searchList.length > 0) {
2535 for (var i = 0,len = searchList.length; i < len; ++i) {
2536 var l = searchList[i];
2537 if (l && l[this.EL] === el &&
2538 (!eventName || eventName === l[this.TYPE])) {
2543 adjust: l[this.ADJ_SCOPE],
2551 return (results.length) ? results : null;
2555 _unload: function(e) {
2557 var EU = Roo.lib.Event, i, j, l, len, index;
2559 for (i = 0,len = unloadListeners.length; i < len; ++i) {
2560 l = unloadListeners[i];
2563 if (l[EU.ADJ_SCOPE]) {
2564 if (l[EU.ADJ_SCOPE] === true) {
2567 scope = l[EU.ADJ_SCOPE];
2570 l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2571 unloadListeners[i] = null;
2577 unloadListeners = null;
2579 if (listeners && listeners.length > 0) {
2580 j = listeners.length;
2583 l = listeners[index];
2585 EU.removeListener(l[EU.EL], l[EU.TYPE],
2595 EU.doRemove(window, "unload", EU._unload);
2600 getScroll: function() {
2601 var dd = document.documentElement, db = document.body;
2602 if (dd && (dd.scrollTop || dd.scrollLeft)) {
2603 return [dd.scrollTop, dd.scrollLeft];
2605 return [db.scrollTop, db.scrollLeft];
2612 doAdd: function () {
2613 if (window.addEventListener) {
2614 return function(el, eventName, fn, capture) {
2615 el.addEventListener(eventName, fn, (capture));
2617 } else if (window.attachEvent) {
2618 return function(el, eventName, fn, capture) {
2619 el.attachEvent("on" + eventName, fn);
2628 doRemove: function() {
2629 if (window.removeEventListener) {
2630 return function (el, eventName, fn, capture) {
2631 el.removeEventListener(eventName, fn, (capture));
2633 } else if (window.detachEvent) {
2634 return function (el, eventName, fn) {
2635 el.detachEvent("on" + eventName, fn);
2647 var E = Roo.lib.Event;
2648 E.on = E.addListener;
2649 E.un = E.removeListener;
2651 if (document && document.body) {
2654 E.doAdd(window, "load", E._load);
2656 E.doAdd(window, "unload", E._unload);
2657 E._tryPreloadAttach();
2664 * @class Roo.lib.Ajax
2666 * provide a simple Ajax request utility functions
2668 * Portions of this file are based on pieces of Yahoo User Interface Library
2669 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2670 * YUI licensed under the BSD License:
2671 * http://developer.yahoo.net/yui/license.txt
2672 * <script type="text/javascript">
2680 request : function(method, uri, cb, data, options) {
2682 var hs = options.headers;
2685 if(hs.hasOwnProperty(h)){
2686 this.initHeader(h, hs[h], false);
2690 if(options.xmlData){
2691 this.initHeader('Content-Type', 'text/xml', false);
2693 data = options.xmlData;
2697 return this.asyncRequest(method, uri, cb, data);
2703 * @param {DomForm} form element
2704 * @return {String} urlencode form output.
2706 serializeForm : function(form) {
2707 if(typeof form == 'string') {
2708 form = (document.getElementById(form) || document.forms[form]);
2711 var el, name, val, disabled, data = '', hasSubmit = false;
2712 for (var i = 0; i < form.elements.length; i++) {
2713 el = form.elements[i];
2714 disabled = form.elements[i].disabled;
2715 name = form.elements[i].name;
2716 val = form.elements[i].value;
2718 if (!disabled && name){
2722 case 'select-multiple':
2723 for (var j = 0; j < el.options.length; j++) {
2724 if (el.options[j].selected) {
2726 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2729 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2737 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2750 if(hasSubmit == false) {
2751 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2756 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2761 data = data.substr(0, data.length - 1);
2769 useDefaultHeader:true,
2771 defaultPostHeader:'application/x-www-form-urlencoded',
2773 useDefaultXhrHeader:true,
2775 defaultXhrHeader:'XMLHttpRequest',
2777 hasDefaultHeaders:true,
2789 setProgId:function(id)
2791 this.activeX.unshift(id);
2794 setDefaultPostHeader:function(b)
2796 this.useDefaultHeader = b;
2799 setDefaultXhrHeader:function(b)
2801 this.useDefaultXhrHeader = b;
2804 setPollingInterval:function(i)
2806 if (typeof i == 'number' && isFinite(i)) {
2807 this.pollInterval = i;
2811 createXhrObject:function(transactionId)
2817 http = new XMLHttpRequest();
2819 obj = { conn:http, tId:transactionId };
2823 for (var i = 0; i < this.activeX.length; ++i) {
2827 http = new ActiveXObject(this.activeX[i]);
2829 obj = { conn:http, tId:transactionId };
2842 getConnectionObject:function()
2845 var tId = this.transactionId;
2849 o = this.createXhrObject(tId);
2851 this.transactionId++;
2862 asyncRequest:function(method, uri, callback, postData)
2864 var o = this.getConnectionObject();
2870 o.conn.open(method, uri, true);
2872 if (this.useDefaultXhrHeader) {
2873 if (!this.defaultHeaders['X-Requested-With']) {
2874 this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2878 if(postData && this.useDefaultHeader){
2879 this.initHeader('Content-Type', this.defaultPostHeader);
2882 if (this.hasDefaultHeaders || this.hasHeaders) {
2886 this.handleReadyState(o, callback);
2887 o.conn.send(postData || null);
2893 handleReadyState:function(o, callback)
2897 if (callback && callback.timeout) {
2899 this.timeout[o.tId] = window.setTimeout(function() {
2900 oConn.abort(o, callback, true);
2901 }, callback.timeout);
2904 this.poll[o.tId] = window.setInterval(
2906 if (o.conn && o.conn.readyState == 4) {
2907 window.clearInterval(oConn.poll[o.tId]);
2908 delete oConn.poll[o.tId];
2910 if(callback && callback.timeout) {
2911 window.clearTimeout(oConn.timeout[o.tId]);
2912 delete oConn.timeout[o.tId];
2915 oConn.handleTransactionResponse(o, callback);
2918 , this.pollInterval);
2921 handleTransactionResponse:function(o, callback, isAbort)
2925 this.releaseObject(o);
2929 var httpStatus, responseObject;
2933 if (o.conn.status !== undefined && o.conn.status != 0) {
2934 httpStatus = o.conn.status;
2946 if (httpStatus >= 200 && httpStatus < 300) {
2947 responseObject = this.createResponseObject(o, callback.argument);
2948 if (callback.success) {
2949 if (!callback.scope) {
2950 callback.success(responseObject);
2955 callback.success.apply(callback.scope, [responseObject]);
2960 switch (httpStatus) {
2968 responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2969 if (callback.failure) {
2970 if (!callback.scope) {
2971 callback.failure(responseObject);
2974 callback.failure.apply(callback.scope, [responseObject]);
2979 responseObject = this.createResponseObject(o, callback.argument);
2980 if (callback.failure) {
2981 if (!callback.scope) {
2982 callback.failure(responseObject);
2985 callback.failure.apply(callback.scope, [responseObject]);
2991 this.releaseObject(o);
2992 responseObject = null;
2995 createResponseObject:function(o, callbackArg)
3002 var headerStr = o.conn.getAllResponseHeaders();
3003 var header = headerStr.split('\n');
3004 for (var i = 0; i < header.length; i++) {
3005 var delimitPos = header[i].indexOf(':');
3006 if (delimitPos != -1) {
3007 headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3015 obj.status = o.conn.status;
3016 obj.statusText = o.conn.statusText;
3017 obj.getResponseHeader = headerObj;
3018 obj.getAllResponseHeaders = headerStr;
3019 obj.responseText = o.conn.responseText;
3020 obj.responseXML = o.conn.responseXML;
3022 if (typeof callbackArg !== undefined) {
3023 obj.argument = callbackArg;
3029 createExceptionObject:function(tId, callbackArg, isAbort)
3032 var COMM_ERROR = 'communication failure';
3033 var ABORT_CODE = -1;
3034 var ABORT_ERROR = 'transaction aborted';
3040 obj.status = ABORT_CODE;
3041 obj.statusText = ABORT_ERROR;
3044 obj.status = COMM_CODE;
3045 obj.statusText = COMM_ERROR;
3049 obj.argument = callbackArg;
3055 initHeader:function(label, value, isDefault)
3057 var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3059 if (headerObj[label] === undefined) {
3060 headerObj[label] = value;
3065 headerObj[label] = value + "," + headerObj[label];
3069 this.hasDefaultHeaders = true;
3072 this.hasHeaders = true;
3077 setHeader:function(o)
3079 if (this.hasDefaultHeaders) {
3080 for (var prop in this.defaultHeaders) {
3081 if (this.defaultHeaders.hasOwnProperty(prop)) {
3082 o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3087 if (this.hasHeaders) {
3088 for (var prop in this.headers) {
3089 if (this.headers.hasOwnProperty(prop)) {
3090 o.conn.setRequestHeader(prop, this.headers[prop]);
3094 this.hasHeaders = false;
3098 resetDefaultHeaders:function() {
3099 delete this.defaultHeaders;
3100 this.defaultHeaders = {};
3101 this.hasDefaultHeaders = false;
3104 abort:function(o, callback, isTimeout)
3106 if(this.isCallInProgress(o)) {
3108 window.clearInterval(this.poll[o.tId]);
3109 delete this.poll[o.tId];
3111 delete this.timeout[o.tId];
3114 this.handleTransactionResponse(o, callback, true);
3124 isCallInProgress:function(o)
3127 return o.conn.readyState != 4 && o.conn.readyState != 0;
3136 releaseObject:function(o)
3145 'MSXML2.XMLHTTP.3.0',
3153 * Portions of this file are based on pieces of Yahoo User Interface Library
3154 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3155 * YUI licensed under the BSD License:
3156 * http://developer.yahoo.net/yui/license.txt
3157 * <script type="text/javascript">
3161 Roo.lib.Region = function(t, r, b, l) {
3171 Roo.lib.Region.prototype = {
3172 contains : function(region) {
3173 return ( region.left >= this.left &&
3174 region.right <= this.right &&
3175 region.top >= this.top &&
3176 region.bottom <= this.bottom );
3180 getArea : function() {
3181 return ( (this.bottom - this.top) * (this.right - this.left) );
3184 intersect : function(region) {
3185 var t = Math.max(this.top, region.top);
3186 var r = Math.min(this.right, region.right);
3187 var b = Math.min(this.bottom, region.bottom);
3188 var l = Math.max(this.left, region.left);
3190 if (b >= t && r >= l) {
3191 return new Roo.lib.Region(t, r, b, l);
3196 union : function(region) {
3197 var t = Math.min(this.top, region.top);
3198 var r = Math.max(this.right, region.right);
3199 var b = Math.max(this.bottom, region.bottom);
3200 var l = Math.min(this.left, region.left);
3202 return new Roo.lib.Region(t, r, b, l);
3205 adjust : function(t, l, b, r) {
3214 Roo.lib.Region.getRegion = function(el) {
3215 var p = Roo.lib.Dom.getXY(el);
3218 var r = p[0] + el.offsetWidth;
3219 var b = p[1] + el.offsetHeight;
3222 return new Roo.lib.Region(t, r, b, l);
3225 * Portions of this file are based on pieces of Yahoo User Interface Library
3226 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3227 * YUI licensed under the BSD License:
3228 * http://developer.yahoo.net/yui/license.txt
3229 * <script type="text/javascript">
3232 //@@dep Roo.lib.Region
3235 Roo.lib.Point = function(x, y) {
3236 if (x instanceof Array) {
3240 this.x = this.right = this.left = this[0] = x;
3241 this.y = this.top = this.bottom = this[1] = y;
3244 Roo.lib.Point.prototype = new Roo.lib.Region();
3246 * Portions of this file are based on pieces of Yahoo User Interface Library
3247 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3248 * YUI licensed under the BSD License:
3249 * http://developer.yahoo.net/yui/license.txt
3250 * <script type="text/javascript">
3257 scroll : function(el, args, duration, easing, cb, scope) {
3258 this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3261 motion : function(el, args, duration, easing, cb, scope) {
3262 this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3265 color : function(el, args, duration, easing, cb, scope) {
3266 this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3269 run : function(el, args, duration, easing, cb, scope, type) {
3270 type = type || Roo.lib.AnimBase;
3271 if (typeof easing == "string") {
3272 easing = Roo.lib.Easing[easing];
3274 var anim = new type(el, args, duration, easing);
3275 anim.animateX(function() {
3276 Roo.callback(cb, scope);
3282 * Portions of this file are based on pieces of Yahoo User Interface Library
3283 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3284 * YUI licensed under the BSD License:
3285 * http://developer.yahoo.net/yui/license.txt
3286 * <script type="text/javascript">
3294 if (!libFlyweight) {
3295 libFlyweight = new Roo.Element.Flyweight();
3297 libFlyweight.dom = el;
3298 return libFlyweight;
3301 // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3305 Roo.lib.AnimBase = function(el, attributes, duration, method) {
3307 this.init(el, attributes, duration, method);
3311 Roo.lib.AnimBase.fly = fly;
3315 Roo.lib.AnimBase.prototype = {
3317 toString: function() {
3318 var el = this.getEl();
3319 var id = el.id || el.tagName;
3320 return ("Anim " + id);
3324 noNegatives: /width|height|opacity|padding/i,
3325 offsetAttribute: /^((width|height)|(top|left))$/,
3326 defaultUnit: /width|height|top$|bottom$|left$|right$/i,
3327 offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3331 doMethod: function(attr, start, end) {
3332 return this.method(this.currentFrame, start, end - start, this.totalFrames);
3336 setAttribute: function(attr, val, unit) {
3337 if (this.patterns.noNegatives.test(attr)) {
3338 val = (val > 0) ? val : 0;
3341 Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3345 getAttribute: function(attr) {
3346 var el = this.getEl();
3347 var val = fly(el).getStyle(attr);
3349 if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3350 return parseFloat(val);
3353 var a = this.patterns.offsetAttribute.exec(attr) || [];
3354 var pos = !!( a[3] );
3355 var box = !!( a[2] );
3358 if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3359 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3368 getDefaultUnit: function(attr) {
3369 if (this.patterns.defaultUnit.test(attr)) {
3376 animateX : function(callback, scope) {
3377 var f = function() {
3378 this.onComplete.removeListener(f);
3379 if (typeof callback == "function") {
3380 callback.call(scope || this, this);
3383 this.onComplete.addListener(f, this);
3388 setRuntimeAttribute: function(attr) {
3391 var attributes = this.attributes;
3393 this.runtimeAttributes[attr] = {};
3395 var isset = function(prop) {
3396 return (typeof prop !== 'undefined');
3399 if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3403 start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3406 if (isset(attributes[attr]['to'])) {
3407 end = attributes[attr]['to'];
3408 } else if (isset(attributes[attr]['by'])) {
3409 if (start.constructor == Array) {
3411 for (var i = 0, len = start.length; i < len; ++i) {
3412 end[i] = start[i] + attributes[attr]['by'][i];
3415 end = start + attributes[attr]['by'];
3419 this.runtimeAttributes[attr].start = start;
3420 this.runtimeAttributes[attr].end = end;
3423 this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3427 init: function(el, attributes, duration, method) {
3429 var isAnimated = false;
3432 var startTime = null;
3435 var actualFrames = 0;
3438 el = Roo.getDom(el);
3441 this.attributes = attributes || {};
3444 this.duration = duration || 1;
3447 this.method = method || Roo.lib.Easing.easeNone;
3450 this.useSeconds = true;
3453 this.currentFrame = 0;
3456 this.totalFrames = Roo.lib.AnimMgr.fps;
3459 this.getEl = function() {
3464 this.isAnimated = function() {
3469 this.getStartTime = function() {
3473 this.runtimeAttributes = {};
3476 this.animate = function() {
3477 if (this.isAnimated()) {
3481 this.currentFrame = 0;
3483 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3485 Roo.lib.AnimMgr.registerElement(this);
3489 this.stop = function(finish) {
3491 this.currentFrame = this.totalFrames;
3492 this._onTween.fire();
3494 Roo.lib.AnimMgr.stop(this);
3497 var onStart = function() {
3498 this.onStart.fire();
3500 this.runtimeAttributes = {};
3501 for (var attr in this.attributes) {
3502 this.setRuntimeAttribute(attr);
3507 startTime = new Date();
3511 var onTween = function() {
3513 duration: new Date() - this.getStartTime(),
3514 currentFrame: this.currentFrame
3517 data.toString = function() {
3519 'duration: ' + data.duration +
3520 ', currentFrame: ' + data.currentFrame
3524 this.onTween.fire(data);
3526 var runtimeAttributes = this.runtimeAttributes;
3528 for (var attr in runtimeAttributes) {
3529 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3535 var onComplete = function() {
3536 var actual_duration = (new Date() - startTime) / 1000 ;
3539 duration: actual_duration,
3540 frames: actualFrames,
3541 fps: actualFrames / actual_duration
3544 data.toString = function() {
3546 'duration: ' + data.duration +
3547 ', frames: ' + data.frames +
3548 ', fps: ' + data.fps
3554 this.onComplete.fire(data);
3558 this._onStart = new Roo.util.Event(this);
3559 this.onStart = new Roo.util.Event(this);
3560 this.onTween = new Roo.util.Event(this);
3561 this._onTween = new Roo.util.Event(this);
3562 this.onComplete = new Roo.util.Event(this);
3563 this._onComplete = new Roo.util.Event(this);
3564 this._onStart.addListener(onStart);
3565 this._onTween.addListener(onTween);
3566 this._onComplete.addListener(onComplete);
3571 * Portions of this file are based on pieces of Yahoo User Interface Library
3572 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3573 * YUI licensed under the BSD License:
3574 * http://developer.yahoo.net/yui/license.txt
3575 * <script type="text/javascript">
3579 Roo.lib.AnimMgr = new function() {
3596 this.registerElement = function(tween) {
3597 queue[queue.length] = tween;
3599 tween._onStart.fire();
3604 this.unRegister = function(tween, index) {
3605 tween._onComplete.fire();
3606 index = index || getIndex(tween);
3608 queue.splice(index, 1);
3612 if (tweenCount <= 0) {
3618 this.start = function() {
3619 if (thread === null) {
3620 thread = setInterval(this.run, this.delay);
3625 this.stop = function(tween) {
3627 clearInterval(thread);
3629 for (var i = 0, len = queue.length; i < len; ++i) {
3630 if (queue[0].isAnimated()) {
3631 this.unRegister(queue[0], 0);
3640 this.unRegister(tween);
3645 this.run = function() {
3646 for (var i = 0, len = queue.length; i < len; ++i) {
3647 var tween = queue[i];
3648 if (!tween || !tween.isAnimated()) {
3652 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3654 tween.currentFrame += 1;
3656 if (tween.useSeconds) {
3657 correctFrame(tween);
3659 tween._onTween.fire();
3662 Roo.lib.AnimMgr.stop(tween, i);
3667 var getIndex = function(anim) {
3668 for (var i = 0, len = queue.length; i < len; ++i) {
3669 if (queue[i] == anim) {
3677 var correctFrame = function(tween) {
3678 var frames = tween.totalFrames;
3679 var frame = tween.currentFrame;
3680 var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3681 var elapsed = (new Date() - tween.getStartTime());
3684 if (elapsed < tween.duration * 1000) {
3685 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3687 tweak = frames - (frame + 1);
3689 if (tweak > 0 && isFinite(tweak)) {
3690 if (tween.currentFrame + tweak >= frames) {
3691 tweak = frames - (frame + 1);
3694 tween.currentFrame += tweak;
3700 * Portions of this file are based on pieces of Yahoo User Interface Library
3701 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3702 * YUI licensed under the BSD License:
3703 * http://developer.yahoo.net/yui/license.txt
3704 * <script type="text/javascript">
3707 Roo.lib.Bezier = new function() {
3709 this.getPosition = function(points, t) {
3710 var n = points.length;
3713 for (var i = 0; i < n; ++i) {
3714 tmp[i] = [points[i][0], points[i][1]];
3717 for (var j = 1; j < n; ++j) {
3718 for (i = 0; i < n - j; ++i) {
3719 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3720 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3724 return [ tmp[0][0], tmp[0][1] ];
3730 * @class Roo.lib.Color
3732 * An abstract Color implementation. Concrete Color implementations should use
3733 * an instance of this function as their prototype, and implement the getRGB and
3734 * getHSL functions. getRGB should return an object representing the RGB
3735 * components of this Color, with the red, green, and blue components in the
3736 * range [0,255] and the alpha component in the range [0,100]. getHSL should
3737 * return an object representing the HSL components of this Color, with the hue
3738 * component in the range [0,360), the saturation and lightness components in
3739 * the range [0,100], and the alpha component in the range [0,1].
3744 * Functions for Color handling and processing.
3746 * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3748 * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3749 * rights to this program, with the intention of it becoming part of the public
3750 * domain. Because this program is released into the public domain, it comes with
3751 * no warranty either expressed or implied, to the extent permitted by law.
3753 * For more free and public domain JavaScript code by the same author, visit:
3754 * http://www.safalra.com/web-design/javascript/
3757 Roo.lib.Color = function() { }
3760 Roo.apply(Roo.lib.Color.prototype, {
3768 * @return {Object} an object representing the RGBA components of this Color. The red,
3769 * green, and blue components are converted to integers in the range [0,255].
3770 * The alpha is a value in the range [0,1].
3772 getIntegerRGB : function(){
3774 // get the RGB components of this Color
3775 var rgb = this.getRGB();
3777 // return the integer components
3779 'r' : Math.round(rgb.r),
3780 'g' : Math.round(rgb.g),
3781 'b' : Math.round(rgb.b),
3789 * @return {Object} an object representing the RGBA components of this Color. The red,
3790 * green, and blue components are converted to numbers in the range [0,100].
3791 * The alpha is a value in the range [0,1].
3793 getPercentageRGB : function(){
3795 // get the RGB components of this Color
3796 var rgb = this.getRGB();
3798 // return the percentage components
3800 'r' : 100 * rgb.r / 255,
3801 'g' : 100 * rgb.g / 255,
3802 'b' : 100 * rgb.b / 255,
3809 * getCSSHexadecimalRGB
3810 * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3811 * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3812 * are two-digit hexadecimal numbers.
3814 getCSSHexadecimalRGB : function()
3817 // get the integer RGB components
3818 var rgb = this.getIntegerRGB();
3820 // determine the hexadecimal equivalents
3821 var r16 = rgb.r.toString(16);
3822 var g16 = rgb.g.toString(16);
3823 var b16 = rgb.b.toString(16);
3825 // return the CSS RGB Color value
3827 + (r16.length == 2 ? r16 : '0' + r16)
3828 + (g16.length == 2 ? g16 : '0' + g16)
3829 + (b16.length == 2 ? b16 : '0' + b16);
3835 * @return {String} a string representing this Color as a CSS integer RGB Color
3836 * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3837 * are integers in the range [0,255].
3839 getCSSIntegerRGB : function(){
3841 // get the integer RGB components
3842 var rgb = this.getIntegerRGB();
3844 // return the CSS RGB Color value
3845 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3851 * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3852 * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3853 * b are integers in the range [0,255] and a is in the range [0,1].
3855 getCSSIntegerRGBA : function(){
3857 // get the integer RGB components
3858 var rgb = this.getIntegerRGB();
3860 // return the CSS integer RGBA Color value
3861 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3866 * getCSSPercentageRGB
3867 * @return {String} a string representing this Color as a CSS percentage RGB Color
3868 * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3869 * b are in the range [0,100].
3871 getCSSPercentageRGB : function(){
3873 // get the percentage RGB components
3874 var rgb = this.getPercentageRGB();
3876 // return the CSS RGB Color value
3877 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3882 * getCSSPercentageRGBA
3883 * @return {String} a string representing this Color as a CSS percentage RGBA Color
3884 * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3885 * and b are in the range [0,100] and a is in the range [0,1].
3887 getCSSPercentageRGBA : function(){
3889 // get the percentage RGB components
3890 var rgb = this.getPercentageRGB();
3892 // return the CSS percentage RGBA Color value
3893 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3899 * @return {String} a string representing this Color as a CSS HSL Color value - that
3900 * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3901 * s and l are in the range [0,100].
3903 getCSSHSL : function(){
3905 // get the HSL components
3906 var hsl = this.getHSL();
3908 // return the CSS HSL Color value
3909 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3915 * @return {String} a string representing this Color as a CSS HSLA Color value - that
3916 * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3917 * s and l are in the range [0,100], and a is in the range [0,1].
3919 getCSSHSLA : function(){
3921 // get the HSL components
3922 var hsl = this.getHSL();
3924 // return the CSS HSL Color value
3925 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3930 * Sets the Color of the specified node to this Color. This functions sets
3931 * the CSS 'color' property for the node. The parameter is:
3933 * @param {DomElement} node - the node whose Color should be set
3935 setNodeColor : function(node){
3937 // set the Color of the node
3938 node.style.color = this.getCSSHexadecimalRGB();
3943 * Sets the background Color of the specified node to this Color. This
3944 * functions sets the CSS 'background-color' property for the node. The
3947 * @param {DomElement} node - the node whose background Color should be set
3949 setNodeBackgroundColor : function(node){
3951 // set the background Color of the node
3952 node.style.backgroundColor = this.getCSSHexadecimalRGB();
3955 // convert between formats..
3958 var r = this.getIntegerRGB();
3959 return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3964 var hsl = this.getHSL();
3965 // return the CSS HSL Color value
3966 return new Roo.lib.HSLColor(hsl.h, hsl.s, hsl.l , hsl.a );
3972 var rgb = this.toRGB();
3973 var hsv = rgb.getHSV();
3974 // return the CSS HSL Color value
3975 return new Roo.lib.HSVColor(hsv.h, hsv.s, hsv.v , hsv.a );
3979 // modify v = 0 ... 1 (eg. 0.5)
3980 saturate : function(v)
3982 var rgb = this.toRGB();
3983 var hsv = rgb.getHSV();
3984 return new Roo.lib.HSVColor(hsv.h, hsv.s * v, hsv.v , hsv.a );
3992 * @return {Object} the RGB and alpha components of this Color as an object with r,
3993 * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3998 // return the RGB components
4010 * @return {Object} the HSV and alpha components of this Color as an object with h,
4011 * s, v, and a properties. h is in the range [0,360), s and v are in the range
4012 * [0,100], and a is in the range [0,1].
4017 // calculate the HSV components if necessary
4018 if (this.hsv == null) {
4019 this.calculateHSV();
4022 // return the HSV components
4034 * @return {Object} the HSL and alpha components of this Color as an object with h,
4035 * s, l, and a properties. h is in the range [0,360), s and l are in the range
4036 * [0,100], and a is in the range [0,1].
4038 getHSL : function(){
4041 // calculate the HSV components if necessary
4042 if (this.hsl == null) { this.calculateHSL(); }
4044 // return the HSL components
4059 * @class Roo.lib.RGBColor
4060 * @extends Roo.lib.Color
4061 * Creates a Color specified in the RGB Color space, with an optional alpha
4062 * component. The parameters are:
4066 * @param {Number} r - the red component, clipped to the range [0,255]
4067 * @param {Number} g - the green component, clipped to the range [0,255]
4068 * @param {Number} b - the blue component, clipped to the range [0,255]
4069 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4070 * optional and defaults to 1
4072 Roo.lib.RGBColor = function (r, g, b, a){
4074 // store the alpha component after clipping it if necessary
4075 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4077 // store the RGB components after clipping them if necessary
4080 'r' : Math.max(0, Math.min(255, r)),
4081 'g' : Math.max(0, Math.min(255, g)),
4082 'b' : Math.max(0, Math.min(255, b))
4085 // initialise the HSV and HSL components to null
4089 * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4090 * range [0,360). The parameters are:
4092 * maximum - the maximum of the RGB component values
4093 * range - the range of the RGB component values
4098 // this does an 'exteds'
4099 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4102 getHue : function(maximum, range)
4106 // check whether the range is zero
4109 // set the hue to zero (any hue is acceptable as the Color is grey)
4114 // determine which of the components has the highest value and set the hue
4117 // red has the highest value
4119 var hue = (rgb.g - rgb.b) / range * 60;
4120 if (hue < 0) { hue += 360; }
4123 // green has the highest value
4125 var hue = (rgb.b - rgb.r) / range * 60 + 120;
4128 // blue has the highest value
4130 var hue = (rgb.r - rgb.g) / range * 60 + 240;
4142 /* //private Calculates and stores the HSV components of this RGBColor so that they can
4143 * be returned be the getHSV function.
4145 calculateHSV : function(){
4147 // get the maximum and range of the RGB component values
4148 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4149 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4151 // store the HSV components
4154 'h' : this.getHue(maximum, range),
4155 's' : (maximum == 0 ? 0 : 100 * range / maximum),
4156 'v' : maximum / 2.55
4161 /* //private Calculates and stores the HSL components of this RGBColor so that they can
4162 * be returned be the getHSL function.
4164 calculateHSL : function(){
4166 // get the maximum and range of the RGB component values
4167 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4168 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4170 // determine the lightness in the range [0,1]
4171 var l = maximum / 255 - range / 510;
4173 // store the HSL components
4176 'h' : this.getHue(maximum, range),
4177 's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4186 * @class Roo.lib.HSVColor
4187 * @extends Roo.lib.Color
4188 * Creates a Color specified in the HSV Color space, with an optional alpha
4189 * component. The parameters are:
4192 * @param {Number} h - the hue component, wrapped to the range [0,360)
4193 * @param {Number} s - the saturation component, clipped to the range [0,100]
4194 * @param {Number} v - the value component, clipped to the range [0,100]
4195 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4196 * optional and defaults to 1
4198 Roo.lib.HSVColor = function (h, s, v, a){
4200 // store the alpha component after clipping it if necessary
4201 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4203 // store the HSV components after clipping or wrapping them if necessary
4206 'h' : (h % 360 + 360) % 360,
4207 's' : Math.max(0, Math.min(100, s)),
4208 'v' : Math.max(0, Math.min(100, v))
4211 // initialise the RGB and HSL components to null
4216 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4217 /* Calculates and stores the RGB components of this HSVColor so that they can
4218 * be returned be the getRGB function.
4220 calculateRGB: function ()
4223 // check whether the saturation is zero
4226 // set the Color to the appropriate shade of grey
4233 // set some temporary values
4234 var f = hsv.h / 60 - Math.floor(hsv.h / 60);
4235 var p = hsv.v * (1 - hsv.s / 100);
4236 var q = hsv.v * (1 - hsv.s / 100 * f);
4237 var t = hsv.v * (1 - hsv.s / 100 * (1 - f));
4239 // set the RGB Color components to their temporary values
4240 switch (Math.floor(hsv.h / 60)){
4241 case 0: var r = hsv.v; var g = t; var b = p; break;
4242 case 1: var r = q; var g = hsv.v; var b = p; break;
4243 case 2: var r = p; var g = hsv.v; var b = t; break;
4244 case 3: var r = p; var g = q; var b = hsv.v; break;
4245 case 4: var r = t; var g = p; var b = hsv.v; break;
4246 case 5: var r = hsv.v; var g = p; var b = q; break;
4251 // store the RGB components
4261 /* Calculates and stores the HSL components of this HSVColor so that they can
4262 * be returned be the getHSL function.
4264 calculateHSL : function (){
4267 // determine the lightness in the range [0,100]
4268 var l = (2 - hsv.s / 100) * hsv.v / 2;
4270 // store the HSL components
4274 's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4278 // correct a division-by-zero error
4279 if (isNaN(hsl.s)) { hsl.s = 0; }
4288 * @class Roo.lib.HSLColor
4289 * @extends Roo.lib.Color
4292 * Creates a Color specified in the HSL Color space, with an optional alpha
4293 * component. The parameters are:
4295 * @param {Number} h - the hue component, wrapped to the range [0,360)
4296 * @param {Number} s - the saturation component, clipped to the range [0,100]
4297 * @param {Number} l - the lightness component, clipped to the range [0,100]
4298 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4299 * optional and defaults to 1
4302 Roo.lib.HSLColor = function(h, s, l, a){
4304 // store the alpha component after clipping it if necessary
4305 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4307 // store the HSL components after clipping or wrapping them if necessary
4310 'h' : (h % 360 + 360) % 360,
4311 's' : Math.max(0, Math.min(100, s)),
4312 'l' : Math.max(0, Math.min(100, l))
4315 // initialise the RGB and HSV components to null
4318 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4320 /* Calculates and stores the RGB components of this HSLColor so that they can
4321 * be returned be the getRGB function.
4323 calculateRGB: function (){
4325 // check whether the saturation is zero
4326 if (this.hsl.s == 0){
4328 // store the RGB components representing the appropriate shade of grey
4331 'r' : this.hsl.l * 2.55,
4332 'g' : this.hsl.l * 2.55,
4333 'b' : this.hsl.l * 2.55
4338 // set some temporary values
4339 var p = this.hsl.l < 50
4340 ? this.hsl.l * (1 + hsl.s / 100)
4341 : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4342 var q = 2 * hsl.l - p;
4344 // initialise the RGB components
4347 'r' : (h + 120) / 60 % 6,
4349 'b' : (h + 240) / 60 % 6
4352 // loop over the RGB components
4353 for (var key in this.rgb){
4355 // ensure that the property is not inherited from the root object
4356 if (this.rgb.hasOwnProperty(key)){
4358 // set the component to its value in the range [0,100]
4359 if (this.rgb[key] < 1){
4360 this.rgb[key] = q + (p - q) * this.rgb[key];
4361 }else if (this.rgb[key] < 3){
4363 }else if (this.rgb[key] < 4){
4364 this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4369 // set the component to its value in the range [0,255]
4370 this.rgb[key] *= 2.55;
4380 /* Calculates and stores the HSV components of this HSLColor so that they can
4381 * be returned be the getHSL function.
4383 calculateHSV : function(){
4385 // set a temporary value
4386 var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4388 // store the HSV components
4392 's' : 200 * t / (this.hsl.l + t),
4393 'v' : t + this.hsl.l
4396 // correct a division-by-zero error
4397 if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4404 * Portions of this file are based on pieces of Yahoo User Interface Library
4405 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4406 * YUI licensed under the BSD License:
4407 * http://developer.yahoo.net/yui/license.txt
4408 * <script type="text/javascript">
4413 Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4414 Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4417 Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4419 var fly = Roo.lib.AnimBase.fly;
4421 var superclass = Y.ColorAnim.superclass;
4422 var proto = Y.ColorAnim.prototype;
4424 proto.toString = function() {
4425 var el = this.getEl();
4426 var id = el.id || el.tagName;
4427 return ("ColorAnim " + id);
4430 proto.patterns.color = /color$/i;
4431 proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4432 proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4433 proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4434 proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4437 proto.parseColor = function(s) {
4438 if (s.length == 3) {
4442 var c = this.patterns.hex.exec(s);
4443 if (c && c.length == 4) {
4444 return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4447 c = this.patterns.rgb.exec(s);
4448 if (c && c.length == 4) {
4449 return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4452 c = this.patterns.hex3.exec(s);
4453 if (c && c.length == 4) {
4454 return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4459 // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4460 proto.getAttribute = function(attr) {
4461 var el = this.getEl();
4462 if (this.patterns.color.test(attr)) {
4463 var val = fly(el).getStyle(attr);
4465 if (this.patterns.transparent.test(val)) {
4466 var parent = el.parentNode;
4467 val = fly(parent).getStyle(attr);
4469 while (parent && this.patterns.transparent.test(val)) {
4470 parent = parent.parentNode;
4471 val = fly(parent).getStyle(attr);
4472 if (parent.tagName.toUpperCase() == 'HTML') {
4478 val = superclass.getAttribute.call(this, attr);
4483 proto.getAttribute = function(attr) {
4484 var el = this.getEl();
4485 if (this.patterns.color.test(attr)) {
4486 var val = fly(el).getStyle(attr);
4488 if (this.patterns.transparent.test(val)) {
4489 var parent = el.parentNode;
4490 val = fly(parent).getStyle(attr);
4492 while (parent && this.patterns.transparent.test(val)) {
4493 parent = parent.parentNode;
4494 val = fly(parent).getStyle(attr);
4495 if (parent.tagName.toUpperCase() == 'HTML') {
4501 val = superclass.getAttribute.call(this, attr);
4507 proto.doMethod = function(attr, start, end) {
4510 if (this.patterns.color.test(attr)) {
4512 for (var i = 0, len = start.length; i < len; ++i) {
4513 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4516 val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4519 val = superclass.doMethod.call(this, attr, start, end);
4525 proto.setRuntimeAttribute = function(attr) {
4526 superclass.setRuntimeAttribute.call(this, attr);
4528 if (this.patterns.color.test(attr)) {
4529 var attributes = this.attributes;
4530 var start = this.parseColor(this.runtimeAttributes[attr].start);
4531 var end = this.parseColor(this.runtimeAttributes[attr].end);
4533 if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4534 end = this.parseColor(attributes[attr].by);
4536 for (var i = 0, len = start.length; i < len; ++i) {
4537 end[i] = start[i] + end[i];
4541 this.runtimeAttributes[attr].start = start;
4542 this.runtimeAttributes[attr].end = end;
4548 * Portions of this file are based on pieces of Yahoo User Interface Library
4549 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4550 * YUI licensed under the BSD License:
4551 * http://developer.yahoo.net/yui/license.txt
4552 * <script type="text/javascript">
4558 easeNone: function (t, b, c, d) {
4559 return c * t / d + b;
4563 easeIn: function (t, b, c, d) {
4564 return c * (t /= d) * t + b;
4568 easeOut: function (t, b, c, d) {
4569 return -c * (t /= d) * (t - 2) + b;
4573 easeBoth: function (t, b, c, d) {
4574 if ((t /= d / 2) < 1) {
4575 return c / 2 * t * t + b;
4578 return -c / 2 * ((--t) * (t - 2) - 1) + b;
4582 easeInStrong: function (t, b, c, d) {
4583 return c * (t /= d) * t * t * t + b;
4587 easeOutStrong: function (t, b, c, d) {
4588 return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4592 easeBothStrong: function (t, b, c, d) {
4593 if ((t /= d / 2) < 1) {
4594 return c / 2 * t * t * t * t + b;
4597 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4602 elasticIn: function (t, b, c, d, a, p) {
4606 if ((t /= d) == 1) {
4613 if (!a || a < Math.abs(c)) {
4618 var s = p / (2 * Math.PI) * Math.asin(c / a);
4621 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4625 elasticOut: function (t, b, c, d, a, p) {
4629 if ((t /= d) == 1) {
4636 if (!a || a < Math.abs(c)) {
4641 var s = p / (2 * Math.PI) * Math.asin(c / a);
4644 return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4648 elasticBoth: function (t, b, c, d, a, p) {
4653 if ((t /= d / 2) == 2) {
4661 if (!a || a < Math.abs(c)) {
4666 var s = p / (2 * Math.PI) * Math.asin(c / a);
4670 return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4671 Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4673 return a * Math.pow(2, -10 * (t -= 1)) *
4674 Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4679 backIn: function (t, b, c, d, s) {
4680 if (typeof s == 'undefined') {
4683 return c * (t /= d) * t * ((s + 1) * t - s) + b;
4687 backOut: function (t, b, c, d, s) {
4688 if (typeof s == 'undefined') {
4691 return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4695 backBoth: function (t, b, c, d, s) {
4696 if (typeof s == 'undefined') {
4700 if ((t /= d / 2 ) < 1) {
4701 return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4703 return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4707 bounceIn: function (t, b, c, d) {
4708 return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4712 bounceOut: function (t, b, c, d) {
4713 if ((t /= d) < (1 / 2.75)) {
4714 return c * (7.5625 * t * t) + b;
4715 } else if (t < (2 / 2.75)) {
4716 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4717 } else if (t < (2.5 / 2.75)) {
4718 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4720 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4724 bounceBoth: function (t, b, c, d) {
4726 return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4728 return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4731 * Portions of this file are based on pieces of Yahoo User Interface Library
4732 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4733 * YUI licensed under the BSD License:
4734 * http://developer.yahoo.net/yui/license.txt
4735 * <script type="text/javascript">
4739 Roo.lib.Motion = function(el, attributes, duration, method) {
4741 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4745 Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4749 var superclass = Y.Motion.superclass;
4750 var proto = Y.Motion.prototype;
4752 proto.toString = function() {
4753 var el = this.getEl();
4754 var id = el.id || el.tagName;
4755 return ("Motion " + id);
4758 proto.patterns.points = /^points$/i;
4760 proto.setAttribute = function(attr, val, unit) {
4761 if (this.patterns.points.test(attr)) {
4762 unit = unit || 'px';
4763 superclass.setAttribute.call(this, 'left', val[0], unit);
4764 superclass.setAttribute.call(this, 'top', val[1], unit);
4766 superclass.setAttribute.call(this, attr, val, unit);
4770 proto.getAttribute = function(attr) {
4771 if (this.patterns.points.test(attr)) {
4773 superclass.getAttribute.call(this, 'left'),
4774 superclass.getAttribute.call(this, 'top')
4777 val = superclass.getAttribute.call(this, attr);
4783 proto.doMethod = function(attr, start, end) {
4786 if (this.patterns.points.test(attr)) {
4787 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4788 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4790 val = superclass.doMethod.call(this, attr, start, end);
4795 proto.setRuntimeAttribute = function(attr) {
4796 if (this.patterns.points.test(attr)) {
4797 var el = this.getEl();
4798 var attributes = this.attributes;
4800 var control = attributes['points']['control'] || [];
4804 if (control.length > 0 && !(control[0] instanceof Array)) {
4805 control = [control];
4808 for (i = 0,len = control.length; i < len; ++i) {
4809 tmp[i] = control[i];
4814 Roo.fly(el).position();
4816 if (isset(attributes['points']['from'])) {
4817 Roo.lib.Dom.setXY(el, attributes['points']['from']);
4820 Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4823 start = this.getAttribute('points');
4826 if (isset(attributes['points']['to'])) {
4827 end = translateValues.call(this, attributes['points']['to'], start);
4829 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4830 for (i = 0,len = control.length; i < len; ++i) {
4831 control[i] = translateValues.call(this, control[i], start);
4835 } else if (isset(attributes['points']['by'])) {
4836 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4838 for (i = 0,len = control.length; i < len; ++i) {
4839 control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4843 this.runtimeAttributes[attr] = [start];
4845 if (control.length > 0) {
4846 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4849 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4852 superclass.setRuntimeAttribute.call(this, attr);
4856 var translateValues = function(val, start) {
4857 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4858 val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4863 var isset = function(prop) {
4864 return (typeof prop !== 'undefined');
4868 * Portions of this file are based on pieces of Yahoo User Interface Library
4869 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4870 * YUI licensed under the BSD License:
4871 * http://developer.yahoo.net/yui/license.txt
4872 * <script type="text/javascript">
4876 Roo.lib.Scroll = function(el, attributes, duration, method) {
4878 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4882 Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4886 var superclass = Y.Scroll.superclass;
4887 var proto = Y.Scroll.prototype;
4889 proto.toString = function() {
4890 var el = this.getEl();
4891 var id = el.id || el.tagName;
4892 return ("Scroll " + id);
4895 proto.doMethod = function(attr, start, end) {
4898 if (attr == 'scroll') {
4900 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4901 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4905 val = superclass.doMethod.call(this, attr, start, end);
4910 proto.getAttribute = function(attr) {
4912 var el = this.getEl();
4914 if (attr == 'scroll') {
4915 val = [ el.scrollLeft, el.scrollTop ];
4917 val = superclass.getAttribute.call(this, attr);
4923 proto.setAttribute = function(attr, val, unit) {
4924 var el = this.getEl();
4926 if (attr == 'scroll') {
4927 el.scrollLeft = val[0];
4928 el.scrollTop = val[1];
4930 superclass.setAttribute.call(this, attr, val, unit);
4935 * Originally based of this code... - refactored for Roo...
4936 * https://github.com/aaalsaleh/undo-manager
4939 * @author Abdulrahman Alsaleh
4940 * @copyright 2015 Abdulrahman Alsaleh
4941 * @license MIT License (c)
4943 * Hackily modifyed by alan@roojs.com
4948 * TOTALLY UNTESTED...
4950 * Documentation to be done....
4955 * @class Roo.lib.UndoManager
4956 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4957 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4963 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4967 * For more information see this blog post with examples:
4968 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4969 - Create Elements using DOM, HTML fragments and Templates</a>.
4971 * @param {Number} limit how far back to go ... use 1000?
4972 * @param {Object} scope usually use document..
4975 Roo.lib.UndoManager = function (limit, undoScopeHost)
4979 this.scope = undoScopeHost;
4980 this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4981 if (this.fireEvent) {
4988 Roo.lib.UndoManager.prototype = {
4999 * To push and execute a transaction, the method undoManager.transact
5000 * must be called by passing a transaction object as the first argument, and a merge
5001 * flag as the second argument. A transaction object has the following properties:
5005 undoManager.transact({
5007 execute: function() { ... },
5008 undo: function() { ... },
5009 // redo same as execute
5010 redo: function() { this.execute(); }
5013 // merge transaction
5014 undoManager.transact({
5016 execute: function() { ... }, // this will be run...
5017 undo: function() { ... }, // what to do when undo is run.
5018 // redo same as execute
5019 redo: function() { this.execute(); }
5024 * @param {Object} transaction The transaction to add to the stack.
5025 * @return {String} The HTML fragment
5029 transact : function (transaction, merge)
5031 if (arguments.length < 2) {
5032 throw new TypeError('Not enough arguments to UndoManager.transact.');
5035 transaction.execute();
5037 this.stack.splice(0, this.position);
5038 if (merge && this.length) {
5039 this.stack[0].push(transaction);
5041 this.stack.unshift([transaction]);
5046 if (this.limit && this.stack.length > this.limit) {
5047 this.length = this.stack.length = this.limit;
5049 this.length = this.stack.length;
5052 if (this.fireEvent) {
5053 this.scope.dispatchEvent(
5054 new CustomEvent('DOMTransaction', {
5056 transactions: this.stack[0].slice()
5064 //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5071 //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5073 if (this.position < this.length) {
5074 for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5075 this.stack[this.position][i].undo();
5079 if (this.fireEvent) {
5080 this.scope.dispatchEvent(
5081 new CustomEvent('undo', {
5083 transactions: this.stack[this.position - 1].slice()
5095 if (this.position > 0) {
5096 for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5097 this.stack[this.position - 1][i].redo();
5101 if (this.fireEvent) {
5102 this.scope.dispatchEvent(
5103 new CustomEvent('redo', {
5105 transactions: this.stack[this.position].slice()
5115 item : function (index)
5117 if (index >= 0 && index < this.length) {
5118 return this.stack[index].slice();
5123 clearUndo : function () {
5124 this.stack.length = this.length = this.position;
5127 clearRedo : function () {
5128 this.stack.splice(0, this.position);
5130 this.length = this.stack.length;
5133 * Reset the undo - probaly done on load to clear all history.
5140 this.current_html = this.scope.innerHTML;
5141 if (this.timer !== false) {
5142 clearTimeout(this.timer);
5154 // this will handle the undo/redo on the element.?
5155 bindEvents : function()
5157 var el = this.scope;
5158 el.undoManager = this;
5161 this.scope.addEventListener('keydown', function(e) {
5162 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5164 el.undoManager.redo(); // Ctrl/Command + Shift + Z
5166 el.undoManager.undo(); // Ctrl/Command + Z
5173 this.scope.addEventListener('keyup', function(e) {
5174 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5183 el.addEventListener('input', function(e) {
5184 if(el.innerHTML == t.current_html) {
5187 // only record events every second.
5188 if (t.timer !== false) {
5189 clearTimeout(t.timer);
5192 t.timer = setTimeout(function() { t.merge = false; }, 1000);
5194 t.addEvent(t.merge);
5195 t.merge = true; // ignore changes happening every second..
5199 * Manually add an event.
5200 * Normall called without arguements - and it will just get added to the stack.
5204 addEvent : function(merge)
5206 //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5207 // not sure if this should clear the timer
5208 merge = typeof(merge) == 'undefined' ? false : merge;
5210 this.scope.undoManager.transact({
5212 oldHTML: this.current_html,
5213 newHTML: this.scope.innerHTML,
5214 // nothing to execute (content already changed when input is fired)
5215 execute: function() { },
5217 this.scope.innerHTML = this.current_html = this.oldHTML;
5220 this.scope.innerHTML = this.current_html = this.newHTML;
5222 }, false); //merge);
5226 this.current_html = this.scope.innerHTML;
5236 * @class Roo.lib.Range
5238 * This is a toolkit, normally used to copy features into a Dom Range element
5239 * Roo.lib.Range.wrap(x);
5244 Roo.lib.Range = function() { };
5247 * Wrap a Dom Range object, to give it new features...
5249 * @param {Range} the range to wrap
5251 Roo.lib.Range.wrap = function(r) {
5252 return Roo.apply(r, Roo.lib.Range.prototype);
5255 * find a parent node eg. LI / OL
5256 * @param {string|Array} node name or array of nodenames
5257 * @return {DomElement|false}
5259 Roo.apply(Roo.lib.Range.prototype,
5262 closest : function(str)
5264 if (typeof(str) != 'string') {
5265 // assume it's a array.
5266 for(var i = 0;i < str.length;i++) {
5267 var r = this.closest(str[i]);
5275 str = str.toLowerCase();
5276 var n = this.commonAncestorContainer; // might not be a node
5277 while (n.nodeType != 1) {
5281 if (n.nodeName.toLowerCase() == str ) {
5284 if (n.nodeName.toLowerCase() == 'body') {
5288 return n.closest(str) || false;
5291 cloneRange : function()
5293 return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5296 * @class Roo.lib.Selection
5298 * This is a toolkit, normally used to copy features into a Dom Selection element
5299 * Roo.lib.Selection.wrap(x);
5304 Roo.lib.Selection = function() { };
5307 * Wrap a Dom Range object, to give it new features...
5309 * @param {Range} the range to wrap
5311 Roo.lib.Selection.wrap = function(r, doc) {
5312 Roo.apply(r, Roo.lib.Selection.prototype);
5313 r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5317 * find a parent node eg. LI / OL
5318 * @param {string|Array} node name or array of nodenames
5319 * @return {DomElement|false}
5321 Roo.apply(Roo.lib.Selection.prototype,
5324 * the owner document
5326 ownerDocument : false,
5328 getRangeAt : function(n)
5330 return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5334 * insert node at selection
5335 * @param {DomElement|string} node
5336 * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5338 insertNode: function(node, cursor)
5340 if (typeof(node) == 'string') {
5341 node = this.ownerDocument.createElement(node);
5342 if (cursor == 'in') {
5343 node.innerHTML = ' ';
5347 var range = this.getRangeAt(0);
5349 if (this.type != 'Caret') {
5350 range.deleteContents();
5352 var sn = node.childNodes[0]; // select the contents.
5356 range.insertNode(node);
5357 if (cursor == 'after') {
5358 node.insertAdjacentHTML('afterend', ' ');
5359 sn = node.nextSibling;
5362 if (cursor == 'none') {
5366 this.cursorText(sn);
5369 cursorText : function(n)
5372 //var range = this.getRangeAt(0);
5373 range = Roo.lib.Range.wrap(new Range());
5374 //range.selectNode(n);
5376 var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5377 range.setStart(n.parentNode,ix);
5378 range.setEnd(n.parentNode,ix+1);
5379 //range.collapse(false);
5381 this.removeAllRanges();
5382 this.addRange(range);
5384 Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5386 cursorAfter : function(n)
5388 if (!n.nextSibling || n.nextSibling.nodeValue != ' ') {
5389 n.insertAdjacentHTML('afterend', ' ');
5391 this.cursorText (n.nextSibling);
5397 * Ext JS Library 1.1.1
5398 * Copyright(c) 2006-2007, Ext JS, LLC.
5400 * Originally Released Under LGPL - original licence link has changed is not relivant.
5403 * <script type="text/javascript">
5407 // nasty IE9 hack - what a pile of crap that is..
5409 if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5410 Range.prototype.createContextualFragment = function (html) {
5411 var doc = window.document;
5412 var container = doc.createElement("div");
5413 container.innerHTML = html;
5414 var frag = doc.createDocumentFragment(), n;
5415 while ((n = container.firstChild)) {
5416 frag.appendChild(n);
5423 * @class Roo.DomHelper
5424 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5425 * 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>.
5428 Roo.DomHelper = function(){
5429 var tempTableEl = null;
5430 var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5431 var tableRe = /^table|tbody|tr|td$/i;
5433 // build as innerHTML where available
5435 var createHtml = function(o){
5436 if(typeof o == 'string'){
5445 if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5446 if(attr == "style"){
5448 if(typeof s == "function"){
5451 if(typeof s == "string"){
5452 b += ' style="' + s + '"';
5453 }else if(typeof s == "object"){
5456 if(typeof s[key] != "function"){
5457 b += key + ":" + s[key] + ";";
5464 b += ' class="' + o["cls"] + '"';
5465 }else if(attr == "htmlFor"){
5466 b += ' for="' + o["htmlFor"] + '"';
5468 b += " " + attr + '="' + o[attr] + '"';
5472 if(emptyTags.test(o.tag)){
5476 var cn = o.children || o.cn;
5478 //http://bugs.kde.org/show_bug.cgi?id=71506
5479 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5480 for(var i = 0, len = cn.length; i < len; i++) {
5481 b += createHtml(cn[i], b);
5484 b += createHtml(cn, b);
5490 b += "</" + o.tag + ">";
5497 var createDom = function(o, parentNode){
5499 // defininition craeted..
5501 if (o.ns && o.ns != 'html') {
5503 if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5504 xmlns[o.ns] = o.xmlns;
5507 if (typeof(xmlns[o.ns]) == 'undefined') {
5508 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5514 if (typeof(o) == 'string') {
5515 return parentNode.appendChild(document.createTextNode(o));
5517 o.tag = o.tag || div;
5518 if (o.ns && Roo.isIE) {
5520 o.tag = o.ns + ':' + o.tag;
5523 var el = ns ? document.createElementNS( ns, o.tag||'div') : document.createElement(o.tag||'div');
5524 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5527 if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" ||
5528 attr == "style" || typeof o[attr] == "function") { continue; }
5530 if(attr=="cls" && Roo.isIE){
5531 el.className = o["cls"];
5533 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5539 Roo.DomHelper.applyStyles(el, o.style);
5540 var cn = o.children || o.cn;
5542 //http://bugs.kde.org/show_bug.cgi?id=71506
5543 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5544 for(var i = 0, len = cn.length; i < len; i++) {
5545 createDom(cn[i], el);
5552 el.innerHTML = o.html;
5555 parentNode.appendChild(el);
5560 var ieTable = function(depth, s, h, e){
5561 tempTableEl.innerHTML = [s, h, e].join('');
5562 var i = -1, el = tempTableEl;
5563 while(++i < depth && el.firstChild){
5569 // kill repeat to save bytes
5573 tbe = '</tbody>'+te,
5579 * Nasty code for IE's broken table implementation
5581 var insertIntoTable = function(tag, where, el, html){
5583 tempTableEl = document.createElement('div');
5588 if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5591 if(where == 'beforebegin'){
5595 before = el.nextSibling;
5598 node = ieTable(4, trs, html, tre);
5600 else if(tag == 'tr'){
5601 if(where == 'beforebegin'){
5604 node = ieTable(3, tbs, html, tbe);
5605 } else if(where == 'afterend'){
5606 before = el.nextSibling;
5608 node = ieTable(3, tbs, html, tbe);
5609 } else{ // INTO a TR
5610 if(where == 'afterbegin'){
5611 before = el.firstChild;
5613 node = ieTable(4, trs, html, tre);
5615 } else if(tag == 'tbody'){
5616 if(where == 'beforebegin'){
5619 node = ieTable(2, ts, html, te);
5620 } else if(where == 'afterend'){
5621 before = el.nextSibling;
5623 node = ieTable(2, ts, html, te);
5625 if(where == 'afterbegin'){
5626 before = el.firstChild;
5628 node = ieTable(3, tbs, html, tbe);
5631 if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5634 if(where == 'afterbegin'){
5635 before = el.firstChild;
5637 node = ieTable(2, ts, html, te);
5639 el.insertBefore(node, before);
5643 // this is a bit like the react update code...
5646 var updateNode = function(from, to)
5648 // should we handle non-standard elements?
5649 Roo.log(["UpdateNode" , from, to]);
5650 if (from.nodeType != to.nodeType) {
5651 Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5652 from.parentNode.replaceChild(to, from);
5655 if (from.nodeType == 3) {
5656 // assume it's text?!
5657 if (from.data == to.data) {
5660 from.data = to.data;
5663 if (!from.parentNode) {
5664 // not sure why this is happening?
5667 // assume 'to' doesnt have '1/3 nodetypes!
5668 // not sure why, by from, parent node might not exist?
5669 if (from.nodeType !=1 || from.tagName != to.tagName) {
5670 Roo.log(["ReplaceChild" , from, to ]);
5672 from.parentNode.replaceChild(to, from);
5675 // compare attributes
5676 var ar = Array.from(from.attributes);
5677 for(var i = 0; i< ar.length;i++) {
5678 if (to.hasAttribute(ar[i].name)) {
5681 if (ar[i].name == 'id') { // always keep ids?
5684 //if (ar[i].name == 'style') {
5685 // throw "style removed?";
5687 Roo.log("removeAttribute" + ar[i].name);
5688 from.removeAttribute(ar[i].name);
5691 for(var i = 0; i< ar.length;i++) {
5692 if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5693 Roo.log("skipAttribute " + ar[i].name + '=' + to.getAttribute(ar[i].name));
5696 Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5697 from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5700 var far = Array.from(from.childNodes);
5701 var tar = Array.from(to.childNodes);
5702 // if the lengths are different.. then it's probably a editable content change, rather than
5703 // a change of the block definition..
5705 // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5706 /*if (from.innerHTML == to.innerHTML) {
5709 if (far.length != tar.length) {
5710 from.innerHTML = to.innerHTML;
5715 for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5716 if (i >= far.length) {
5717 from.appendChild(tar[i]);
5718 Roo.log(["add", tar[i]]);
5720 } else if ( i >= tar.length) {
5721 from.removeChild(far[i]);
5722 Roo.log(["remove", far[i]]);
5725 updateNode(far[i], tar[i]);
5737 /** True to force the use of DOM instead of html fragments @type Boolean */
5741 * Returns the markup for the passed Element(s) config
5742 * @param {Object} o The Dom object spec (and children)
5745 markup : function(o){
5746 return createHtml(o);
5750 * Applies a style specification to an element
5751 * @param {String/HTMLElement} el The element to apply styles to
5752 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5753 * a function which returns such a specification.
5755 applyStyles : function(el, styles){
5758 if(typeof styles == "string"){
5759 var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5761 while ((matches = re.exec(styles)) != null){
5762 el.setStyle(matches[1], matches[2]);
5764 }else if (typeof styles == "object"){
5765 for (var style in styles){
5766 el.setStyle(style, styles[style]);
5768 }else if (typeof styles == "function"){
5769 Roo.DomHelper.applyStyles(el, styles.call());
5775 * Inserts an HTML fragment into the Dom
5776 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5777 * @param {HTMLElement} el The context element
5778 * @param {String} html The HTML fragmenet
5779 * @return {HTMLElement} The new node
5781 insertHtml : function(where, el, html){
5782 where = where.toLowerCase();
5783 if(el.insertAdjacentHTML){
5784 if(tableRe.test(el.tagName)){
5786 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5792 el.insertAdjacentHTML('BeforeBegin', html);
5793 return el.previousSibling;
5795 el.insertAdjacentHTML('AfterBegin', html);
5796 return el.firstChild;
5798 el.insertAdjacentHTML('BeforeEnd', html);
5799 return el.lastChild;
5801 el.insertAdjacentHTML('AfterEnd', html);
5802 return el.nextSibling;
5804 throw 'Illegal insertion point -> "' + where + '"';
5806 var range = el.ownerDocument.createRange();
5810 range.setStartBefore(el);
5811 frag = range.createContextualFragment(html);
5812 el.parentNode.insertBefore(frag, el);
5813 return el.previousSibling;
5816 range.setStartBefore(el.firstChild);
5817 frag = range.createContextualFragment(html);
5818 el.insertBefore(frag, el.firstChild);
5819 return el.firstChild;
5821 el.innerHTML = html;
5822 return el.firstChild;
5826 range.setStartAfter(el.lastChild);
5827 frag = range.createContextualFragment(html);
5828 el.appendChild(frag);
5829 return el.lastChild;
5831 el.innerHTML = html;
5832 return el.lastChild;
5835 range.setStartAfter(el);
5836 frag = range.createContextualFragment(html);
5837 el.parentNode.insertBefore(frag, el.nextSibling);
5838 return el.nextSibling;
5840 throw 'Illegal insertion point -> "' + where + '"';
5844 * Creates new Dom element(s) and inserts them before el
5845 * @param {String/HTMLElement/Element} el The context element
5846 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5847 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5848 * @return {HTMLElement/Roo.Element} The new node
5850 insertBefore : function(el, o, returnElement){
5851 return this.doInsert(el, o, returnElement, "beforeBegin");
5855 * Creates new Dom element(s) and inserts them after el
5856 * @param {String/HTMLElement/Element} el The context element
5857 * @param {Object} o The Dom object spec (and children)
5858 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5859 * @return {HTMLElement/Roo.Element} The new node
5861 insertAfter : function(el, o, returnElement){
5862 return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5866 * Creates new Dom element(s) and inserts them as the first child of el
5867 * @param {String/HTMLElement/Element} el The context element
5868 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5869 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5870 * @return {HTMLElement/Roo.Element} The new node
5872 insertFirst : function(el, o, returnElement){
5873 return this.doInsert(el, o, returnElement, "afterBegin");
5877 doInsert : function(el, o, returnElement, pos, sibling){
5878 el = Roo.getDom(el);
5880 if(this.useDom || o.ns){
5881 newNode = createDom(o, null);
5882 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5884 var html = createHtml(o);
5885 newNode = this.insertHtml(pos, el, html);
5887 return returnElement ? Roo.get(newNode, true) : newNode;
5891 * Creates new Dom element(s) and appends them to el
5892 * @param {String/HTMLElement/Element} el The context element
5893 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5894 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5895 * @return {HTMLElement/Roo.Element} The new node
5897 append : function(el, o, returnElement){
5898 el = Roo.getDom(el);
5900 if(this.useDom || o.ns){
5901 newNode = createDom(o, null);
5902 el.appendChild(newNode);
5904 var html = createHtml(o);
5905 newNode = this.insertHtml("beforeEnd", el, html);
5907 return returnElement ? Roo.get(newNode, true) : newNode;
5911 * Creates new Dom element(s) and overwrites the contents of el with them
5912 * @param {String/HTMLElement/Element} el The context element
5913 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5914 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5915 * @return {HTMLElement/Roo.Element} The new node
5917 overwrite : function(el, o, returnElement)
5919 el = Roo.getDom(el);
5922 while (el.childNodes.length) {
5923 el.removeChild(el.firstChild);
5927 el.innerHTML = createHtml(o);
5930 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5934 * Creates a new Roo.DomHelper.Template from the Dom object spec
5935 * @param {Object} o The Dom object spec (and children)
5936 * @return {Roo.DomHelper.Template} The new template
5938 createTemplate : function(o){
5939 var html = createHtml(o);
5940 return new Roo.Template(html);
5943 * Updates the first element with the spec from the o (replacing if necessary)
5944 * This iterates through the children, and updates attributes / children etc..
5945 * @param {String/HTMLElement/Element} el The context element
5946 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5949 update : function(el, o)
5951 updateNode(Roo.getDom(el), createDom(o));
5960 * Ext JS Library 1.1.1
5961 * Copyright(c) 2006-2007, Ext JS, LLC.
5963 * Originally Released Under LGPL - original licence link has changed is not relivant.
5966 * <script type="text/javascript">
5970 * @class Roo.Template
5971 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5972 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5975 var t = new Roo.Template({
5976 html : '<div name="{id}">' +
5977 '<span class="{cls}">{name:trim} {someval:this.myformat}{value:ellipsis(10)}</span>' +
5979 myformat: function (value, allValues) {
5980 return 'XX' + value;
5983 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5985 * For more information see this blog post with examples:
5986 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5987 - Create Elements using DOM, HTML fragments and Templates</a>.
5989 * @param {Object} cfg - Configuration object.
5991 Roo.Template = function(cfg){
5993 if(cfg instanceof Array){
5995 }else if(arguments.length > 1){
5996 cfg = Array.prototype.join.call(arguments, "");
6000 if (typeof(cfg) == 'object') {
6011 Roo.Template.prototype = {
6014 * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6020 * @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..
6021 * it should be fixed so that template is observable...
6025 * @cfg {String} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6033 * Returns an HTML fragment of this template with the specified values applied.
6034 * @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'})
6035 * @return {String} The HTML fragment
6040 applyTemplate : function(values){
6041 //Roo.log(["applyTemplate", values]);
6045 return this.compiled(values);
6047 var useF = this.disableFormats !== true;
6048 var fm = Roo.util.Format, tpl = this;
6049 var fn = function(m, name, format, args){
6051 if(format.substr(0, 5) == "this."){
6052 return tpl.call(format.substr(5), values[name], values);
6055 // quoted values are required for strings in compiled templates,
6056 // but for non compiled we need to strip them
6057 // quoted reversed for jsmin
6058 var re = /^\s*['"](.*)["']\s*$/;
6059 args = args.split(',');
6060 for(var i = 0, len = args.length; i < len; i++){
6061 args[i] = args[i].replace(re, "$1");
6063 args = [values[name]].concat(args);
6065 args = [values[name]];
6067 return fm[format].apply(fm, args);
6070 return values[name] !== undefined ? values[name] : "";
6073 return this.html.replace(this.re, fn);
6091 this.loading = true;
6092 this.compiled = false;
6094 var cx = new Roo.data.Connection();
6098 success : function (response) {
6102 _t.set(response.responseText,true);
6108 failure : function(response) {
6109 Roo.log("Template failed to load from " + _t.url);
6116 * Sets the HTML used as the template and optionally compiles it.
6117 * @param {String} html
6118 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6119 * @return {Roo.Template} this
6121 set : function(html, compile){
6123 this.compiled = false;
6131 * True to disable format functions (defaults to false)
6134 disableFormats : false,
6137 * The regular expression used to match template variables
6141 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6144 * Compiles the template into an internal function, eliminating the RegEx overhead.
6145 * @return {Roo.Template} this
6147 compile : function(){
6148 var fm = Roo.util.Format;
6149 var useF = this.disableFormats !== true;
6150 var sep = Roo.isGecko ? "+" : ",";
6151 var fn = function(m, name, format, args){
6153 args = args ? ',' + args : "";
6154 if(format.substr(0, 5) != "this."){
6155 format = "fm." + format + '(';
6157 format = 'this.call("'+ format.substr(5) + '", ';
6161 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6163 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6166 // branched to use + in gecko and [].join() in others
6168 body = "this.compiled = function(values){ return '" +
6169 this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6172 body = ["this.compiled = function(values){ return ['"];
6173 body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6174 body.push("'].join('');};");
6175 body = body.join('');
6185 // private function used to call members
6186 call : function(fnName, value, allValues){
6187 return this[fnName](value, allValues);
6191 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6192 * @param {String/HTMLElement/Roo.Element} el The context element
6193 * @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'})
6194 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6195 * @return {HTMLElement/Roo.Element} The new node or Element
6197 insertFirst: function(el, values, returnElement){
6198 return this.doInsert('afterBegin', el, values, returnElement);
6202 * Applies the supplied values to the template and inserts the new node(s) before el.
6203 * @param {String/HTMLElement/Roo.Element} el The context element
6204 * @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'})
6205 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6206 * @return {HTMLElement/Roo.Element} The new node or Element
6208 insertBefore: function(el, values, returnElement){
6209 return this.doInsert('beforeBegin', el, values, returnElement);
6213 * Applies the supplied values to the template and inserts the new node(s) after el.
6214 * @param {String/HTMLElement/Roo.Element} el The context element
6215 * @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'})
6216 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6217 * @return {HTMLElement/Roo.Element} The new node or Element
6219 insertAfter : function(el, values, returnElement){
6220 return this.doInsert('afterEnd', el, values, returnElement);
6224 * Applies the supplied values to the template and appends the new node(s) to el.
6225 * @param {String/HTMLElement/Roo.Element} el The context element
6226 * @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'})
6227 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6228 * @return {HTMLElement/Roo.Element} The new node or Element
6230 append : function(el, values, returnElement){
6231 return this.doInsert('beforeEnd', el, values, returnElement);
6234 doInsert : function(where, el, values, returnEl){
6235 el = Roo.getDom(el);
6236 var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6237 return returnEl ? Roo.get(newNode, true) : newNode;
6241 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6242 * @param {String/HTMLElement/Roo.Element} el The context element
6243 * @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'})
6244 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6245 * @return {HTMLElement/Roo.Element} The new node or Element
6247 overwrite : function(el, values, returnElement){
6248 el = Roo.getDom(el);
6249 el.innerHTML = this.applyTemplate(values);
6250 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6254 * Alias for {@link #applyTemplate}
6257 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6260 Roo.DomHelper.Template = Roo.Template;
6263 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6264 * @param {String/HTMLElement} el A DOM element or its id
6265 * @returns {Roo.Template} The created template
6268 Roo.Template.from = function(el){
6269 el = Roo.getDom(el);
6270 return new Roo.Template(el.value || el.innerHTML);
6273 * Ext JS Library 1.1.1
6274 * Copyright(c) 2006-2007, Ext JS, LLC.
6276 * Originally Released Under LGPL - original licence link has changed is not relivant.
6279 * <script type="text/javascript">
6284 * This is code is also distributed under MIT license for use
6285 * with jQuery and prototype JavaScript libraries.
6288 * @class Roo.DomQuery
6289 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).
6291 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>
6294 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.
6296 <h4>Element Selectors:</h4>
6298 <li> <b>*</b> any element</li>
6299 <li> <b>E</b> an element with the tag E</li>
6300 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6301 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6302 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6303 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6305 <h4>Attribute Selectors:</h4>
6306 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6308 <li> <b>E[foo]</b> has an attribute "foo"</li>
6309 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6310 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6311 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6312 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6313 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6314 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6316 <h4>Pseudo Classes:</h4>
6318 <li> <b>E:first-child</b> E is the first child of its parent</li>
6319 <li> <b>E:last-child</b> E is the last child of its parent</li>
6320 <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>
6321 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6322 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6323 <li> <b>E:only-child</b> E is the only child of its parent</li>
6324 <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>
6325 <li> <b>E:first</b> the first E in the resultset</li>
6326 <li> <b>E:last</b> the last E in the resultset</li>
6327 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6328 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6329 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6330 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6331 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6332 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6333 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6334 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6335 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6337 <h4>CSS Value Selectors:</h4>
6339 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6340 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6341 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6342 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6343 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6344 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6348 Roo.DomQuery = function(){
6349 var cache = {}, simpleCache = {}, valueCache = {};
6350 var nonSpace = /\S/;
6351 var trimRe = /^\s+|\s+$/g;
6352 var tplRe = /\{(\d+)\}/g;
6353 var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6354 var tagTokenRe = /^(#)?([\w-\*]+)/;
6355 var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6357 function child(p, index){
6359 var n = p.firstChild;
6361 if(n.nodeType == 1){
6372 while((n = n.nextSibling) && n.nodeType != 1);
6377 while((n = n.previousSibling) && n.nodeType != 1);
6381 function children(d){
6382 var n = d.firstChild, ni = -1;
6384 var nx = n.nextSibling;
6385 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6395 function byClassName(c, a, v){
6399 var r = [], ri = -1, cn;
6400 for(var i = 0, ci; ci = c[i]; i++){
6404 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6405 +' ').indexOf(v) != -1){
6412 function attrValue(n, attr){
6413 if(!n.tagName && typeof n.length != "undefined"){
6422 if(attr == "class" || attr == "className"){
6423 return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6425 return n.getAttribute(attr) || n[attr];
6429 function getNodes(ns, mode, tagName){
6430 var result = [], ri = -1, cs;
6434 tagName = tagName || "*";
6435 if(typeof ns.getElementsByTagName != "undefined"){
6439 for(var i = 0, ni; ni = ns[i]; i++){
6440 cs = ni.getElementsByTagName(tagName);
6441 for(var j = 0, ci; ci = cs[j]; j++){
6445 }else if(mode == "/" || mode == ">"){
6446 var utag = tagName.toUpperCase();
6447 for(var i = 0, ni, cn; ni = ns[i]; i++){
6448 cn = ni.children || ni.childNodes;
6449 for(var j = 0, cj; cj = cn[j]; j++){
6450 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
6455 }else if(mode == "+"){
6456 var utag = tagName.toUpperCase();
6457 for(var i = 0, n; n = ns[i]; i++){
6458 while((n = n.nextSibling) && n.nodeType != 1);
6459 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6463 }else if(mode == "~"){
6464 for(var i = 0, n; n = ns[i]; i++){
6465 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6474 function concat(a, b){
6478 for(var i = 0, l = b.length; i < l; i++){
6484 function byTag(cs, tagName){
6485 if(cs.tagName || cs == document){
6491 var r = [], ri = -1;
6492 tagName = tagName.toLowerCase();
6493 for(var i = 0, ci; ci = cs[i]; i++){
6494 if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6501 function byId(cs, attr, id){
6502 if(cs.tagName || cs == document){
6508 var r = [], ri = -1;
6509 for(var i = 0,ci; ci = cs[i]; i++){
6510 if(ci && ci.id == id){
6518 function byAttribute(cs, attr, value, op, custom){
6519 var r = [], ri = -1, st = custom=="{";
6520 var f = Roo.DomQuery.operators[op];
6521 for(var i = 0, ci; ci = cs[i]; i++){
6524 a = Roo.DomQuery.getStyle(ci, attr);
6526 else if(attr == "class" || attr == "className"){
6527 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6528 }else if(attr == "for"){
6530 }else if(attr == "href"){
6531 a = ci.getAttribute("href", 2);
6533 a = ci.getAttribute(attr);
6535 if((f && f(a, value)) || (!f && a)){
6542 function byPseudo(cs, name, value){
6543 return Roo.DomQuery.pseudos[name](cs, value);
6546 // This is for IE MSXML which does not support expandos.
6547 // IE runs the same speed using setAttribute, however FF slows way down
6548 // and Safari completely fails so they need to continue to use expandos.
6549 var isIE = window.ActiveXObject ? true : false;
6551 // this eval is stop the compressor from
6552 // renaming the variable to something shorter
6554 /** eval:var:batch */
6559 function nodupIEXml(cs){
6561 cs[0].setAttribute("_nodup", d);
6563 for(var i = 1, len = cs.length; i < len; i++){
6565 if(!c.getAttribute("_nodup") != d){
6566 c.setAttribute("_nodup", d);
6570 for(var i = 0, len = cs.length; i < len; i++){
6571 cs[i].removeAttribute("_nodup");
6580 var len = cs.length, c, i, r = cs, cj, ri = -1;
6581 if(!len || typeof cs.nodeType != "undefined" || len == 1){
6584 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6585 return nodupIEXml(cs);
6589 for(i = 1; c = cs[i]; i++){
6594 for(var j = 0; j < i; j++){
6597 for(j = i+1; cj = cs[j]; j++){
6609 function quickDiffIEXml(c1, c2){
6611 for(var i = 0, len = c1.length; i < len; i++){
6612 c1[i].setAttribute("_qdiff", d);
6615 for(var i = 0, len = c2.length; i < len; i++){
6616 if(c2[i].getAttribute("_qdiff") != d){
6617 r[r.length] = c2[i];
6620 for(var i = 0, len = c1.length; i < len; i++){
6621 c1[i].removeAttribute("_qdiff");
6626 function quickDiff(c1, c2){
6627 var len1 = c1.length;
6631 if(isIE && c1[0].selectSingleNode){
6632 return quickDiffIEXml(c1, c2);
6635 for(var i = 0; i < len1; i++){
6639 for(var i = 0, len = c2.length; i < len; i++){
6640 if(c2[i]._qdiff != d){
6641 r[r.length] = c2[i];
6647 function quickId(ns, mode, root, id){
6649 var d = root.ownerDocument || root;
6650 return d.getElementById(id);
6652 ns = getNodes(ns, mode, "*");
6653 return byId(ns, null, id);
6657 getStyle : function(el, name){
6658 return Roo.fly(el).getStyle(name);
6661 * Compiles a selector/xpath query into a reusable function. The returned function
6662 * takes one parameter "root" (optional), which is the context node from where the query should start.
6663 * @param {String} selector The selector/xpath query
6664 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6665 * @return {Function}
6667 compile : function(path, type){
6668 type = type || "select";
6670 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6671 var q = path, mode, lq;
6672 var tk = Roo.DomQuery.matchers;
6673 var tklen = tk.length;
6676 // accept leading mode switch
6677 var lmode = q.match(modeRe);
6678 if(lmode && lmode[1]){
6679 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6680 q = q.replace(lmode[1], "");
6682 // strip leading slashes
6683 while(path.substr(0, 1)=="/"){
6684 path = path.substr(1);
6687 while(q && lq != q){
6689 var tm = q.match(tagTokenRe);
6690 if(type == "select"){
6693 fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6695 fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6697 q = q.replace(tm[0], "");
6698 }else if(q.substr(0, 1) != '@'){
6699 fn[fn.length] = 'n = getNodes(n, mode, "*");';
6704 fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6706 fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6708 q = q.replace(tm[0], "");
6711 while(!(mm = q.match(modeRe))){
6712 var matched = false;
6713 for(var j = 0; j < tklen; j++){
6715 var m = q.match(t.re);
6717 fn[fn.length] = t.select.replace(tplRe, function(x, i){
6720 q = q.replace(m[0], "");
6725 // prevent infinite loop on bad selector
6727 throw 'Error parsing selector, parsing failed at "' + q + '"';
6731 fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6732 q = q.replace(mm[1], "");
6735 fn[fn.length] = "return nodup(n);\n}";
6738 * list of variables that need from compression as they are used by eval.
6748 * eval:var:byClassName
6750 * eval:var:byAttribute
6751 * eval:var:attrValue
6759 * Selects a group of elements.
6760 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6761 * @param {Node} root (optional) The start of the query (defaults to document).
6764 select : function(path, root, type){
6765 if(!root || root == document){
6768 if(typeof root == "string"){
6769 root = document.getElementById(root);
6771 var paths = path.split(",");
6773 for(var i = 0, len = paths.length; i < len; i++){
6774 var p = paths[i].replace(trimRe, "");
6776 cache[p] = Roo.DomQuery.compile(p);
6778 throw p + " is not a valid selector";
6781 var result = cache[p](root);
6782 if(result && result != document){
6783 results = results.concat(result);
6786 if(paths.length > 1){
6787 return nodup(results);
6793 * Selects a single element.
6794 * @param {String} selector The selector/xpath query
6795 * @param {Node} root (optional) The start of the query (defaults to document).
6798 selectNode : function(path, root){
6799 return Roo.DomQuery.select(path, root)[0];
6803 * Selects the value of a node, optionally replacing null with the defaultValue.
6804 * @param {String} selector The selector/xpath query
6805 * @param {Node} root (optional) The start of the query (defaults to document).
6806 * @param {String} defaultValue
6808 selectValue : function(path, root, defaultValue){
6809 path = path.replace(trimRe, "");
6810 if(!valueCache[path]){
6811 valueCache[path] = Roo.DomQuery.compile(path, "select");
6813 var n = valueCache[path](root);
6814 n = n[0] ? n[0] : n;
6815 var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6816 return ((v === null||v === undefined||v==='') ? defaultValue : v);
6820 * Selects the value of a node, parsing integers and floats.
6821 * @param {String} selector The selector/xpath query
6822 * @param {Node} root (optional) The start of the query (defaults to document).
6823 * @param {Number} defaultValue
6826 selectNumber : function(path, root, defaultValue){
6827 var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6828 return parseFloat(v);
6832 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6833 * @param {String/HTMLElement/Array} el An element id, element or array of elements
6834 * @param {String} selector The simple selector to test
6837 is : function(el, ss){
6838 if(typeof el == "string"){
6839 el = document.getElementById(el);
6841 var isArray = (el instanceof Array);
6842 var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6843 return isArray ? (result.length == el.length) : (result.length > 0);
6847 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6848 * @param {Array} el An array of elements to filter
6849 * @param {String} selector The simple selector to test
6850 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6851 * the selector instead of the ones that match
6854 filter : function(els, ss, nonMatches){
6855 ss = ss.replace(trimRe, "");
6856 if(!simpleCache[ss]){
6857 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6859 var result = simpleCache[ss](els);
6860 return nonMatches ? quickDiff(result, els) : result;
6864 * Collection of matching regular expressions and code snippets.
6868 select: 'n = byClassName(n, null, " {1} ");'
6870 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6871 select: 'n = byPseudo(n, "{1}", "{2}");'
6873 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6874 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6877 select: 'n = byId(n, null, "{1}");'
6880 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6885 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6886 * 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, > <.
6889 "=" : function(a, v){
6892 "!=" : function(a, v){
6895 "^=" : function(a, v){
6896 return a && a.substr(0, v.length) == v;
6898 "$=" : function(a, v){
6899 return a && a.substr(a.length-v.length) == v;
6901 "*=" : function(a, v){
6902 return a && a.indexOf(v) !== -1;
6904 "%=" : function(a, v){
6905 return (a % v) == 0;
6907 "|=" : function(a, v){
6908 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6910 "~=" : function(a, v){
6911 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6916 * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6917 * and the argument (if any) supplied in the selector.
6920 "first-child" : function(c){
6921 var r = [], ri = -1, n;
6922 for(var i = 0, ci; ci = n = c[i]; i++){
6923 while((n = n.previousSibling) && n.nodeType != 1);
6931 "last-child" : function(c){
6932 var r = [], ri = -1, n;
6933 for(var i = 0, ci; ci = n = c[i]; i++){
6934 while((n = n.nextSibling) && n.nodeType != 1);
6942 "nth-child" : function(c, a) {
6943 var r = [], ri = -1;
6944 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6945 var f = (m[1] || 1) - 0, l = m[2] - 0;
6946 for(var i = 0, n; n = c[i]; i++){
6947 var pn = n.parentNode;
6948 if (batch != pn._batch) {
6950 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6951 if(cn.nodeType == 1){
6958 if (l == 0 || n.nodeIndex == l){
6961 } else if ((n.nodeIndex + l) % f == 0){
6969 "only-child" : function(c){
6970 var r = [], ri = -1;;
6971 for(var i = 0, ci; ci = c[i]; i++){
6972 if(!prev(ci) && !next(ci)){
6979 "empty" : function(c){
6980 var r = [], ri = -1;
6981 for(var i = 0, ci; ci = c[i]; i++){
6982 var cns = ci.childNodes, j = 0, cn, empty = true;
6985 if(cn.nodeType == 1 || cn.nodeType == 3){
6997 "contains" : function(c, v){
6998 var r = [], ri = -1;
6999 for(var i = 0, ci; ci = c[i]; i++){
7000 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7007 "nodeValue" : function(c, v){
7008 var r = [], ri = -1;
7009 for(var i = 0, ci; ci = c[i]; i++){
7010 if(ci.firstChild && ci.firstChild.nodeValue == v){
7017 "checked" : function(c){
7018 var r = [], ri = -1;
7019 for(var i = 0, ci; ci = c[i]; i++){
7020 if(ci.checked == true){
7027 "not" : function(c, ss){
7028 return Roo.DomQuery.filter(c, ss, true);
7031 "odd" : function(c){
7032 return this["nth-child"](c, "odd");
7035 "even" : function(c){
7036 return this["nth-child"](c, "even");
7039 "nth" : function(c, a){
7040 return c[a-1] || [];
7043 "first" : function(c){
7047 "last" : function(c){
7048 return c[c.length-1] || [];
7051 "has" : function(c, ss){
7052 var s = Roo.DomQuery.select;
7053 var r = [], ri = -1;
7054 for(var i = 0, ci; ci = c[i]; i++){
7055 if(s(ss, ci).length > 0){
7062 "next" : function(c, ss){
7063 var is = Roo.DomQuery.is;
7064 var r = [], ri = -1;
7065 for(var i = 0, ci; ci = c[i]; i++){
7074 "prev" : function(c, ss){
7075 var is = Roo.DomQuery.is;
7076 var r = [], ri = -1;
7077 for(var i = 0, ci; ci = c[i]; i++){
7090 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7091 * @param {String} path The selector/xpath query
7092 * @param {Node} root (optional) The start of the query (defaults to document).
7097 Roo.query = Roo.DomQuery.select;
7100 * Ext JS Library 1.1.1
7101 * Copyright(c) 2006-2007, Ext JS, LLC.
7103 * Originally Released Under LGPL - original licence link has changed is not relivant.
7106 * <script type="text/javascript">
7110 * @class Roo.util.Observable
7111 * Base class that provides a common interface for publishing events. Subclasses are expected to
7112 * to have a property "events" with all the events defined.<br>
7115 Employee = function(name){
7122 Roo.extend(Employee, Roo.util.Observable);
7124 * @param {Object} config properties to use (incuding events / listeners)
7127 Roo.util.Observable = function(cfg){
7130 this.addEvents(cfg.events || {});
7132 delete cfg.events; // make sure
7135 Roo.apply(this, cfg);
7138 this.on(this.listeners);
7139 delete this.listeners;
7142 Roo.util.Observable.prototype = {
7144 * @cfg {Object} listeners list of events and functions to call for this object,
7148 'click' : function(e) {
7158 * Fires the specified event with the passed parameters (minus the event name).
7159 * @param {String} eventName
7160 * @param {Object...} args Variable number of parameters are passed to handlers
7161 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7163 fireEvent : function(){
7164 var ce = this.events[arguments[0].toLowerCase()];
7165 if(typeof ce == "object"){
7166 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7173 filterOptRe : /^(?:scope|delay|buffer|single)$/,
7176 * Appends an event handler to this component
7177 * @param {String} eventName The type of event to listen for
7178 * @param {Function} handler The method the event invokes
7179 * @param {Object} scope (optional) The scope in which to execute the handler
7180 * function. The handler function's "this" context.
7181 * @param {Object} options (optional) An object containing handler configuration
7182 * properties. This may contain any of the following properties:<ul>
7183 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7184 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7185 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7186 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7187 * by the specified number of milliseconds. If the event fires again within that time, the original
7188 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7191 * <b>Combining Options</b><br>
7192 * Using the options argument, it is possible to combine different types of listeners:<br>
7194 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7196 el.on('click', this.onClick, this, {
7203 * <b>Attaching multiple handlers in 1 call</b><br>
7204 * The method also allows for a single argument to be passed which is a config object containing properties
7205 * which specify multiple handlers.
7214 fn: this.onMouseOver,
7218 fn: this.onMouseOut,
7224 * Or a shorthand syntax which passes the same scope object to all handlers:
7227 'click': this.onClick,
7228 'mouseover': this.onMouseOver,
7229 'mouseout': this.onMouseOut,
7234 addListener : function(eventName, fn, scope, o){
7235 if(typeof eventName == "object"){
7238 if(this.filterOptRe.test(e)){
7241 if(typeof o[e] == "function"){
7243 this.addListener(e, o[e], o.scope, o);
7245 // individual options
7246 this.addListener(e, o[e].fn, o[e].scope, o[e]);
7251 o = (!o || typeof o == "boolean") ? {} : o;
7252 eventName = eventName.toLowerCase();
7253 var ce = this.events[eventName] || true;
7254 if(typeof ce == "boolean"){
7255 ce = new Roo.util.Event(this, eventName);
7256 this.events[eventName] = ce;
7258 ce.addListener(fn, scope, o);
7262 * Removes a listener
7263 * @param {String} eventName The type of event to listen for
7264 * @param {Function} handler The handler to remove
7265 * @param {Object} scope (optional) The scope (this object) for the handler
7267 removeListener : function(eventName, fn, scope){
7268 var ce = this.events[eventName.toLowerCase()];
7269 if(typeof ce == "object"){
7270 ce.removeListener(fn, scope);
7275 * Removes all listeners for this object
7277 purgeListeners : function(){
7278 for(var evt in this.events){
7279 if(typeof this.events[evt] == "object"){
7280 this.events[evt].clearListeners();
7285 relayEvents : function(o, events){
7286 var createHandler = function(ename){
7289 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7292 for(var i = 0, len = events.length; i < len; i++){
7293 var ename = events[i];
7294 if(!this.events[ename]){
7295 this.events[ename] = true;
7297 o.on(ename, createHandler(ename), this);
7302 * Used to define events on this Observable
7303 * @param {Object} object The object with the events defined
7305 addEvents : function(o){
7309 Roo.applyIf(this.events, o);
7313 * Checks to see if this object has any listeners for a specified event
7314 * @param {String} eventName The name of the event to check for
7315 * @return {Boolean} True if the event is being listened for, else false
7317 hasListener : function(eventName){
7318 var e = this.events[eventName];
7319 return typeof e == "object" && e.listeners.length > 0;
7323 * Appends an event handler to this element (shorthand for addListener)
7324 * @param {String} eventName The type of event to listen for
7325 * @param {Function} handler The method the event invokes
7326 * @param {Object} scope (optional) The scope in which to execute the handler
7327 * function. The handler function's "this" context.
7328 * @param {Object} options (optional)
7331 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7333 * Removes a listener (shorthand for removeListener)
7334 * @param {String} eventName The type of event to listen for
7335 * @param {Function} handler The handler to remove
7336 * @param {Object} scope (optional) The scope (this object) for the handler
7339 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7342 * Starts capture on the specified Observable. All events will be passed
7343 * to the supplied function with the event name + standard signature of the event
7344 * <b>before</b> the event is fired. If the supplied function returns false,
7345 * the event will not fire.
7346 * @param {Observable} o The Observable to capture
7347 * @param {Function} fn The function to call
7348 * @param {Object} scope (optional) The scope (this object) for the fn
7351 Roo.util.Observable.capture = function(o, fn, scope){
7352 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7356 * Removes <b>all</b> added captures from the Observable.
7357 * @param {Observable} o The Observable to release
7360 Roo.util.Observable.releaseCapture = function(o){
7361 o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7366 var createBuffered = function(h, o, scope){
7367 var task = new Roo.util.DelayedTask();
7369 task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7373 var createSingle = function(h, e, fn, scope){
7375 e.removeListener(fn, scope);
7376 return h.apply(scope, arguments);
7380 var createDelayed = function(h, o, scope){
7382 var args = Array.prototype.slice.call(arguments, 0);
7383 setTimeout(function(){
7384 h.apply(scope, args);
7389 Roo.util.Event = function(obj, name){
7392 this.listeners = [];
7395 Roo.util.Event.prototype = {
7396 addListener : function(fn, scope, options){
7397 var o = options || {};
7398 scope = scope || this.obj;
7399 if(!this.isListening(fn, scope)){
7400 var l = {fn: fn, scope: scope, options: o};
7403 h = createDelayed(h, o, scope);
7406 h = createSingle(h, this, fn, scope);
7409 h = createBuffered(h, o, scope);
7412 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7413 this.listeners.push(l);
7415 this.listeners = this.listeners.slice(0);
7416 this.listeners.push(l);
7421 findListener : function(fn, scope){
7422 scope = scope || this.obj;
7423 var ls = this.listeners;
7424 for(var i = 0, len = ls.length; i < len; i++){
7426 if(l.fn == fn && l.scope == scope){
7433 isListening : function(fn, scope){
7434 return this.findListener(fn, scope) != -1;
7437 removeListener : function(fn, scope){
7439 if((index = this.findListener(fn, scope)) != -1){
7441 this.listeners.splice(index, 1);
7443 this.listeners = this.listeners.slice(0);
7444 this.listeners.splice(index, 1);
7451 clearListeners : function(){
7452 this.listeners = [];
7456 var ls = this.listeners, scope, len = ls.length;
7459 var args = Array.prototype.slice.call(arguments, 0);
7460 for(var i = 0; i < len; i++){
7462 if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7463 this.firing = false;
7467 this.firing = false;
7474 * Copyright(c) 2007-2017, Roo J Solutions Ltd
7481 * @class Roo.Document
7482 * @extends Roo.util.Observable
7483 * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7485 * @param {Object} config the methods and properties of the 'base' class for the application.
7487 * Generic Page handler - implement this to start your app..
7490 * MyProject = new Roo.Document({
7492 'load' : true // your events..
7495 'ready' : function() {
7496 // fired on Roo.onReady()
7501 Roo.Document = function(cfg) {
7506 Roo.util.Observable.call(this,cfg);
7510 Roo.onReady(function() {
7511 _this.fireEvent('ready');
7517 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7519 * Ext JS Library 1.1.1
7520 * Copyright(c) 2006-2007, Ext JS, LLC.
7522 * Originally Released Under LGPL - original licence link has changed is not relivant.
7525 * <script type="text/javascript">
7529 * @class Roo.EventManager
7530 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
7531 * several useful events directly.
7532 * See {@link Roo.EventObject} for more details on normalized event objects.
7535 Roo.EventManager = function(){
7536 var docReadyEvent, docReadyProcId, docReadyState = false;
7537 var resizeEvent, resizeTask, textEvent, textSize;
7538 var E = Roo.lib.Event;
7539 var D = Roo.lib.Dom;
7544 var fireDocReady = function(){
7546 docReadyState = true;
7549 clearInterval(docReadyProcId);
7551 if(Roo.isGecko || Roo.isOpera) {
7552 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7555 var defer = document.getElementById("ie-deferred-loader");
7557 defer.onreadystatechange = null;
7558 defer.parentNode.removeChild(defer);
7562 docReadyEvent.fire();
7563 docReadyEvent.clearListeners();
7568 var initDocReady = function(){
7569 docReadyEvent = new Roo.util.Event();
7570 if(Roo.isGecko || Roo.isOpera) {
7571 document.addEventListener("DOMContentLoaded", fireDocReady, false);
7573 document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7574 var defer = document.getElementById("ie-deferred-loader");
7575 defer.onreadystatechange = function(){
7576 if(this.readyState == "complete"){
7580 }else if(Roo.isSafari){
7581 docReadyProcId = setInterval(function(){
7582 var rs = document.readyState;
7583 if(rs == "complete") {
7588 // no matter what, make sure it fires on load
7589 E.on(window, "load", fireDocReady);
7592 var createBuffered = function(h, o){
7593 var task = new Roo.util.DelayedTask(h);
7595 // create new event object impl so new events don't wipe out properties
7596 e = new Roo.EventObjectImpl(e);
7597 task.delay(o.buffer, h, null, [e]);
7601 var createSingle = function(h, el, ename, fn){
7603 Roo.EventManager.removeListener(el, ename, fn);
7608 var createDelayed = function(h, o){
7610 // create new event object impl so new events don't wipe out properties
7611 e = new Roo.EventObjectImpl(e);
7612 setTimeout(function(){
7617 var transitionEndVal = false;
7619 var transitionEnd = function()
7621 if (transitionEndVal) {
7622 return transitionEndVal;
7624 var el = document.createElement('div');
7626 var transEndEventNames = {
7627 WebkitTransition : 'webkitTransitionEnd',
7628 MozTransition : 'transitionend',
7629 OTransition : 'oTransitionEnd otransitionend',
7630 transition : 'transitionend'
7633 for (var name in transEndEventNames) {
7634 if (el.style[name] !== undefined) {
7635 transitionEndVal = transEndEventNames[name];
7636 return transitionEndVal ;
7643 var listen = function(element, ename, opt, fn, scope)
7645 var o = (!opt || typeof opt == "boolean") ? {} : opt;
7646 fn = fn || o.fn; scope = scope || o.scope;
7647 var el = Roo.getDom(element);
7651 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7654 if (ename == 'transitionend') {
7655 ename = transitionEnd();
7657 var h = function(e){
7658 e = Roo.EventObject.setEvent(e);
7661 t = e.getTarget(o.delegate, el);
7668 if(o.stopEvent === true){
7671 if(o.preventDefault === true){
7674 if(o.stopPropagation === true){
7675 e.stopPropagation();
7678 if(o.normalized === false){
7682 fn.call(scope || el, e, t, o);
7685 h = createDelayed(h, o);
7688 h = createSingle(h, el, ename, fn);
7691 h = createBuffered(h, o);
7694 fn._handlers = fn._handlers || [];
7697 fn._handlers.push([Roo.id(el), ename, h]);
7701 E.on(el, ename, h); // this adds the actuall listener to the object..
7704 if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7705 el.addEventListener("DOMMouseScroll", h, false);
7706 E.on(window, 'unload', function(){
7707 el.removeEventListener("DOMMouseScroll", h, false);
7710 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7711 Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7716 var stopListening = function(el, ename, fn){
7717 var id = Roo.id(el), hds = fn._handlers, hd = fn;
7719 for(var i = 0, len = hds.length; i < len; i++){
7721 if(h[0] == id && h[1] == ename){
7728 E.un(el, ename, hd);
7729 el = Roo.getDom(el);
7730 if(ename == "mousewheel" && el.addEventListener){
7731 el.removeEventListener("DOMMouseScroll", hd, false);
7733 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7734 Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7738 var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7745 * @scope Roo.EventManager
7750 * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7751 * object with a Roo.EventObject
7752 * @param {Function} fn The method the event invokes
7753 * @param {Object} scope An object that becomes the scope of the handler
7754 * @param {boolean} override If true, the obj passed in becomes
7755 * the execution scope of the listener
7756 * @return {Function} The wrapped function
7759 wrap : function(fn, scope, override){
7761 Roo.EventObject.setEvent(e);
7762 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7767 * Appends an event handler to an element (shorthand for addListener)
7768 * @param {String/HTMLElement} element The html element or id to assign the
7769 * @param {String} eventName The type of event to listen for
7770 * @param {Function} handler The method the event invokes
7771 * @param {Object} scope (optional) The scope in which to execute the handler
7772 * function. The handler function's "this" context.
7773 * @param {Object} options (optional) An object containing handler configuration
7774 * properties. This may contain any of the following properties:<ul>
7775 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7776 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7777 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7778 * <li>preventDefault {Boolean} True to prevent the default action</li>
7779 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7780 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7781 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7782 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7783 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7784 * by the specified number of milliseconds. If the event fires again within that time, the original
7785 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7788 * <b>Combining Options</b><br>
7789 * Using the options argument, it is possible to combine different types of listeners:<br>
7791 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7793 el.on('click', this.onClick, this, {
7800 * <b>Attaching multiple handlers in 1 call</b><br>
7801 * The method also allows for a single argument to be passed which is a config object containing properties
7802 * which specify multiple handlers.
7812 fn: this.onMouseOver
7821 * Or a shorthand syntax:<br>
7824 'click' : this.onClick,
7825 'mouseover' : this.onMouseOver,
7826 'mouseout' : this.onMouseOut
7830 addListener : function(element, eventName, fn, scope, options){
7831 if(typeof eventName == "object"){
7837 if(typeof o[e] == "function"){
7839 listen(element, e, o, o[e], o.scope);
7841 // individual options
7842 listen(element, e, o[e]);
7847 return listen(element, eventName, options, fn, scope);
7851 * Removes an event handler
7853 * @param {String/HTMLElement} element The id or html element to remove the
7855 * @param {String} eventName The type of event
7856 * @param {Function} fn
7857 * @return {Boolean} True if a listener was actually removed
7859 removeListener : function(element, eventName, fn){
7860 return stopListening(element, eventName, fn);
7864 * Fires when the document is ready (before onload and before images are loaded). Can be
7865 * accessed shorthanded Roo.onReady().
7866 * @param {Function} fn The method the event invokes
7867 * @param {Object} scope An object that becomes the scope of the handler
7868 * @param {boolean} options
7870 onDocumentReady : function(fn, scope, options){
7871 if(docReadyState){ // if it already fired
7872 docReadyEvent.addListener(fn, scope, options);
7873 docReadyEvent.fire();
7874 docReadyEvent.clearListeners();
7880 docReadyEvent.addListener(fn, scope, options);
7884 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7885 * @param {Function} fn The method the event invokes
7886 * @param {Object} scope An object that becomes the scope of the handler
7887 * @param {boolean} options
7889 onWindowResize : function(fn, scope, options)
7892 resizeEvent = new Roo.util.Event();
7893 resizeTask = new Roo.util.DelayedTask(function(){
7894 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7896 E.on(window, "resize", function()
7899 resizeTask.delay(50);
7901 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7905 resizeEvent.addListener(fn, scope, options);
7909 * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7910 * @param {Function} fn The method the event invokes
7911 * @param {Object} scope An object that becomes the scope of the handler
7912 * @param {boolean} options
7914 onTextResize : function(fn, scope, options){
7916 textEvent = new Roo.util.Event();
7917 var textEl = new Roo.Element(document.createElement('div'));
7918 textEl.dom.className = 'x-text-resize';
7919 textEl.dom.innerHTML = 'X';
7920 textEl.appendTo(document.body);
7921 textSize = textEl.dom.offsetHeight;
7922 setInterval(function(){
7923 if(textEl.dom.offsetHeight != textSize){
7924 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7926 }, this.textResizeInterval);
7928 textEvent.addListener(fn, scope, options);
7932 * Removes the passed window resize listener.
7933 * @param {Function} fn The method the event invokes
7934 * @param {Object} scope The scope of handler
7936 removeResizeListener : function(fn, scope){
7938 resizeEvent.removeListener(fn, scope);
7943 fireResize : function(){
7945 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7949 * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7953 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7955 textResizeInterval : 50
7960 * @scopeAlias pub=Roo.EventManager
7964 * Appends an event handler to an element (shorthand for addListener)
7965 * @param {String/HTMLElement} element The html element or id to assign the
7966 * @param {String} eventName The type of event to listen for
7967 * @param {Function} handler The method the event invokes
7968 * @param {Object} scope (optional) The scope in which to execute the handler
7969 * function. The handler function's "this" context.
7970 * @param {Object} options (optional) An object containing handler configuration
7971 * properties. This may contain any of the following properties:<ul>
7972 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7973 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7974 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7975 * <li>preventDefault {Boolean} True to prevent the default action</li>
7976 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7977 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7978 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7979 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7980 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7981 * by the specified number of milliseconds. If the event fires again within that time, the original
7982 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7985 * <b>Combining Options</b><br>
7986 * Using the options argument, it is possible to combine different types of listeners:<br>
7988 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7990 el.on('click', this.onClick, this, {
7997 * <b>Attaching multiple handlers in 1 call</b><br>
7998 * The method also allows for a single argument to be passed which is a config object containing properties
7999 * which specify multiple handlers.
8009 fn: this.onMouseOver
8018 * Or a shorthand syntax:<br>
8021 'click' : this.onClick,
8022 'mouseover' : this.onMouseOver,
8023 'mouseout' : this.onMouseOut
8027 pub.on = pub.addListener;
8028 pub.un = pub.removeListener;
8030 pub.stoppedMouseDownEvent = new Roo.util.Event();
8034 * Fires when the document is ready (before onload and before images are loaded). Shorthand of {@link Roo.EventManager#onDocumentReady}.
8035 * @param {Function} fn The method the event invokes
8036 * @param {Object} scope An object that becomes the scope of the handler
8037 * @param {boolean} override If true, the obj passed in becomes
8038 * the execution scope of the listener
8042 Roo.onReady = Roo.EventManager.onDocumentReady;
8044 Roo.onReady(function(){
8045 var bd = Roo.get(document.body);
8050 : Roo.isIE11 ? "roo-ie11"
8051 : Roo.isEdge ? "roo-edge"
8052 : Roo.isGecko ? "roo-gecko"
8053 : Roo.isOpera ? "roo-opera"
8054 : Roo.isSafari ? "roo-safari" : ""];
8057 cls.push("roo-mac");
8060 cls.push("roo-linux");
8063 cls.push("roo-ios");
8066 cls.push("roo-touch");
8068 if(Roo.isBorderBox){
8069 cls.push('roo-border-box');
8071 if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8072 var p = bd.dom.parentNode;
8074 p.className += ' roo-strict';
8077 bd.addClass(cls.join(' '));
8081 * @class Roo.EventObject
8082 * EventObject exposes the Yahoo! UI Event functionality directly on the object
8083 * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code
8086 function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8088 var target = e.getTarget();
8091 var myDiv = Roo.get("myDiv");
8092 myDiv.on("click", handleClick);
8094 Roo.EventManager.on("myDiv", 'click', handleClick);
8095 Roo.EventManager.addListener("myDiv", 'click', handleClick);
8099 Roo.EventObject = function(){
8101 var E = Roo.lib.Event;
8103 // safari keypress events for special keys return bad keycodes
8106 63235 : 39, // right
8109 63276 : 33, // page up
8110 63277 : 34, // page down
8111 63272 : 46, // delete
8116 // normalize button clicks
8117 var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8118 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8120 Roo.EventObjectImpl = function(e){
8122 this.setEvent(e.browserEvent || e);
8125 Roo.EventObjectImpl.prototype = {
8127 * Used to fix doc tools.
8128 * @scope Roo.EventObject.prototype
8134 /** The normal browser event */
8135 browserEvent : null,
8136 /** The button pressed in a mouse event */
8138 /** True if the shift key was down during the event */
8140 /** True if the control key was down during the event */
8142 /** True if the alt key was down during the event */
8201 setEvent : function(e){
8202 if(e == this || (e && e.browserEvent)){ // already wrapped
8205 this.browserEvent = e;
8207 // normalize buttons
8208 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8209 if(e.type == 'click' && this.button == -1){
8213 this.shiftKey = e.shiftKey;
8214 // mac metaKey behaves like ctrlKey
8215 this.ctrlKey = e.ctrlKey || e.metaKey;
8216 this.altKey = e.altKey;
8217 // in getKey these will be normalized for the mac
8218 this.keyCode = e.keyCode;
8219 // keyup warnings on firefox.
8220 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8221 // cache the target for the delayed and or buffered events
8222 this.target = E.getTarget(e);
8224 this.xy = E.getXY(e);
8227 this.shiftKey = false;
8228 this.ctrlKey = false;
8229 this.altKey = false;
8239 * Stop the event (preventDefault and stopPropagation)
8241 stopEvent : function(){
8242 if(this.browserEvent){
8243 if(this.browserEvent.type == 'mousedown'){
8244 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8246 E.stopEvent(this.browserEvent);
8251 * Prevents the browsers default handling of the event.
8253 preventDefault : function(){
8254 if(this.browserEvent){
8255 E.preventDefault(this.browserEvent);
8260 isNavKeyPress : function(){
8261 var k = this.keyCode;
8262 k = Roo.isSafari ? (safariKeys[k] || k) : k;
8263 return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8266 isSpecialKey : function(){
8267 var k = this.keyCode;
8268 return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13 || k == 40 || k == 27 ||
8269 (k == 16) || (k == 17) ||
8270 (k >= 18 && k <= 20) ||
8271 (k >= 33 && k <= 35) ||
8272 (k >= 36 && k <= 39) ||
8273 (k >= 44 && k <= 45);
8276 * Cancels bubbling of the event.
8278 stopPropagation : function(){
8279 if(this.browserEvent){
8280 if(this.type == 'mousedown'){
8281 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8283 E.stopPropagation(this.browserEvent);
8288 * Gets the key code for the event.
8291 getCharCode : function(){
8292 return this.charCode || this.keyCode;
8296 * Returns a normalized keyCode for the event.
8297 * @return {Number} The key code
8299 getKey : function(){
8300 var k = this.keyCode || this.charCode;
8301 return Roo.isSafari ? (safariKeys[k] || k) : k;
8305 * Gets the x coordinate of the event.
8308 getPageX : function(){
8313 * Gets the y coordinate of the event.
8316 getPageY : function(){
8321 * Gets the time of the event.
8324 getTime : function(){
8325 if(this.browserEvent){
8326 return E.getTime(this.browserEvent);
8332 * Gets the page coordinates of the event.
8333 * @return {Array} The xy values like [x, y]
8340 * Gets the target for the event.
8341 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8342 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8343 search as a number or element (defaults to 10 || document.body)
8344 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8345 * @return {HTMLelement}
8347 getTarget : function(selector, maxDepth, returnEl){
8348 return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8351 * Gets the related target.
8352 * @return {HTMLElement}
8354 getRelatedTarget : function(){
8355 if(this.browserEvent){
8356 return E.getRelatedTarget(this.browserEvent);
8362 * Normalizes mouse wheel delta across browsers
8363 * @return {Number} The delta
8365 getWheelDelta : function(){
8366 var e = this.browserEvent;
8368 if(e.wheelDelta){ /* IE/Opera. */
8369 delta = e.wheelDelta/120;
8370 }else if(e.detail){ /* Mozilla case. */
8371 delta = -e.detail/3;
8377 * Returns true if the control, meta, shift or alt key was pressed during this event.
8380 hasModifier : function(){
8381 return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8385 * Returns true if the target of this event equals el or is a child of el
8386 * @param {String/HTMLElement/Element} el
8387 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8390 within : function(el, related){
8391 var t = this[related ? "getRelatedTarget" : "getTarget"]();
8392 return t && Roo.fly(el).contains(t);
8395 getPoint : function(){
8396 return new Roo.lib.Point(this.xy[0], this.xy[1]);
8400 return new Roo.EventObjectImpl();
8405 * Ext JS Library 1.1.1
8406 * Copyright(c) 2006-2007, Ext JS, LLC.
8408 * Originally Released Under LGPL - original licence link has changed is not relivant.
8411 * <script type="text/javascript">
8415 // was in Composite Element!??!?!
8418 var D = Roo.lib.Dom;
8419 var E = Roo.lib.Event;
8420 var A = Roo.lib.Anim;
8422 // local style camelizing for speed
8424 var camelRe = /(-[a-z])/gi;
8425 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8426 var view = document.defaultView;
8429 * @class Roo.Element
8430 * Represents an Element in the DOM.<br><br>
8433 var el = Roo.get("my-div");
8436 var el = getEl("my-div");
8438 // or with a DOM element
8439 var el = Roo.get(myDivElement);
8441 * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8442 * each call instead of constructing a new one.<br><br>
8443 * <b>Animations</b><br />
8444 * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8445 * should either be a boolean (true) or an object literal with animation options. The animation options are:
8447 Option Default Description
8448 --------- -------- ---------------------------------------------
8449 duration .35 The duration of the animation in seconds
8450 easing easeOut The YUI easing method
8451 callback none A function to execute when the anim completes
8452 scope this The scope (this) of the callback function
8454 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8455 * manipulate the animation. Here's an example:
8457 var el = Roo.get("my-div");
8462 // default animation
8463 el.setWidth(100, true);
8465 // animation with some options set
8472 // using the "anim" property to get the Anim object
8478 el.setWidth(100, opt);
8480 if(opt.anim.isAnimated()){
8484 * <b> Composite (Collections of) Elements</b><br />
8485 * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8486 * @constructor Create a new Element directly.
8487 * @param {String/HTMLElement} element
8488 * @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).
8490 Roo.Element = function(element, forceNew)
8492 var dom = typeof element == "string" ?
8493 document.getElementById(element) : element;
8495 this.listeners = {};
8497 if(!dom){ // invalid id/element
8501 if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8502 return Roo.Element.cache[id];
8512 * The DOM element ID
8515 this.id = id || Roo.id(dom);
8517 return this; // assumed for cctor?
8520 var El = Roo.Element;
8524 * The element's default display mode (defaults to "")
8527 originalDisplay : "",
8530 // note this is overridden in BS version..
8533 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8539 * Sets the element's visibility mode. When setVisible() is called it
8540 * will use this to determine whether to set the visibility or the display property.
8541 * @param visMode Element.VISIBILITY or Element.DISPLAY
8542 * @return {Roo.Element} this
8544 setVisibilityMode : function(visMode){
8545 this.visibilityMode = visMode;
8549 * Convenience method for setVisibilityMode(Element.DISPLAY)
8550 * @param {String} display (optional) What to set display to when visible
8551 * @return {Roo.Element} this
8553 enableDisplayMode : function(display){
8554 this.setVisibilityMode(El.DISPLAY);
8555 if(typeof display != "undefined") { this.originalDisplay = display; }
8560 * 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)
8561 * @param {String} selector The simple selector to test
8562 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8563 search as a number or element (defaults to 10 || document.body)
8564 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8565 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8567 findParent : function(simpleSelector, maxDepth, returnEl){
8568 var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8569 maxDepth = maxDepth || 50;
8570 if(typeof maxDepth != "number"){
8571 stopEl = Roo.getDom(maxDepth);
8574 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8575 if(dq.is(p, simpleSelector)){
8576 return returnEl ? Roo.get(p) : p;
8586 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8587 * @param {String} selector The simple selector to test
8588 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8589 search as a number or element (defaults to 10 || document.body)
8590 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8591 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8593 findParentNode : function(simpleSelector, maxDepth, returnEl){
8594 var p = Roo.fly(this.dom.parentNode, '_internal');
8595 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8599 * Looks at the scrollable parent element
8601 findScrollableParent : function()
8603 var overflowRegex = /(auto|scroll)/;
8605 if(this.getStyle('position') === 'fixed'){
8606 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8609 var excludeStaticParent = this.getStyle('position') === "absolute";
8611 for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8613 if (excludeStaticParent && parent.getStyle('position') === "static") {
8617 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8621 if(parent.dom.nodeName.toLowerCase() == 'body'){
8622 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8626 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8630 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8631 * This is a shortcut for findParentNode() that always returns an Roo.Element.
8632 * @param {String} selector The simple selector to test
8633 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8634 search as a number or element (defaults to 10 || document.body)
8635 * @return {Roo.Element} The matching DOM node (or null if no match was found)
8637 up : function(simpleSelector, maxDepth){
8638 return this.findParentNode(simpleSelector, maxDepth, true);
8644 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8645 * @param {String} selector The simple selector to test
8646 * @return {Boolean} True if this element matches the selector, else false
8648 is : function(simpleSelector){
8649 return Roo.DomQuery.is(this.dom, simpleSelector);
8653 * Perform animation on this element.
8654 * @param {Object} args The YUI animation control args
8655 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8656 * @param {Function} onComplete (optional) Function to call when animation completes
8657 * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8658 * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8659 * @return {Roo.Element} this
8661 animate : function(args, duration, onComplete, easing, animType){
8662 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8667 * @private Internal animation call
8669 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8670 animType = animType || 'run';
8672 var anim = Roo.lib.Anim[animType](
8674 (opt.duration || defaultDur) || .35,
8675 (opt.easing || defaultEase) || 'easeOut',
8677 Roo.callback(cb, this);
8678 Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8686 // private legacy anim prep
8687 preanim : function(a, i){
8688 return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8692 * Removes worthless text nodes
8693 * @param {Boolean} forceReclean (optional) By default the element
8694 * keeps track if it has been cleaned already so
8695 * you can call this over and over. However, if you update the element and
8696 * need to force a reclean, you can pass true.
8698 clean : function(forceReclean){
8699 if(this.isCleaned && forceReclean !== true){
8703 var d = this.dom, n = d.firstChild, ni = -1;
8705 var nx = n.nextSibling;
8706 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8713 this.isCleaned = true;
8718 calcOffsetsTo : function(el){
8721 var restorePos = false;
8722 if(el.getStyle('position') == 'static'){
8723 el.position('relative');
8728 while(op && op != d && op.tagName != 'HTML'){
8731 op = op.offsetParent;
8734 el.position('static');
8740 * Scrolls this element into view within the passed container.
8741 * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8742 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8743 * @return {Roo.Element} this
8745 scrollIntoView : function(container, hscroll){
8746 var c = Roo.getDom(container) || document.body;
8749 var o = this.calcOffsetsTo(c),
8752 b = t+el.offsetHeight,
8753 r = l+el.offsetWidth;
8755 var ch = c.clientHeight;
8756 var ct = parseInt(c.scrollTop, 10);
8757 var cl = parseInt(c.scrollLeft, 10);
8759 var cr = cl + c.clientWidth;
8767 if(hscroll !== false){
8771 c.scrollLeft = r-c.clientWidth;
8778 scrollChildIntoView : function(child, hscroll){
8779 Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8783 * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8784 * the new height may not be available immediately.
8785 * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8786 * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8787 * @param {Function} onComplete (optional) Function to call when animation completes
8788 * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8789 * @return {Roo.Element} this
8791 autoHeight : function(animate, duration, onComplete, easing){
8792 var oldHeight = this.getHeight();
8794 this.setHeight(1); // force clipping
8795 setTimeout(function(){
8796 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8798 this.setHeight(height);
8800 if(typeof onComplete == "function"){
8804 this.setHeight(oldHeight); // restore original height
8805 this.setHeight(height, animate, duration, function(){
8807 if(typeof onComplete == "function") { onComplete(); }
8808 }.createDelegate(this), easing);
8810 }.createDelegate(this), 0);
8815 * Returns true if this element is an ancestor of the passed element
8816 * @param {HTMLElement/String} el The element to check
8817 * @return {Boolean} True if this element is an ancestor of el, else false
8819 contains : function(el){
8820 if(!el){return false;}
8821 return D.isAncestor(this.dom, el.dom ? el.dom : el);
8825 * Checks whether the element is currently visible using both visibility and display properties.
8826 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8827 * @return {Boolean} True if the element is currently visible, else false
8829 isVisible : function(deep) {
8830 var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8831 if(deep !== true || !vis){
8834 var p = this.dom.parentNode;
8835 while(p && p.tagName.toLowerCase() != "body"){
8836 if(!Roo.fly(p, '_isVisible').isVisible()){
8845 * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8846 * @param {String} selector The CSS selector
8847 * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8848 * @return {CompositeElement/CompositeElementLite} The composite element
8850 select : function(selector, unique){
8851 return El.select(selector, unique, this.dom);
8855 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8856 * @param {String} selector The CSS selector
8857 * @return {Array} An array of the matched nodes
8859 query : function(selector, unique){
8860 return Roo.DomQuery.select(selector, this.dom);
8864 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8865 * @param {String} selector The CSS selector
8866 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8867 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8869 child : function(selector, returnDom){
8870 var n = Roo.DomQuery.selectNode(selector, this.dom);
8871 return returnDom ? n : Roo.get(n);
8875 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8876 * @param {String} selector The CSS selector
8877 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8878 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8880 down : function(selector, returnDom){
8881 var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8882 return returnDom ? n : Roo.get(n);
8886 * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8887 * @param {String} group The group the DD object is member of
8888 * @param {Object} config The DD config object
8889 * @param {Object} overrides An object containing methods to override/implement on the DD object
8890 * @return {Roo.dd.DD} The DD object
8892 initDD : function(group, config, overrides){
8893 var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8894 return Roo.apply(dd, overrides);
8898 * Initializes a {@link Roo.dd.DDProxy} object for this element.
8899 * @param {String} group The group the DDProxy object is member of
8900 * @param {Object} config The DDProxy config object
8901 * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8902 * @return {Roo.dd.DDProxy} The DDProxy object
8904 initDDProxy : function(group, config, overrides){
8905 var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8906 return Roo.apply(dd, overrides);
8910 * Initializes a {@link Roo.dd.DDTarget} object for this element.
8911 * @param {String} group The group the DDTarget object is member of
8912 * @param {Object} config The DDTarget config object
8913 * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8914 * @return {Roo.dd.DDTarget} The DDTarget object
8916 initDDTarget : function(group, config, overrides){
8917 var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8918 return Roo.apply(dd, overrides);
8922 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8923 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8924 * @param {Boolean} visible Whether the element is visible
8925 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8926 * @return {Roo.Element} this
8928 setVisible : function(visible, animate){
8930 if(this.visibilityMode == El.DISPLAY){
8931 this.setDisplayed(visible);
8934 this.dom.style.visibility = visible ? "visible" : "hidden";
8937 // closure for composites
8939 var visMode = this.visibilityMode;
8941 this.setOpacity(.01);
8942 this.setVisible(true);
8944 this.anim({opacity: { to: (visible?1:0) }},
8945 this.preanim(arguments, 1),
8946 null, .35, 'easeIn', function(){
8948 if(visMode == El.DISPLAY){
8949 dom.style.display = "none";
8951 dom.style.visibility = "hidden";
8953 Roo.get(dom).setOpacity(1);
8961 * Returns true if display is not "none"
8964 isDisplayed : function() {
8965 return this.getStyle("display") != "none";
8969 * Toggles the element's visibility or display, depending on visibility mode.
8970 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8971 * @return {Roo.Element} this
8973 toggle : function(animate){
8974 this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8979 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8980 * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8981 * @return {Roo.Element} this
8983 setDisplayed : function(value) {
8984 if(typeof value == "boolean"){
8985 value = value ? this.originalDisplay : "none";
8987 this.setStyle("display", value);
8992 * Tries to focus the element. Any exceptions are caught and ignored.
8993 * @return {Roo.Element} this
8995 focus : function() {
9003 * Tries to blur the element. Any exceptions are caught and ignored.
9004 * @return {Roo.Element} this
9014 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9015 * @param {String/Array} className The CSS class to add, or an array of classes
9016 * @return {Roo.Element} this
9018 addClass : function(className){
9019 if(className instanceof Array){
9020 for(var i = 0, len = className.length; i < len; i++) {
9021 this.addClass(className[i]);
9024 if(className && !this.hasClass(className)){
9025 if (this.dom instanceof SVGElement) {
9026 this.dom.className.baseVal =this.dom.className.baseVal + " " + className;
9028 this.dom.className = this.dom.className + " " + className;
9036 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9037 * @param {String/Array} className The CSS class to add, or an array of classes
9038 * @return {Roo.Element} this
9040 radioClass : function(className){
9041 var siblings = this.dom.parentNode.childNodes;
9042 for(var i = 0; i < siblings.length; i++) {
9043 var s = siblings[i];
9044 if(s.nodeType == 1){
9045 Roo.get(s).removeClass(className);
9048 this.addClass(className);
9053 * Removes one or more CSS classes from the element.
9054 * @param {String/Array} className The CSS class to remove, or an array of classes
9055 * @return {Roo.Element} this
9057 removeClass : function(className){
9059 var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9060 if(!className || !cn){
9063 if(className instanceof Array){
9064 for(var i = 0, len = className.length; i < len; i++) {
9065 this.removeClass(className[i]);
9068 if(this.hasClass(className)){
9069 var re = this.classReCache[className];
9071 re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9072 this.classReCache[className] = re;
9074 if (this.dom instanceof SVGElement) {
9075 this.dom.className.baseVal = cn.replace(re, " ");
9077 this.dom.className = cn.replace(re, " ");
9088 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9089 * @param {String} className The CSS class to toggle
9090 * @return {Roo.Element} this
9092 toggleClass : function(className){
9093 if(this.hasClass(className)){
9094 this.removeClass(className);
9096 this.addClass(className);
9102 * Checks if the specified CSS class exists on this element's DOM node.
9103 * @param {String} className The CSS class to check for
9104 * @return {Boolean} True if the class exists, else false
9106 hasClass : function(className){
9107 if (this.dom instanceof SVGElement) {
9108 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1;
9110 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9114 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
9115 * @param {String} oldClassName The CSS class to replace
9116 * @param {String} newClassName The replacement CSS class
9117 * @return {Roo.Element} this
9119 replaceClass : function(oldClassName, newClassName){
9120 this.removeClass(oldClassName);
9121 this.addClass(newClassName);
9126 * Returns an object with properties matching the styles requested.
9127 * For example, el.getStyles('color', 'font-size', 'width') might return
9128 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9129 * @param {String} style1 A style name
9130 * @param {String} style2 A style name
9131 * @param {String} etc.
9132 * @return {Object} The style object
9134 getStyles : function(){
9135 var a = arguments, len = a.length, r = {};
9136 for(var i = 0; i < len; i++){
9137 r[a[i]] = this.getStyle(a[i]);
9143 * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9144 * @param {String} property The style property whose value is returned.
9145 * @return {String} The current value of the style property for this element.
9147 getStyle : function(){
9148 return view && view.getComputedStyle ?
9150 var el = this.dom, v, cs, camel;
9151 if(prop == 'float'){
9154 if(el.style && (v = el.style[prop])){
9157 if(cs = view.getComputedStyle(el, "")){
9158 if(!(camel = propCache[prop])){
9159 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9166 var el = this.dom, v, cs, camel;
9167 if(prop == 'opacity'){
9168 if(typeof el.style.filter == 'string'){
9169 var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9171 var fv = parseFloat(m[1]);
9173 return fv ? fv / 100 : 0;
9178 }else if(prop == 'float'){
9179 prop = "styleFloat";
9181 if(!(camel = propCache[prop])){
9182 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9184 if(v = el.style[camel]){
9187 if(cs = el.currentStyle){
9195 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9196 * @param {String/Object} property The style property to be set, or an object of multiple styles.
9197 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9198 * @return {Roo.Element} this
9200 setStyle : function(prop, value){
9201 if(typeof prop == "string"){
9203 if (prop == 'float') {
9204 this.setStyle(Roo.isIE ? 'styleFloat' : 'cssFloat', value);
9209 if(!(camel = propCache[prop])){
9210 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9213 if(camel == 'opacity') {
9214 this.setOpacity(value);
9216 this.dom.style[camel] = value;
9219 for(var style in prop){
9220 if(typeof prop[style] != "function"){
9221 this.setStyle(style, prop[style]);
9229 * More flexible version of {@link #setStyle} for setting style properties.
9230 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9231 * a function which returns such a specification.
9232 * @return {Roo.Element} this
9234 applyStyles : function(style){
9235 Roo.DomHelper.applyStyles(this.dom, style);
9240 * 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).
9241 * @return {Number} The X position of the element
9244 return D.getX(this.dom);
9248 * 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).
9249 * @return {Number} The Y position of the element
9252 return D.getY(this.dom);
9256 * 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).
9257 * @return {Array} The XY position of the element
9260 return D.getXY(this.dom);
9264 * 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).
9265 * @param {Number} The X position of the element
9266 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9267 * @return {Roo.Element} this
9269 setX : function(x, animate){
9271 D.setX(this.dom, x);
9273 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9279 * 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).
9280 * @param {Number} The Y position of the element
9281 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9282 * @return {Roo.Element} this
9284 setY : function(y, animate){
9286 D.setY(this.dom, y);
9288 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9294 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9295 * @param {String} left The left CSS property value
9296 * @return {Roo.Element} this
9298 setLeft : function(left){
9299 this.setStyle("left", this.addUnits(left));
9304 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9305 * @param {String} top The top CSS property value
9306 * @return {Roo.Element} this
9308 setTop : function(top){
9309 this.setStyle("top", this.addUnits(top));
9314 * Sets the element's CSS right style.
9315 * @param {String} right The right CSS property value
9316 * @return {Roo.Element} this
9318 setRight : function(right){
9319 this.setStyle("right", this.addUnits(right));
9324 * Sets the element's CSS bottom style.
9325 * @param {String} bottom The bottom CSS property value
9326 * @return {Roo.Element} this
9328 setBottom : function(bottom){
9329 this.setStyle("bottom", this.addUnits(bottom));
9334 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9335 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9336 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9337 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9338 * @return {Roo.Element} this
9340 setXY : function(pos, animate){
9342 D.setXY(this.dom, pos);
9344 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9350 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9351 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9352 * @param {Number} x X value for new position (coordinates are page-based)
9353 * @param {Number} y Y value for new position (coordinates are page-based)
9354 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9355 * @return {Roo.Element} this
9357 setLocation : function(x, y, animate){
9358 this.setXY([x, y], this.preanim(arguments, 2));
9363 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9364 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9365 * @param {Number} x X value for new position (coordinates are page-based)
9366 * @param {Number} y Y value for new position (coordinates are page-based)
9367 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9368 * @return {Roo.Element} this
9370 moveTo : function(x, y, animate){
9371 this.setXY([x, y], this.preanim(arguments, 2));
9376 * Returns the region of the given element.
9377 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9378 * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9380 getRegion : function(){
9381 return D.getRegion(this.dom);
9385 * Returns the offset height of the element
9386 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9387 * @return {Number} The element's height
9389 getHeight : function(contentHeight){
9390 var h = this.dom.offsetHeight || 0;
9391 return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9395 * Returns the offset width of the element
9396 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9397 * @return {Number} The element's width
9399 getWidth : function(contentWidth){
9400 var w = this.dom.offsetWidth || 0;
9401 return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9405 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9406 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9407 * if a height has not been set using CSS.
9410 getComputedHeight : function(){
9411 var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9413 h = parseInt(this.getStyle('height'), 10) || 0;
9414 if(!this.isBorderBox()){
9415 h += this.getFrameWidth('tb');
9422 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9423 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9424 * if a width has not been set using CSS.
9427 getComputedWidth : function(){
9428 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9430 w = parseInt(this.getStyle('width'), 10) || 0;
9431 if(!this.isBorderBox()){
9432 w += this.getFrameWidth('lr');
9439 * Returns the size of the element.
9440 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9441 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9443 getSize : function(contentSize){
9444 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9448 * Returns the width and height of the viewport.
9449 * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9451 getViewSize : function(){
9452 var d = this.dom, doc = document, aw = 0, ah = 0;
9453 if(d == doc || d == doc.body){
9454 return {width : D.getViewWidth(), height: D.getViewHeight()};
9457 width : d.clientWidth,
9458 height: d.clientHeight
9464 * Returns the value of the "value" attribute
9465 * @param {Boolean} asNumber true to parse the value as a number
9466 * @return {String/Number}
9468 getValue : function(asNumber){
9469 return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9473 adjustWidth : function(width){
9474 if(typeof width == "number"){
9475 if(this.autoBoxAdjust && !this.isBorderBox()){
9476 width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9486 adjustHeight : function(height){
9487 if(typeof height == "number"){
9488 if(this.autoBoxAdjust && !this.isBorderBox()){
9489 height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9499 * Set the width of the element
9500 * @param {Number} width The new width
9501 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9502 * @return {Roo.Element} this
9504 setWidth : function(width, animate){
9505 width = this.adjustWidth(width);
9507 this.dom.style.width = this.addUnits(width);
9509 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9515 * Set the height of the element
9516 * @param {Number} height The new height
9517 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9518 * @return {Roo.Element} this
9520 setHeight : function(height, animate){
9521 height = this.adjustHeight(height);
9523 this.dom.style.height = this.addUnits(height);
9525 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9531 * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9532 * @param {Number} width The new width
9533 * @param {Number} height The new height
9534 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9535 * @return {Roo.Element} this
9537 setSize : function(width, height, animate){
9538 if(typeof width == "object"){ // in case of object from getSize()
9539 height = width.height; width = width.width;
9541 width = this.adjustWidth(width); height = this.adjustHeight(height);
9543 this.dom.style.width = this.addUnits(width);
9544 this.dom.style.height = this.addUnits(height);
9546 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9552 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9553 * @param {Number} x X value for new position (coordinates are page-based)
9554 * @param {Number} y Y value for new position (coordinates are page-based)
9555 * @param {Number} width The new width
9556 * @param {Number} height The new height
9557 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9558 * @return {Roo.Element} this
9560 setBounds : function(x, y, width, height, animate){
9562 this.setSize(width, height);
9563 this.setLocation(x, y);
9565 width = this.adjustWidth(width); height = this.adjustHeight(height);
9566 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9567 this.preanim(arguments, 4), 'motion');
9573 * 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.
9574 * @param {Roo.lib.Region} region The region to fill
9575 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9576 * @return {Roo.Element} this
9578 setRegion : function(region, animate){
9579 this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9584 * Appends an event handler
9586 * @param {String} eventName The type of event to append
9587 * @param {Function} fn The method the event invokes
9588 * @param {Object} scope (optional) The scope (this object) of the fn
9589 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
9591 addListener : function(eventName, fn, scope, options)
9593 if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9594 this.addListener('touchstart', this.onTapHandler, this);
9597 // we need to handle a special case where dom element is a svg element.
9598 // in this case we do not actua
9603 if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9604 if (typeof(this.listeners[eventName]) == 'undefined') {
9605 this.listeners[eventName] = new Roo.util.Event(this, eventName);
9607 this.listeners[eventName].addListener(fn, scope, options);
9612 Roo.EventManager.on(this.dom, eventName, fn, scope || this, options);
9617 onTapHandler : function(event)
9619 if(!this.tapedTwice) {
9620 this.tapedTwice = true;
9622 setTimeout( function() {
9623 s.tapedTwice = false;
9627 event.preventDefault();
9628 var revent = new MouseEvent('dblclick', {
9634 this.dom.dispatchEvent(revent);
9635 //action on double tap goes below
9640 * Removes an event handler from this element
9641 * @param {String} eventName the type of event to remove
9642 * @param {Function} fn the method the event invokes
9643 * @param {Function} scope (needed for svg fake listeners)
9644 * @return {Roo.Element} this
9646 removeListener : function(eventName, fn, scope){
9647 Roo.EventManager.removeListener(this.dom, eventName, fn);
9648 if (typeof(this.listeners) == 'undefined' || typeof(this.listeners[eventName]) == 'undefined') {
9651 this.listeners[eventName].removeListener(fn, scope);
9656 * Removes all previous added listeners from this element
9657 * @return {Roo.Element} this
9659 removeAllListeners : function(){
9660 E.purgeElement(this.dom);
9661 this.listeners = {};
9665 relayEvent : function(eventName, observable){
9666 this.on(eventName, function(e){
9667 observable.fireEvent(eventName, e);
9673 * Set the opacity of the element
9674 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9675 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9676 * @return {Roo.Element} this
9678 setOpacity : function(opacity, animate){
9680 var s = this.dom.style;
9683 s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9684 (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9686 s.opacity = opacity;
9689 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9695 * Gets the left X coordinate
9696 * @param {Boolean} local True to get the local css position instead of page coordinate
9699 getLeft : function(local){
9703 return parseInt(this.getStyle("left"), 10) || 0;
9708 * Gets the right X coordinate of the element (element X position + element width)
9709 * @param {Boolean} local True to get the local css position instead of page coordinate
9712 getRight : function(local){
9714 return this.getX() + this.getWidth();
9716 return (this.getLeft(true) + this.getWidth()) || 0;
9721 * Gets the top Y coordinate
9722 * @param {Boolean} local True to get the local css position instead of page coordinate
9725 getTop : function(local) {
9729 return parseInt(this.getStyle("top"), 10) || 0;
9734 * Gets the bottom Y coordinate of the element (element Y position + element height)
9735 * @param {Boolean} local True to get the local css position instead of page coordinate
9738 getBottom : function(local){
9740 return this.getY() + this.getHeight();
9742 return (this.getTop(true) + this.getHeight()) || 0;
9747 * Initializes positioning on this element. If a desired position is not passed, it will make the
9748 * the element positioned relative IF it is not already positioned.
9749 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9750 * @param {Number} zIndex (optional) The zIndex to apply
9751 * @param {Number} x (optional) Set the page X position
9752 * @param {Number} y (optional) Set the page Y position
9754 position : function(pos, zIndex, x, y){
9756 if(this.getStyle('position') == 'static'){
9757 this.setStyle('position', 'relative');
9760 this.setStyle("position", pos);
9763 this.setStyle("z-index", zIndex);
9765 if(x !== undefined && y !== undefined){
9767 }else if(x !== undefined){
9769 }else if(y !== undefined){
9775 * Clear positioning back to the default when the document was loaded
9776 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9777 * @return {Roo.Element} this
9779 clearPositioning : function(value){
9787 "position" : "static"
9793 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9794 * snapshot before performing an update and then restoring the element.
9797 getPositioning : function(){
9798 var l = this.getStyle("left");
9799 var t = this.getStyle("top");
9801 "position" : this.getStyle("position"),
9803 "right" : l ? "" : this.getStyle("right"),
9805 "bottom" : t ? "" : this.getStyle("bottom"),
9806 "z-index" : this.getStyle("z-index")
9811 * Gets the width of the border(s) for the specified side(s)
9812 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9813 * passing lr would get the border (l)eft width + the border (r)ight width.
9814 * @return {Number} The width of the sides passed added together
9816 getBorderWidth : function(side){
9817 return this.addStyles(side, El.borders);
9821 * Gets the width of the padding(s) for the specified side(s)
9822 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9823 * passing lr would get the padding (l)eft + the padding (r)ight.
9824 * @return {Number} The padding of the sides passed added together
9826 getPadding : function(side){
9827 return this.addStyles(side, El.paddings);
9831 * Set positioning with an object returned by getPositioning().
9832 * @param {Object} posCfg
9833 * @return {Roo.Element} this
9835 setPositioning : function(pc){
9836 this.applyStyles(pc);
9837 if(pc.right == "auto"){
9838 this.dom.style.right = "";
9840 if(pc.bottom == "auto"){
9841 this.dom.style.bottom = "";
9847 fixDisplay : function(){
9848 if(this.getStyle("display") == "none"){
9849 this.setStyle("visibility", "hidden");
9850 this.setStyle("display", this.originalDisplay); // first try reverting to default
9851 if(this.getStyle("display") == "none"){ // if that fails, default to block
9852 this.setStyle("display", "block");
9858 * Quick set left and top adding default units
9859 * @param {String} left The left CSS property value
9860 * @param {String} top The top CSS property value
9861 * @return {Roo.Element} this
9863 setLeftTop : function(left, top){
9864 this.dom.style.left = this.addUnits(left);
9865 this.dom.style.top = this.addUnits(top);
9870 * Move this element relative to its current position.
9871 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9872 * @param {Number} distance How far to move the element in pixels
9873 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9874 * @return {Roo.Element} this
9876 move : function(direction, distance, animate){
9877 var xy = this.getXY();
9878 direction = direction.toLowerCase();
9882 this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9886 this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9891 this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9896 this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9903 * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9904 * @return {Roo.Element} this
9907 if(!this.isClipped){
9908 this.isClipped = true;
9909 this.originalClip = {
9910 "o": this.getStyle("overflow"),
9911 "x": this.getStyle("overflow-x"),
9912 "y": this.getStyle("overflow-y")
9914 this.setStyle("overflow", "hidden");
9915 this.setStyle("overflow-x", "hidden");
9916 this.setStyle("overflow-y", "hidden");
9922 * Return clipping (overflow) to original clipping before clip() was called
9923 * @return {Roo.Element} this
9925 unclip : function(){
9927 this.isClipped = false;
9928 var o = this.originalClip;
9929 if(o.o){this.setStyle("overflow", o.o);}
9930 if(o.x){this.setStyle("overflow-x", o.x);}
9931 if(o.y){this.setStyle("overflow-y", o.y);}
9938 * Gets the x,y coordinates specified by the anchor position on the element.
9939 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo} for details on supported anchor positions.
9940 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9941 * {width: (target width), height: (target height)} (defaults to the element's current size)
9942 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9943 * @return {Array} [x, y] An array containing the element's x and y coordinates
9945 getAnchorXY : function(anchor, local, s){
9946 //Passing a different size is useful for pre-calculating anchors,
9947 //especially for anchored animations that change the el size.
9949 var w, h, vp = false;
9952 if(d == document.body || d == document){
9954 w = D.getViewWidth(); h = D.getViewHeight();
9956 w = this.getWidth(); h = this.getHeight();
9959 w = s.width; h = s.height;
9961 var x = 0, y = 0, r = Math.round;
9962 switch((anchor || "tl").toLowerCase()){
10000 if(local === true){
10004 var sc = this.getScroll();
10005 return [x + sc.left, y + sc.top];
10007 //Add the element's offset xy
10008 var o = this.getXY();
10009 return [x+o[0], y+o[1]];
10013 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10014 * supported position values.
10015 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10016 * @param {String} position The position to align to.
10017 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10018 * @return {Array} [x, y]
10020 getAlignToXY : function(el, p, o)
10025 throw "Element.alignTo with an element that doesn't exist";
10027 var c = false; //constrain to viewport
10028 var p1 = "", p2 = "";
10033 }else if(p == "?"){
10035 }else if(p.indexOf("-") == -1){
10038 p = p.toLowerCase();
10039 var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10041 throw "Element.alignTo with an invalid alignment " + p;
10043 p1 = m[1]; p2 = m[2]; c = !!m[3];
10045 //Subtract the aligned el's internal xy from the target's offset xy
10046 //plus custom offset to get the aligned el's new offset xy
10047 var a1 = this.getAnchorXY(p1, true);
10048 var a2 = el.getAnchorXY(p2, false);
10049 var x = a2[0] - a1[0] + o[0];
10050 var y = a2[1] - a1[1] + o[1];
10052 //constrain the aligned el to viewport if necessary
10053 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10054 // 5px of margin for ie
10055 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10057 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10058 //perpendicular to the vp border, allow the aligned el to slide on that border,
10059 //otherwise swap the aligned el to the opposite border of the target.
10060 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10061 var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10062 var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t") );
10063 var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10065 var doc = document;
10066 var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10067 var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10069 if((x+w) > dw + scrollX){
10070 x = swapX ? r.left-w : dw+scrollX-w;
10073 x = swapX ? r.right : scrollX;
10075 if((y+h) > dh + scrollY){
10076 y = swapY ? r.top-h : dh+scrollY-h;
10079 y = swapY ? r.bottom : scrollY;
10086 getConstrainToXY : function(){
10087 var os = {top:0, left:0, bottom:0, right: 0};
10089 return function(el, local, offsets, proposedXY){
10091 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10093 var vw, vh, vx = 0, vy = 0;
10094 if(el.dom == document.body || el.dom == document){
10095 vw = Roo.lib.Dom.getViewWidth();
10096 vh = Roo.lib.Dom.getViewHeight();
10098 vw = el.dom.clientWidth;
10099 vh = el.dom.clientHeight;
10101 var vxy = el.getXY();
10107 var s = el.getScroll();
10109 vx += offsets.left + s.left;
10110 vy += offsets.top + s.top;
10112 vw -= offsets.right;
10113 vh -= offsets.bottom;
10118 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10119 var x = xy[0], y = xy[1];
10120 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10122 // only move it if it needs it
10125 // first validate right/bottom
10134 // then make sure top/left isn't negative
10143 return moved ? [x, y] : false;
10148 adjustForConstraints : function(xy, parent, offsets){
10149 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
10153 * Aligns this element with another element relative to the specified anchor points. If the other element is the
10154 * document it aligns it to the viewport.
10155 * The position parameter is optional, and can be specified in any one of the following formats:
10157 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10158 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10159 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
10160 * deprecated in favor of the newer two anchor syntax below</i>.</li>
10161 * <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
10162 * element's anchor point, and the second value is used as the target's anchor point.</li>
10164 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
10165 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10166 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
10167 * that specified in order to enforce the viewport constraints.
10168 * Following are all of the supported anchor positions:
10171 ----- -----------------------------
10172 tl The top left corner (default)
10173 t The center of the top edge
10174 tr The top right corner
10175 l The center of the left edge
10176 c In the center of the element
10177 r The center of the right edge
10178 bl The bottom left corner
10179 b The center of the bottom edge
10180 br The bottom right corner
10184 // align el to other-el using the default positioning ("tl-bl", non-constrained)
10185 el.alignTo("other-el");
10187 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10188 el.alignTo("other-el", "tr?");
10190 // align the bottom right corner of el with the center left edge of other-el
10191 el.alignTo("other-el", "br-l?");
10193 // align the center of el with the bottom left corner of other-el and
10194 // adjust the x position by -6 pixels (and the y position by 0)
10195 el.alignTo("other-el", "c-bl", [-6, 0]);
10197 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10198 * @param {String} position The position to align to.
10199 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10200 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10201 * @return {Roo.Element} this
10203 alignTo : function(element, position, offsets, animate){
10204 var xy = this.getAlignToXY(element, position, offsets);
10205 this.setXY(xy, this.preanim(arguments, 3));
10210 * Anchors an element to another element and realigns it when the window is resized.
10211 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10212 * @param {String} position The position to align to.
10213 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10214 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10215 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10216 * is a number, it is used as the buffer delay (defaults to 50ms).
10217 * @param {Function} callback The function to call after the animation finishes
10218 * @return {Roo.Element} this
10220 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10221 var action = function(){
10222 this.alignTo(el, alignment, offsets, animate);
10223 Roo.callback(callback, this);
10225 Roo.EventManager.onWindowResize(action, this);
10226 var tm = typeof monitorScroll;
10227 if(tm != 'undefined'){
10228 Roo.EventManager.on(window, 'scroll', action, this,
10229 {buffer: tm == 'number' ? monitorScroll : 50});
10231 action.call(this); // align immediately
10235 * Clears any opacity settings from this element. Required in some cases for IE.
10236 * @return {Roo.Element} this
10238 clearOpacity : function(){
10239 if (window.ActiveXObject) {
10240 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10241 this.dom.style.filter = "";
10244 this.dom.style.opacity = "";
10245 this.dom.style["-moz-opacity"] = "";
10246 this.dom.style["-khtml-opacity"] = "";
10252 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10253 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10254 * @return {Roo.Element} this
10256 hide : function(animate){
10257 this.setVisible(false, this.preanim(arguments, 0));
10262 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10263 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10264 * @return {Roo.Element} this
10266 show : function(animate){
10267 this.setVisible(true, this.preanim(arguments, 0));
10272 * @private Test if size has a unit, otherwise appends the default
10274 addUnits : function(size){
10275 return Roo.Element.addUnits(size, this.defaultUnit);
10279 * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10280 * @return {Roo.Element} this
10282 beginMeasure : function(){
10284 if(el.offsetWidth || el.offsetHeight){
10285 return this; // offsets work already
10288 var p = this.dom, b = document.body; // start with this element
10289 while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10290 var pe = Roo.get(p);
10291 if(pe.getStyle('display') == 'none'){
10292 changed.push({el: p, visibility: pe.getStyle("visibility")});
10293 p.style.visibility = "hidden";
10294 p.style.display = "block";
10298 this._measureChanged = changed;
10304 * Restores displays to before beginMeasure was called
10305 * @return {Roo.Element} this
10307 endMeasure : function(){
10308 var changed = this._measureChanged;
10310 for(var i = 0, len = changed.length; i < len; i++) {
10311 var r = changed[i];
10312 r.el.style.visibility = r.visibility;
10313 r.el.style.display = "none";
10315 this._measureChanged = null;
10321 * Update the innerHTML of this element, optionally searching for and processing scripts
10322 * @param {String} html The new HTML
10323 * @param {Boolean} loadScripts (optional) true to look for and process scripts
10324 * @param {Function} callback For async script loading you can be noticed when the update completes
10325 * @return {Roo.Element} this
10327 update : function(html, loadScripts, callback){
10328 if(typeof html == "undefined"){
10331 if(loadScripts !== true){
10332 this.dom.innerHTML = html;
10333 if(typeof callback == "function"){
10339 var dom = this.dom;
10341 html += '<span id="' + id + '"></span>';
10343 E.onAvailable(id, function(){
10344 var hd = document.getElementsByTagName("head")[0];
10345 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10346 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10347 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10350 while(match = re.exec(html)){
10351 var attrs = match[1];
10352 var srcMatch = attrs ? attrs.match(srcRe) : false;
10353 if(srcMatch && srcMatch[2]){
10354 var s = document.createElement("script");
10355 s.src = srcMatch[2];
10356 var typeMatch = attrs.match(typeRe);
10357 if(typeMatch && typeMatch[2]){
10358 s.type = typeMatch[2];
10361 }else if(match[2] && match[2].length > 0){
10362 if(window.execScript) {
10363 window.execScript(match[2]);
10371 window.eval(match[2]);
10375 var el = document.getElementById(id);
10376 if(el){el.parentNode.removeChild(el);}
10377 if(typeof callback == "function"){
10381 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10386 * Direct access to the UpdateManager update() method (takes the same parameters).
10387 * @param {String/Function} url The url for this request or a function to call to get the url
10388 * @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}
10389 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10390 * @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.
10391 * @return {Roo.Element} this
10394 var um = this.getUpdateManager();
10395 um.update.apply(um, arguments);
10400 * Gets this element's UpdateManager
10401 * @return {Roo.UpdateManager} The UpdateManager
10403 getUpdateManager : function(){
10404 if(!this.updateManager){
10405 this.updateManager = new Roo.UpdateManager(this);
10407 return this.updateManager;
10411 * Disables text selection for this element (normalized across browsers)
10412 * @return {Roo.Element} this
10414 unselectable : function(){
10415 this.dom.unselectable = "on";
10416 this.swallowEvent("selectstart", true);
10417 this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10418 this.addClass("x-unselectable");
10423 * Calculates the x, y to center this element on the screen
10424 * @return {Array} The x, y values [x, y]
10426 getCenterXY : function(){
10427 return this.getAlignToXY(document, 'c-c');
10431 * Centers the Element in either the viewport, or another Element.
10432 * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10434 center : function(centerIn){
10435 this.alignTo(centerIn || document, 'c-c');
10440 * Tests various css rules/browsers to determine if this element uses a border box
10441 * @return {Boolean}
10443 isBorderBox : function(){
10444 return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10448 * Return a box {x, y, width, height} that can be used to set another elements
10449 * size/location to match this element.
10450 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10451 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10452 * @return {Object} box An object in the format {x, y, width, height}
10454 getBox : function(contentBox, local){
10459 var left = parseInt(this.getStyle("left"), 10) || 0;
10460 var top = parseInt(this.getStyle("top"), 10) || 0;
10463 var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10465 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10467 var l = this.getBorderWidth("l")+this.getPadding("l");
10468 var r = this.getBorderWidth("r")+this.getPadding("r");
10469 var t = this.getBorderWidth("t")+this.getPadding("t");
10470 var b = this.getBorderWidth("b")+this.getPadding("b");
10471 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)};
10473 bx.right = bx.x + bx.width;
10474 bx.bottom = bx.y + bx.height;
10479 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10480 for more information about the sides.
10481 * @param {String} sides
10484 getFrameWidth : function(sides, onlyContentBox){
10485 return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10489 * 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.
10490 * @param {Object} box The box to fill {x, y, width, height}
10491 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10492 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10493 * @return {Roo.Element} this
10495 setBox : function(box, adjust, animate){
10496 var w = box.width, h = box.height;
10497 if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10498 w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10499 h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10501 this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10506 * Forces the browser to repaint this element
10507 * @return {Roo.Element} this
10509 repaint : function(){
10510 var dom = this.dom;
10511 this.addClass("x-repaint");
10512 setTimeout(function(){
10513 Roo.get(dom).removeClass("x-repaint");
10519 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10520 * then it returns the calculated width of the sides (see getPadding)
10521 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10522 * @return {Object/Number}
10524 getMargins : function(side){
10527 top: parseInt(this.getStyle("margin-top"), 10) || 0,
10528 left: parseInt(this.getStyle("margin-left"), 10) || 0,
10529 bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10530 right: parseInt(this.getStyle("margin-right"), 10) || 0
10533 return this.addStyles(side, El.margins);
10538 addStyles : function(sides, styles){
10540 for(var i = 0, len = sides.length; i < len; i++){
10541 v = this.getStyle(styles[sides.charAt(i)]);
10543 w = parseInt(v, 10);
10551 * Creates a proxy element of this element
10552 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10553 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10554 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10555 * @return {Roo.Element} The new proxy element
10557 createProxy : function(config, renderTo, matchBox){
10559 renderTo = Roo.getDom(renderTo);
10561 renderTo = document.body;
10563 config = typeof config == "object" ?
10564 config : {tag : "div", cls: config};
10565 var proxy = Roo.DomHelper.append(renderTo, config, true);
10567 proxy.setBox(this.getBox());
10573 * Puts a mask over this element to disable user interaction. Requires core.css.
10574 * This method can only be applied to elements which accept child nodes.
10575 * @param {String} msg (optional) A message to display in the mask
10576 * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10577 * @return {Element} The mask element
10579 mask : function(msg, msgCls)
10581 if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10582 this.setStyle("position", "relative");
10585 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10588 this.addClass("x-masked");
10589 this._mask.setDisplayed(true);
10593 var dom = this.dom;
10594 while (dom && dom.style) {
10595 if (!isNaN(parseInt(dom.style.zIndex))) {
10596 z = Math.max(z, parseInt(dom.style.zIndex));
10598 dom = dom.parentNode;
10600 // if we are masking the body - then it hides everything..
10601 if (this.dom == document.body) {
10603 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10604 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10607 if(typeof msg == 'string'){
10608 if(!this._maskMsg){
10609 this._maskMsg = Roo.DomHelper.append(this.dom, {
10610 cls: "roo-el-mask-msg",
10614 cls: 'fa fa-spinner fa-spin'
10622 var mm = this._maskMsg;
10623 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10624 if (mm.dom.lastChild) { // weird IE issue?
10625 mm.dom.lastChild.innerHTML = msg;
10627 mm.setDisplayed(true);
10629 mm.setStyle('z-index', z + 102);
10631 if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10632 this._mask.setHeight(this.getHeight());
10634 this._mask.setStyle('z-index', z + 100);
10640 * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10641 * it is cached for reuse.
10643 unmask : function(removeEl){
10645 if(removeEl === true){
10646 this._mask.remove();
10649 this._maskMsg.remove();
10650 delete this._maskMsg;
10653 this._mask.setDisplayed(false);
10655 this._maskMsg.setDisplayed(false);
10659 this.removeClass("x-masked");
10663 * Returns true if this element is masked
10664 * @return {Boolean}
10666 isMasked : function(){
10667 return this._mask && this._mask.isVisible();
10671 * Creates an iframe shim for this element to keep selects and other windowed objects from
10673 * @return {Roo.Element} The new shim element
10675 createShim : function(){
10676 var el = document.createElement('iframe');
10677 el.frameBorder = 'no';
10678 el.className = 'roo-shim';
10679 if(Roo.isIE && Roo.isSecure){
10680 el.src = Roo.SSL_SECURE_URL;
10682 var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10683 shim.autoBoxAdjust = false;
10688 * Removes this element from the DOM and deletes it from the cache
10690 remove : function(){
10691 if(this.dom.parentNode){
10692 this.dom.parentNode.removeChild(this.dom);
10694 delete El.cache[this.dom.id];
10698 * Sets up event handlers to add and remove a css class when the mouse is over this element
10699 * @param {String} className
10700 * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10701 * mouseout events for children elements
10702 * @return {Roo.Element} this
10704 addClassOnOver : function(className, preventFlicker){
10705 this.on("mouseover", function(){
10706 Roo.fly(this, '_internal').addClass(className);
10708 var removeFn = function(e){
10709 if(preventFlicker !== true || !e.within(this, true)){
10710 Roo.fly(this, '_internal').removeClass(className);
10713 this.on("mouseout", removeFn, this.dom);
10718 * Sets up event handlers to add and remove a css class when this element has the focus
10719 * @param {String} className
10720 * @return {Roo.Element} this
10722 addClassOnFocus : function(className){
10723 this.on("focus", function(){
10724 Roo.fly(this, '_internal').addClass(className);
10726 this.on("blur", function(){
10727 Roo.fly(this, '_internal').removeClass(className);
10732 * 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)
10733 * @param {String} className
10734 * @return {Roo.Element} this
10736 addClassOnClick : function(className){
10737 var dom = this.dom;
10738 this.on("mousedown", function(){
10739 Roo.fly(dom, '_internal').addClass(className);
10740 var d = Roo.get(document);
10741 var fn = function(){
10742 Roo.fly(dom, '_internal').removeClass(className);
10743 d.removeListener("mouseup", fn);
10745 d.on("mouseup", fn);
10751 * Stops the specified event from bubbling and optionally prevents the default action
10752 * @param {String} eventName
10753 * @param {Boolean} preventDefault (optional) true to prevent the default action too
10754 * @return {Roo.Element} this
10756 swallowEvent : function(eventName, preventDefault){
10757 var fn = function(e){
10758 e.stopPropagation();
10759 if(preventDefault){
10760 e.preventDefault();
10763 if(eventName instanceof Array){
10764 for(var i = 0, len = eventName.length; i < len; i++){
10765 this.on(eventName[i], fn);
10769 this.on(eventName, fn);
10776 fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10779 * Sizes this element to its parent element's dimensions performing
10780 * neccessary box adjustments.
10781 * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10782 * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10783 * @return {Roo.Element} this
10785 fitToParent : function(monitorResize, targetParent) {
10786 Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10787 this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10788 if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10791 var p = Roo.get(targetParent || this.dom.parentNode);
10792 this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10793 if (monitorResize === true) {
10794 this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10795 Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10801 * Gets the next sibling, skipping text nodes
10802 * @return {HTMLElement} The next sibling or null
10804 getNextSibling : function(){
10805 var n = this.dom.nextSibling;
10806 while(n && n.nodeType != 1){
10813 * Gets the previous sibling, skipping text nodes
10814 * @return {HTMLElement} The previous sibling or null
10816 getPrevSibling : function(){
10817 var n = this.dom.previousSibling;
10818 while(n && n.nodeType != 1){
10819 n = n.previousSibling;
10826 * Appends the passed element(s) to this element
10827 * @param {String/HTMLElement/Array/Element/CompositeElement} el
10828 * @return {Roo.Element} this
10830 appendChild: function(el){
10837 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10838 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
10839 * automatically generated with the specified attributes.
10840 * @param {HTMLElement} insertBefore (optional) a child element of this element
10841 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10842 * @return {Roo.Element} The new child element
10844 createChild: function(config, insertBefore, returnDom){
10845 config = config || {tag:'div'};
10847 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10849 return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
10853 * Appends this element to the passed element
10854 * @param {String/HTMLElement/Element} el The new parent element
10855 * @return {Roo.Element} this
10857 appendTo: function(el){
10858 el = Roo.getDom(el);
10859 el.appendChild(this.dom);
10864 * Inserts this element before the passed element in the DOM
10865 * @param {String/HTMLElement/Element} el The element to insert before
10866 * @return {Roo.Element} this
10868 insertBefore: function(el){
10869 el = Roo.getDom(el);
10870 el.parentNode.insertBefore(this.dom, el);
10875 * Inserts this element after the passed element in the DOM
10876 * @param {String/HTMLElement/Element} el The element to insert after
10877 * @return {Roo.Element} this
10879 insertAfter: function(el){
10880 el = Roo.getDom(el);
10881 el.parentNode.insertBefore(this.dom, el.nextSibling);
10886 * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10887 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10888 * @return {Roo.Element} The new child
10890 insertFirst: function(el, returnDom){
10892 if(typeof el == 'object' && !el.nodeType){ // dh config
10893 return this.createChild(el, this.dom.firstChild, returnDom);
10895 el = Roo.getDom(el);
10896 this.dom.insertBefore(el, this.dom.firstChild);
10897 return !returnDom ? Roo.get(el) : el;
10902 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10903 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10904 * @param {String} where (optional) 'before' or 'after' defaults to before
10905 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10906 * @return {Roo.Element} the inserted Element
10908 insertSibling: function(el, where, returnDom){
10909 where = where ? where.toLowerCase() : 'before';
10911 var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10913 if(typeof el == 'object' && !el.nodeType){ // dh config
10914 if(where == 'after' && !this.dom.nextSibling){
10915 rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10917 rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10921 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10922 where == 'before' ? this.dom : this.dom.nextSibling);
10931 * Creates and wraps this element with another element
10932 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10933 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10934 * @return {HTMLElement/Element} The newly created wrapper element
10936 wrap: function(config, returnDom){
10938 config = {tag: "div"};
10940 var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10941 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10946 * Replaces the passed element with this element
10947 * @param {String/HTMLElement/Element} el The element to replace
10948 * @return {Roo.Element} this
10950 replace: function(el){
10952 this.insertBefore(el);
10958 * Inserts an html fragment into this element
10959 * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10960 * @param {String} html The HTML fragment
10961 * @param {Boolean} returnEl True to return an Roo.Element
10962 * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10964 insertHtml : function(where, html, returnEl){
10965 var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10966 return returnEl ? Roo.get(el) : el;
10970 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10971 * @param {Object} o The object with the attributes
10972 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10973 * @return {Roo.Element} this
10975 set : function(o, useSet){
10977 useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10978 for(var attr in o){
10979 if(attr == "style" || typeof o[attr] == "function") { continue; }
10981 el.className = o["cls"];
10984 el.setAttribute(attr, o[attr]);
10986 el[attr] = o[attr];
10991 Roo.DomHelper.applyStyles(el, o.style);
10997 * Convenience method for constructing a KeyMap
10998 * @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:
10999 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11000 * @param {Function} fn The function to call
11001 * @param {Object} scope (optional) The scope of the function
11002 * @return {Roo.KeyMap} The KeyMap created
11004 addKeyListener : function(key, fn, scope){
11006 if(typeof key != "object" || key instanceof Array){
11022 return new Roo.KeyMap(this, config);
11026 * Creates a KeyMap for this element
11027 * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11028 * @return {Roo.KeyMap} The KeyMap created
11030 addKeyMap : function(config){
11031 return new Roo.KeyMap(this, config);
11035 * Returns true if this element is scrollable.
11036 * @return {Boolean}
11038 isScrollable : function(){
11039 var dom = this.dom;
11040 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11044 * 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().
11045 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11046 * @param {Number} value The new scroll value
11047 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11048 * @return {Element} this
11051 scrollTo : function(side, value, animate){
11052 var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11053 if(!animate || !A){
11054 this.dom[prop] = value;
11056 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11057 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11063 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11064 * within this element's scrollable range.
11065 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11066 * @param {Number} distance How far to scroll the element in pixels
11067 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11068 * @return {Boolean} Returns true if a scroll was triggered or false if the element
11069 * was scrolled as far as it could go.
11071 scroll : function(direction, distance, animate){
11072 if(!this.isScrollable()){
11076 var l = el.scrollLeft, t = el.scrollTop;
11077 var w = el.scrollWidth, h = el.scrollHeight;
11078 var cw = el.clientWidth, ch = el.clientHeight;
11079 direction = direction.toLowerCase();
11080 var scrolled = false;
11081 var a = this.preanim(arguments, 2);
11086 var v = Math.min(l + distance, w-cw);
11087 this.scrollTo("left", v, a);
11094 var v = Math.max(l - distance, 0);
11095 this.scrollTo("left", v, a);
11103 var v = Math.max(t - distance, 0);
11104 this.scrollTo("top", v, a);
11112 var v = Math.min(t + distance, h-ch);
11113 this.scrollTo("top", v, a);
11122 * Translates the passed page coordinates into left/top css values for this element
11123 * @param {Number/Array} x The page x or an array containing [x, y]
11124 * @param {Number} y The page y
11125 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11127 translatePoints : function(x, y){
11128 if(typeof x == 'object' || x instanceof Array){
11129 y = x[1]; x = x[0];
11131 var p = this.getStyle('position');
11132 var o = this.getXY();
11134 var l = parseInt(this.getStyle('left'), 10);
11135 var t = parseInt(this.getStyle('top'), 10);
11138 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11141 t = (p == "relative") ? 0 : this.dom.offsetTop;
11144 return {left: (x - o[0] + l), top: (y - o[1] + t)};
11148 * Returns the current scroll position of the element.
11149 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11151 getScroll : function(){
11152 var d = this.dom, doc = document;
11153 if(d == doc || d == doc.body){
11154 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11155 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11156 return {left: l, top: t};
11158 return {left: d.scrollLeft, top: d.scrollTop};
11163 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11164 * are convert to standard 6 digit hex color.
11165 * @param {String} attr The css attribute
11166 * @param {String} defaultValue The default value to use when a valid color isn't found
11167 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11170 getColor : function(attr, defaultValue, prefix){
11171 var v = this.getStyle(attr);
11172 if(!v || v == "transparent" || v == "inherit") {
11173 return defaultValue;
11175 var color = typeof prefix == "undefined" ? "#" : prefix;
11176 if(v.substr(0, 4) == "rgb("){
11177 var rvs = v.slice(4, v.length -1).split(",");
11178 for(var i = 0; i < 3; i++){
11179 var h = parseInt(rvs[i]).toString(16);
11186 if(v.substr(0, 1) == "#"){
11187 if(v.length == 4) {
11188 for(var i = 1; i < 4; i++){
11189 var c = v.charAt(i);
11192 }else if(v.length == 7){
11193 color += v.substr(1);
11197 return(color.length > 5 ? color.toLowerCase() : defaultValue);
11201 * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11202 * gradient background, rounded corners and a 4-way shadow.
11203 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11204 * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11205 * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11206 * @return {Roo.Element} this
11208 boxWrap : function(cls){
11209 cls = cls || 'x-box';
11210 var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11211 el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11216 * Returns the value of a namespaced attribute from the element's underlying DOM node.
11217 * @param {String} namespace The namespace in which to look for the attribute
11218 * @param {String} name The attribute name
11219 * @return {String} The attribute value
11221 getAttributeNS : Roo.isIE ? function(ns, name){
11223 var type = typeof d[ns+":"+name];
11224 if(type != 'undefined' && type != 'unknown'){
11225 return d[ns+":"+name];
11228 } : function(ns, name){
11230 return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11235 * Sets or Returns the value the dom attribute value
11236 * @param {String|Object} name The attribute name (or object to set multiple attributes)
11237 * @param {String} value (optional) The value to set the attribute to
11238 * @return {String} The attribute value
11240 attr : function(name){
11241 if (arguments.length > 1) {
11242 this.dom.setAttribute(name, arguments[1]);
11243 return arguments[1];
11245 if (typeof(name) == 'object') {
11246 for(var i in name) {
11247 this.attr(i, name[i]);
11253 if (!this.dom.hasAttribute(name)) {
11256 return this.dom.getAttribute(name);
11263 var ep = El.prototype;
11266 * Appends an event handler (Shorthand for addListener)
11267 * @param {String} eventName The type of event to append
11268 * @param {Function} fn The method the event invokes
11269 * @param {Object} scope (optional) The scope (this object) of the fn
11270 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
11273 ep.on = ep.addListener;
11274 // backwards compat
11275 ep.mon = ep.addListener;
11278 * Removes an event handler from this element (shorthand for removeListener)
11279 * @param {String} eventName the type of event to remove
11280 * @param {Function} fn the method the event invokes
11281 * @return {Roo.Element} this
11284 ep.un = ep.removeListener;
11287 * true to automatically adjust width and height settings for box-model issues (default to true)
11289 ep.autoBoxAdjust = true;
11292 El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11295 El.addUnits = function(v, defaultUnit){
11296 if(v === "" || v == "auto"){
11299 if(v === undefined){
11302 if(typeof v == "number" || !El.unitPattern.test(v)){
11303 return v + (defaultUnit || 'px');
11308 // special markup used throughout Roo when box wrapping elements
11309 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>';
11311 * Visibility mode constant - Use visibility to hide element
11317 * Visibility mode constant - Use display to hide element
11323 El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11324 El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11325 El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11337 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11338 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11339 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11340 * @return {Element} The Element object
11343 El.get = function(el){
11345 if(!el){ return null; }
11346 if(typeof el == "string"){ // element id
11347 if(!(elm = document.getElementById(el))){
11350 if(ex = El.cache[el]){
11353 ex = El.cache[el] = new El(elm);
11356 }else if(el.tagName){ // dom element
11360 if(ex = El.cache[id]){
11363 ex = El.cache[id] = new El(el);
11366 }else if(el instanceof El){
11368 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11369 // catch case where it hasn't been appended
11370 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11373 }else if(el.isComposite){
11375 }else if(el instanceof Array){
11376 return El.select(el);
11377 }else if(el == document){
11378 // create a bogus element object representing the document object
11380 var f = function(){};
11381 f.prototype = El.prototype;
11383 docEl.dom = document;
11391 El.uncache = function(el){
11392 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11394 delete El.cache[a[i].id || a[i]];
11400 // Garbage collection - uncache elements/purge listeners on orphaned elements
11401 // so we don't hold a reference and cause the browser to retain them
11402 El.garbageCollect = function(){
11403 if(!Roo.enableGarbageCollector){
11404 clearInterval(El.collectorThread);
11407 for(var eid in El.cache){
11408 var el = El.cache[eid], d = el.dom;
11409 // -------------------------------------------------------
11410 // Determining what is garbage:
11411 // -------------------------------------------------------
11413 // dom node is null, definitely garbage
11414 // -------------------------------------------------------
11416 // no parentNode == direct orphan, definitely garbage
11417 // -------------------------------------------------------
11418 // !d.offsetParent && !document.getElementById(eid)
11419 // display none elements have no offsetParent so we will
11420 // also try to look it up by it's id. However, check
11421 // offsetParent first so we don't do unneeded lookups.
11422 // This enables collection of elements that are not orphans
11423 // directly, but somewhere up the line they have an orphan
11425 // -------------------------------------------------------
11426 if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11427 delete El.cache[eid];
11428 if(d && Roo.enableListenerCollection){
11434 El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11438 El.Flyweight = function(dom){
11441 El.Flyweight.prototype = El.prototype;
11443 El._flyweights = {};
11445 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11446 * the dom node can be overwritten by other code.
11447 * @param {String/HTMLElement} el The dom node or id
11448 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11449 * prevent conflicts (e.g. internally Roo uses "_internal")
11451 * @return {Element} The shared Element object
11453 El.fly = function(el, named){
11454 named = named || '_global';
11455 el = Roo.getDom(el);
11459 if(!El._flyweights[named]){
11460 El._flyweights[named] = new El.Flyweight();
11462 El._flyweights[named].dom = el;
11463 return El._flyweights[named];
11467 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11468 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11469 * Shorthand of {@link Roo.Element#get}
11470 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11471 * @return {Element} The Element object
11477 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11478 * the dom node can be overwritten by other code.
11479 * Shorthand of {@link Roo.Element#fly}
11480 * @param {String/HTMLElement} el The dom node or id
11481 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11482 * prevent conflicts (e.g. internally Roo uses "_internal")
11484 * @return {Element} The shared Element object
11490 // speedy lookup for elements never to box adjust
11491 var noBoxAdjust = Roo.isStrict ? {
11494 input:1, select:1, textarea:1
11496 if(Roo.isIE || Roo.isGecko){
11497 noBoxAdjust['button'] = 1;
11501 Roo.EventManager.on(window, 'unload', function(){
11503 delete El._flyweights;
11511 Roo.Element.selectorFunction = Roo.DomQuery.select;
11514 Roo.Element.select = function(selector, unique, root){
11516 if(typeof selector == "string"){
11517 els = Roo.Element.selectorFunction(selector, root);
11518 }else if(selector.length !== undefined){
11521 throw "Invalid selector";
11523 if(unique === true){
11524 return new Roo.CompositeElement(els);
11526 return new Roo.CompositeElementLite(els);
11530 * Selects elements based on the passed CSS selector to enable working on them as 1.
11531 * @param {String/Array} selector The CSS selector or an array of elements
11532 * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11533 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11534 * @return {CompositeElementLite/CompositeElement}
11538 Roo.select = Roo.Element.select;
11555 * Ext JS Library 1.1.1
11556 * Copyright(c) 2006-2007, Ext JS, LLC.
11558 * Originally Released Under LGPL - original licence link has changed is not relivant.
11561 * <script type="text/javascript">
11566 //Notifies Element that fx methods are available
11567 Roo.enableFx = true;
11571 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
11572 * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11573 * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the
11574 * Element effects to work.</p><br/>
11576 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11577 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11578 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11579 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
11580 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11581 * expected results and should be done with care.</p><br/>
11583 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11584 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
11587 ----- -----------------------------
11588 tl The top left corner
11589 t The center of the top edge
11590 tr The top right corner
11591 l The center of the left edge
11592 r The center of the right edge
11593 bl The bottom left corner
11594 b The center of the bottom edge
11595 br The bottom right corner
11597 * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11598 * below are common options that can be passed to any Fx method.</b>
11599 * @cfg {Function} callback A function called when the effect is finished
11600 * @cfg {Object} scope The scope of the effect function
11601 * @cfg {String} easing A valid Easing value for the effect
11602 * @cfg {String} afterCls A css class to apply after the effect
11603 * @cfg {Number} duration The length of time (in seconds) that the effect should last
11604 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11605 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
11606 * effects that end with the element being visually hidden, ignored otherwise)
11607 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11608 * a function which returns such a specification that will be applied to the Element after the effect finishes
11609 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11610 * @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
11611 * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11615 * Slides the element into view. An anchor point can be optionally passed to set the point of
11616 * origin for the slide effect. This function automatically handles wrapping the element with
11617 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11620 // default: slide the element in from the top
11623 // custom: slide the element in from the right with a 2-second duration
11624 el.slideIn('r', { duration: 2 });
11626 // common config options shown with default values
11632 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11633 * @param {Object} options (optional) Object literal with any of the Fx config options
11634 * @return {Roo.Element} The Element
11636 slideIn : function(anchor, o){
11637 var el = this.getFxEl();
11640 el.queueFx(o, function(){
11642 anchor = anchor || "t";
11644 // fix display to visibility
11647 // restore values after effect
11648 var r = this.getFxRestore();
11649 var b = this.getBox();
11650 // fixed size for slide
11654 var wrap = this.fxWrap(r.pos, o, "hidden");
11656 var st = this.dom.style;
11657 st.visibility = "visible";
11658 st.position = "absolute";
11660 // clear out temp styles after slide and unwrap
11661 var after = function(){
11662 el.fxUnwrap(wrap, r.pos, o);
11663 st.width = r.width;
11664 st.height = r.height;
11667 // time to calc the positions
11668 var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11670 switch(anchor.toLowerCase()){
11672 wrap.setSize(b.width, 0);
11673 st.left = st.bottom = "0";
11677 wrap.setSize(0, b.height);
11678 st.right = st.top = "0";
11682 wrap.setSize(0, b.height);
11683 wrap.setX(b.right);
11684 st.left = st.top = "0";
11685 a = {width: bw, points: pt};
11688 wrap.setSize(b.width, 0);
11689 wrap.setY(b.bottom);
11690 st.left = st.top = "0";
11691 a = {height: bh, points: pt};
11694 wrap.setSize(0, 0);
11695 st.right = st.bottom = "0";
11696 a = {width: bw, height: bh};
11699 wrap.setSize(0, 0);
11700 wrap.setY(b.y+b.height);
11701 st.right = st.top = "0";
11702 a = {width: bw, height: bh, points: pt};
11705 wrap.setSize(0, 0);
11706 wrap.setXY([b.right, b.bottom]);
11707 st.left = st.top = "0";
11708 a = {width: bw, height: bh, points: pt};
11711 wrap.setSize(0, 0);
11712 wrap.setX(b.x+b.width);
11713 st.left = st.bottom = "0";
11714 a = {width: bw, height: bh, points: pt};
11717 this.dom.style.visibility = "visible";
11720 arguments.callee.anim = wrap.fxanim(a,
11730 * Slides the element out of view. An anchor point can be optionally passed to set the end point
11731 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
11732 * 'hidden') but block elements will still take up space in the document. The element must be removed
11733 * from the DOM using the 'remove' config option if desired. This function automatically handles
11734 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11737 // default: slide the element out to the top
11740 // custom: slide the element out to the right with a 2-second duration
11741 el.slideOut('r', { duration: 2 });
11743 // common config options shown with default values
11751 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11752 * @param {Object} options (optional) Object literal with any of the Fx config options
11753 * @return {Roo.Element} The Element
11755 slideOut : function(anchor, o){
11756 var el = this.getFxEl();
11759 el.queueFx(o, function(){
11761 anchor = anchor || "t";
11763 // restore values after effect
11764 var r = this.getFxRestore();
11766 var b = this.getBox();
11767 // fixed size for slide
11771 var wrap = this.fxWrap(r.pos, o, "visible");
11773 var st = this.dom.style;
11774 st.visibility = "visible";
11775 st.position = "absolute";
11779 var after = function(){
11781 el.setDisplayed(false);
11786 el.fxUnwrap(wrap, r.pos, o);
11788 st.width = r.width;
11789 st.height = r.height;
11794 var a, zero = {to: 0};
11795 switch(anchor.toLowerCase()){
11797 st.left = st.bottom = "0";
11798 a = {height: zero};
11801 st.right = st.top = "0";
11805 st.left = st.top = "0";
11806 a = {width: zero, points: {to:[b.right, b.y]}};
11809 st.left = st.top = "0";
11810 a = {height: zero, points: {to:[b.x, b.bottom]}};
11813 st.right = st.bottom = "0";
11814 a = {width: zero, height: zero};
11817 st.right = st.top = "0";
11818 a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11821 st.left = st.top = "0";
11822 a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11825 st.left = st.bottom = "0";
11826 a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11830 arguments.callee.anim = wrap.fxanim(a,
11840 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
11841 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
11842 * The element must be removed from the DOM using the 'remove' config option if desired.
11848 // common config options shown with default values
11856 * @param {Object} options (optional) Object literal with any of the Fx config options
11857 * @return {Roo.Element} The Element
11859 puff : function(o){
11860 var el = this.getFxEl();
11863 el.queueFx(o, function(){
11864 this.clearOpacity();
11867 // restore values after effect
11868 var r = this.getFxRestore();
11869 var st = this.dom.style;
11871 var after = function(){
11873 el.setDisplayed(false);
11880 el.setPositioning(r.pos);
11881 st.width = r.width;
11882 st.height = r.height;
11887 var width = this.getWidth();
11888 var height = this.getHeight();
11890 arguments.callee.anim = this.fxanim({
11891 width : {to: this.adjustWidth(width * 2)},
11892 height : {to: this.adjustHeight(height * 2)},
11893 points : {by: [-(width * .5), -(height * .5)]},
11895 fontSize: {to:200, unit: "%"}
11906 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11907 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
11908 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11914 // all config options shown with default values
11922 * @param {Object} options (optional) Object literal with any of the Fx config options
11923 * @return {Roo.Element} The Element
11925 switchOff : function(o){
11926 var el = this.getFxEl();
11929 el.queueFx(o, function(){
11930 this.clearOpacity();
11933 // restore values after effect
11934 var r = this.getFxRestore();
11935 var st = this.dom.style;
11937 var after = function(){
11939 el.setDisplayed(false);
11945 el.setPositioning(r.pos);
11946 st.width = r.width;
11947 st.height = r.height;
11952 this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11953 this.clearOpacity();
11957 points:{by:[0, this.getHeight() * .5]}
11958 }, o, 'motion', 0.3, 'easeIn', after);
11959 }).defer(100, this);
11966 * Highlights the Element by setting a color (applies to the background-color by default, but can be
11967 * changed using the "attr" config option) and then fading back to the original color. If no original
11968 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11971 // default: highlight background to yellow
11974 // custom: highlight foreground text to blue for 2 seconds
11975 el.highlight("0000ff", { attr: 'color', duration: 2 });
11977 // common config options shown with default values
11978 el.highlight("ffff9c", {
11979 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11980 endColor: (current color) or "ffffff",
11985 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11986 * @param {Object} options (optional) Object literal with any of the Fx config options
11987 * @return {Roo.Element} The Element
11989 highlight : function(color, o){
11990 var el = this.getFxEl();
11993 el.queueFx(o, function(){
11994 color = color || "ffff9c";
11995 attr = o.attr || "backgroundColor";
11997 this.clearOpacity();
12000 var origColor = this.getColor(attr);
12001 var restoreColor = this.dom.style[attr];
12002 endColor = (o.endColor || origColor) || "ffffff";
12004 var after = function(){
12005 el.dom.style[attr] = restoreColor;
12010 a[attr] = {from: color, to: endColor};
12011 arguments.callee.anim = this.fxanim(a,
12021 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12024 // default: a single light blue ripple
12027 // custom: 3 red ripples lasting 3 seconds total
12028 el.frame("ff0000", 3, { duration: 3 });
12030 // common config options shown with default values
12031 el.frame("C3DAF9", 1, {
12032 duration: 1 //duration of entire animation (not each individual ripple)
12033 // Note: Easing is not configurable and will be ignored if included
12036 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12037 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12038 * @param {Object} options (optional) Object literal with any of the Fx config options
12039 * @return {Roo.Element} The Element
12041 frame : function(color, count, o){
12042 var el = this.getFxEl();
12045 el.queueFx(o, function(){
12046 color = color || "#C3DAF9";
12047 if(color.length == 6){
12048 color = "#" + color;
12050 count = count || 1;
12051 duration = o.duration || 1;
12054 var b = this.getBox();
12055 var animFn = function(){
12056 var proxy = this.createProxy({
12059 visbility:"hidden",
12060 position:"absolute",
12061 "z-index":"35000", // yee haw
12062 border:"0px solid " + color
12065 var scale = Roo.isBorderBox ? 2 : 1;
12067 top:{from:b.y, to:b.y - 20},
12068 left:{from:b.x, to:b.x - 20},
12069 borderWidth:{from:0, to:10},
12070 opacity:{from:1, to:0},
12071 height:{from:b.height, to:(b.height + (20*scale))},
12072 width:{from:b.width, to:(b.width + (20*scale))}
12073 }, duration, function(){
12077 animFn.defer((duration/2)*1000, this);
12088 * Creates a pause before any subsequent queued effects begin. If there are
12089 * no effects queued after the pause it will have no effect.
12094 * @param {Number} seconds The length of time to pause (in seconds)
12095 * @return {Roo.Element} The Element
12097 pause : function(seconds){
12098 var el = this.getFxEl();
12101 el.queueFx(o, function(){
12102 setTimeout(function(){
12104 }, seconds * 1000);
12110 * Fade an element in (from transparent to opaque). The ending opacity can be specified
12111 * using the "endOpacity" config option.
12114 // default: fade in from opacity 0 to 100%
12117 // custom: fade in from opacity 0 to 75% over 2 seconds
12118 el.fadeIn({ endOpacity: .75, duration: 2});
12120 // common config options shown with default values
12122 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12127 * @param {Object} options (optional) Object literal with any of the Fx config options
12128 * @return {Roo.Element} The Element
12130 fadeIn : function(o){
12131 var el = this.getFxEl();
12133 el.queueFx(o, function(){
12134 this.setOpacity(0);
12136 this.dom.style.visibility = 'visible';
12137 var to = o.endOpacity || 1;
12138 arguments.callee.anim = this.fxanim({opacity:{to:to}},
12139 o, null, .5, "easeOut", function(){
12141 this.clearOpacity();
12150 * Fade an element out (from opaque to transparent). The ending opacity can be specified
12151 * using the "endOpacity" config option.
12154 // default: fade out from the element's current opacity to 0
12157 // custom: fade out from the element's current opacity to 25% over 2 seconds
12158 el.fadeOut({ endOpacity: .25, duration: 2});
12160 // common config options shown with default values
12162 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12169 * @param {Object} options (optional) Object literal with any of the Fx config options
12170 * @return {Roo.Element} The Element
12172 fadeOut : function(o){
12173 var el = this.getFxEl();
12175 el.queueFx(o, function(){
12176 arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12177 o, null, .5, "easeOut", function(){
12178 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12179 this.dom.style.display = "none";
12181 this.dom.style.visibility = "hidden";
12183 this.clearOpacity();
12191 * Animates the transition of an element's dimensions from a starting height/width
12192 * to an ending height/width.
12195 // change height and width to 100x100 pixels
12196 el.scale(100, 100);
12198 // common config options shown with default values. The height and width will default to
12199 // the element's existing values if passed as null.
12202 [element's height], {
12207 * @param {Number} width The new width (pass undefined to keep the original width)
12208 * @param {Number} height The new height (pass undefined to keep the original height)
12209 * @param {Object} options (optional) Object literal with any of the Fx config options
12210 * @return {Roo.Element} The Element
12212 scale : function(w, h, o){
12213 this.shift(Roo.apply({}, o, {
12221 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12222 * Any of these properties not specified in the config object will not be changed. This effect
12223 * requires that at least one new dimension, position or opacity setting must be passed in on
12224 * the config object in order for the function to have any effect.
12227 // slide the element horizontally to x position 200 while changing the height and opacity
12228 el.shift({ x: 200, height: 50, opacity: .8 });
12230 // common config options shown with default values.
12232 width: [element's width],
12233 height: [element's height],
12234 x: [element's x position],
12235 y: [element's y position],
12236 opacity: [element's opacity],
12241 * @param {Object} options Object literal with any of the Fx config options
12242 * @return {Roo.Element} The Element
12244 shift : function(o){
12245 var el = this.getFxEl();
12247 el.queueFx(o, function(){
12248 var a = {}, w = o.width, h = o.height, x = o.x, y = o.y, op = o.opacity;
12249 if(w !== undefined){
12250 a.width = {to: this.adjustWidth(w)};
12252 if(h !== undefined){
12253 a.height = {to: this.adjustHeight(h)};
12255 if(x !== undefined || y !== undefined){
12257 x !== undefined ? x : this.getX(),
12258 y !== undefined ? y : this.getY()
12261 if(op !== undefined){
12262 a.opacity = {to: op};
12264 if(o.xy !== undefined){
12265 a.points = {to: o.xy};
12267 arguments.callee.anim = this.fxanim(a,
12268 o, 'motion', .35, "easeOut", function(){
12276 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
12277 * ending point of the effect.
12280 // default: slide the element downward while fading out
12283 // custom: slide the element out to the right with a 2-second duration
12284 el.ghost('r', { duration: 2 });
12286 // common config options shown with default values
12294 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12295 * @param {Object} options (optional) Object literal with any of the Fx config options
12296 * @return {Roo.Element} The Element
12298 ghost : function(anchor, o){
12299 var el = this.getFxEl();
12302 el.queueFx(o, function(){
12303 anchor = anchor || "b";
12305 // restore values after effect
12306 var r = this.getFxRestore();
12307 var w = this.getWidth(),
12308 h = this.getHeight();
12310 var st = this.dom.style;
12312 var after = function(){
12314 el.setDisplayed(false);
12320 el.setPositioning(r.pos);
12321 st.width = r.width;
12322 st.height = r.height;
12327 var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12328 switch(anchor.toLowerCase()){
12355 arguments.callee.anim = this.fxanim(a,
12365 * Ensures that all effects queued after syncFx is called on the element are
12366 * run concurrently. This is the opposite of {@link #sequenceFx}.
12367 * @return {Roo.Element} The Element
12369 syncFx : function(){
12370 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12379 * Ensures that all effects queued after sequenceFx is called on the element are
12380 * run in sequence. This is the opposite of {@link #syncFx}.
12381 * @return {Roo.Element} The Element
12383 sequenceFx : function(){
12384 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12386 concurrent : false,
12393 nextFx : function(){
12394 var ef = this.fxQueue[0];
12401 * Returns true if the element has any effects actively running or queued, else returns false.
12402 * @return {Boolean} True if element has active effects, else false
12404 hasActiveFx : function(){
12405 return this.fxQueue && this.fxQueue[0];
12409 * Stops any running effects and clears the element's internal effects queue if it contains
12410 * any additional effects that haven't started yet.
12411 * @return {Roo.Element} The Element
12413 stopFx : function(){
12414 if(this.hasActiveFx()){
12415 var cur = this.fxQueue[0];
12416 if(cur && cur.anim && cur.anim.isAnimated()){
12417 this.fxQueue = [cur]; // clear out others
12418 cur.anim.stop(true);
12425 beforeFx : function(o){
12426 if(this.hasActiveFx() && !o.concurrent){
12437 * Returns true if the element is currently blocking so that no other effect can be queued
12438 * until this effect is finished, else returns false if blocking is not set. This is commonly
12439 * used to ensure that an effect initiated by a user action runs to completion prior to the
12440 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12441 * @return {Boolean} True if blocking, else false
12443 hasFxBlock : function(){
12444 var q = this.fxQueue;
12445 return q && q[0] && q[0].block;
12449 queueFx : function(o, fn){
12453 if(!this.hasFxBlock()){
12454 Roo.applyIf(o, this.fxDefaults);
12456 var run = this.beforeFx(o);
12457 fn.block = o.block;
12458 this.fxQueue.push(fn);
12470 fxWrap : function(pos, o, vis){
12472 if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12475 wrapXY = this.getXY();
12477 var div = document.createElement("div");
12478 div.style.visibility = vis;
12479 wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12480 wrap.setPositioning(pos);
12481 if(wrap.getStyle("position") == "static"){
12482 wrap.position("relative");
12484 this.clearPositioning('auto');
12486 wrap.dom.appendChild(this.dom);
12488 wrap.setXY(wrapXY);
12495 fxUnwrap : function(wrap, pos, o){
12496 this.clearPositioning();
12497 this.setPositioning(pos);
12499 wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12505 getFxRestore : function(){
12506 var st = this.dom.style;
12507 return {pos: this.getPositioning(), width: st.width, height : st.height};
12511 afterFx : function(o){
12513 this.applyStyles(o.afterStyle);
12516 this.addClass(o.afterCls);
12518 if(o.remove === true){
12521 Roo.callback(o.callback, o.scope, [this]);
12523 this.fxQueue.shift();
12529 getFxEl : function(){ // support for composite element fx
12530 return Roo.get(this.dom);
12534 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12535 animType = animType || 'run';
12537 var anim = Roo.lib.Anim[animType](
12539 (opt.duration || defaultDur) || .35,
12540 (opt.easing || defaultEase) || 'easeOut',
12542 Roo.callback(cb, this);
12551 // backwords compat
12552 Roo.Fx.resize = Roo.Fx.scale;
12554 //When included, Roo.Fx is automatically applied to Element so that all basic
12555 //effects are available directly via the Element API
12556 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12558 * Ext JS Library 1.1.1
12559 * Copyright(c) 2006-2007, Ext JS, LLC.
12561 * Originally Released Under LGPL - original licence link has changed is not relivant.
12564 * <script type="text/javascript">
12569 * @class Roo.CompositeElement
12570 * Standard composite class. Creates a Roo.Element for every element in the collection.
12572 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12573 * actions will be performed on all the elements in this collection.</b>
12575 * All methods return <i>this</i> and can be chained.
12577 var els = Roo.select("#some-el div.some-class", true);
12578 // or select directly from an existing element
12579 var el = Roo.get('some-el');
12580 el.select('div.some-class', true);
12582 els.setWidth(100); // all elements become 100 width
12583 els.hide(true); // all elements fade out and hide
12585 els.setWidth(100).hide(true);
12588 Roo.CompositeElement = function(els){
12589 this.elements = [];
12590 this.addElements(els);
12592 Roo.CompositeElement.prototype = {
12594 addElements : function(els){
12598 if(typeof els == "string"){
12599 els = Roo.Element.selectorFunction(els);
12601 var yels = this.elements;
12602 var index = yels.length-1;
12603 for(var i = 0, len = els.length; i < len; i++) {
12604 yels[++index] = Roo.get(els[i]);
12610 * Clears this composite and adds the elements returned by the passed selector.
12611 * @param {String/Array} els A string CSS selector, an array of elements or an element
12612 * @return {CompositeElement} this
12614 fill : function(els){
12615 this.elements = [];
12621 * Filters this composite to only elements that match the passed selector.
12622 * @param {String} selector A string CSS selector
12623 * @param {Boolean} inverse return inverse filter (not matches)
12624 * @return {CompositeElement} this
12626 filter : function(selector, inverse){
12628 inverse = inverse || false;
12629 this.each(function(el){
12630 var match = inverse ? !el.is(selector) : el.is(selector);
12632 els[els.length] = el.dom;
12639 invoke : function(fn, args){
12640 var els = this.elements;
12641 for(var i = 0, len = els.length; i < len; i++) {
12642 Roo.Element.prototype[fn].apply(els[i], args);
12647 * Adds elements to this composite.
12648 * @param {String/Array} els A string CSS selector, an array of elements or an element
12649 * @return {CompositeElement} this
12651 add : function(els){
12652 if(typeof els == "string"){
12653 this.addElements(Roo.Element.selectorFunction(els));
12654 }else if(els.length !== undefined){
12655 this.addElements(els);
12657 this.addElements([els]);
12662 * Calls the passed function passing (el, this, index) for each element in this composite.
12663 * @param {Function} fn The function to call
12664 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12665 * @return {CompositeElement} this
12667 each : function(fn, scope){
12668 var els = this.elements;
12669 for(var i = 0, len = els.length; i < len; i++){
12670 if(fn.call(scope || els[i], els[i], this, i) === false) {
12678 * Returns the Element object at the specified index
12679 * @param {Number} index
12680 * @return {Roo.Element}
12682 item : function(index){
12683 return this.elements[index] || null;
12687 * Returns the first Element
12688 * @return {Roo.Element}
12690 first : function(){
12691 return this.item(0);
12695 * Returns the last Element
12696 * @return {Roo.Element}
12699 return this.item(this.elements.length-1);
12703 * Returns the number of elements in this composite
12706 getCount : function(){
12707 return this.elements.length;
12711 * Returns true if this composite contains the passed element
12714 contains : function(el){
12715 return this.indexOf(el) !== -1;
12719 * Returns true if this composite contains the passed element
12722 indexOf : function(el){
12723 return this.elements.indexOf(Roo.get(el));
12728 * Removes the specified element(s).
12729 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12730 * or an array of any of those.
12731 * @param {Boolean} removeDom (optional) True to also remove the element from the document
12732 * @return {CompositeElement} this
12734 removeElement : function(el, removeDom){
12735 if(el instanceof Array){
12736 for(var i = 0, len = el.length; i < len; i++){
12737 this.removeElement(el[i]);
12741 var index = typeof el == 'number' ? el : this.indexOf(el);
12744 var d = this.elements[index];
12748 d.parentNode.removeChild(d);
12751 this.elements.splice(index, 1);
12757 * Replaces the specified element with the passed element.
12758 * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12760 * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12761 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12762 * @return {CompositeElement} this
12764 replaceElement : function(el, replacement, domReplace){
12765 var index = typeof el == 'number' ? el : this.indexOf(el);
12768 this.elements[index].replaceWith(replacement);
12770 this.elements.splice(index, 1, Roo.get(replacement))
12777 * Removes all elements.
12779 clear : function(){
12780 this.elements = [];
12784 Roo.CompositeElement.createCall = function(proto, fnName){
12785 if(!proto[fnName]){
12786 proto[fnName] = function(){
12787 return this.invoke(fnName, arguments);
12791 for(var fnName in Roo.Element.prototype){
12792 if(typeof Roo.Element.prototype[fnName] == "function"){
12793 Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12799 * Ext JS Library 1.1.1
12800 * Copyright(c) 2006-2007, Ext JS, LLC.
12802 * Originally Released Under LGPL - original licence link has changed is not relivant.
12805 * <script type="text/javascript">
12809 * @class Roo.CompositeElementLite
12810 * @extends Roo.CompositeElement
12811 * Flyweight composite class. Reuses the same Roo.Element for element operations.
12813 var els = Roo.select("#some-el div.some-class");
12814 // or select directly from an existing element
12815 var el = Roo.get('some-el');
12816 el.select('div.some-class');
12818 els.setWidth(100); // all elements become 100 width
12819 els.hide(true); // all elements fade out and hide
12821 els.setWidth(100).hide(true);
12822 </code></pre><br><br>
12823 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12824 * actions will be performed on all the elements in this collection.</b>
12826 Roo.CompositeElementLite = function(els){
12827 Roo.CompositeElementLite.superclass.constructor.call(this, els);
12828 this.el = new Roo.Element.Flyweight();
12830 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12831 addElements : function(els){
12833 if(els instanceof Array){
12834 this.elements = this.elements.concat(els);
12836 var yels = this.elements;
12837 var index = yels.length-1;
12838 for(var i = 0, len = els.length; i < len; i++) {
12839 yels[++index] = els[i];
12845 invoke : function(fn, args){
12846 var els = this.elements;
12848 for(var i = 0, len = els.length; i < len; i++) {
12850 Roo.Element.prototype[fn].apply(el, args);
12855 * Returns a flyweight Element of the dom element object at the specified index
12856 * @param {Number} index
12857 * @return {Roo.Element}
12859 item : function(index){
12860 if(!this.elements[index]){
12863 this.el.dom = this.elements[index];
12867 // fixes scope with flyweight
12868 addListener : function(eventName, handler, scope, opt){
12869 var els = this.elements;
12870 for(var i = 0, len = els.length; i < len; i++) {
12871 Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12877 * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12878 * passed is the flyweight (shared) Roo.Element instance, so if you require a
12879 * a reference to the dom node, use el.dom.</b>
12880 * @param {Function} fn The function to call
12881 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12882 * @return {CompositeElement} this
12884 each : function(fn, scope){
12885 var els = this.elements;
12887 for(var i = 0, len = els.length; i < len; i++){
12889 if(fn.call(scope || el, el, this, i) === false){
12896 indexOf : function(el){
12897 return this.elements.indexOf(Roo.getDom(el));
12900 replaceElement : function(el, replacement, domReplace){
12901 var index = typeof el == 'number' ? el : this.indexOf(el);
12903 replacement = Roo.getDom(replacement);
12905 var d = this.elements[index];
12906 d.parentNode.insertBefore(replacement, d);
12907 d.parentNode.removeChild(d);
12909 this.elements.splice(index, 1, replacement);
12914 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12918 * Ext JS Library 1.1.1
12919 * Copyright(c) 2006-2007, Ext JS, LLC.
12921 * Originally Released Under LGPL - original licence link has changed is not relivant.
12924 * <script type="text/javascript">
12930 * @class Roo.data.Connection
12931 * @extends Roo.util.Observable
12932 * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12933 * either to a configured URL, or to a URL specified at request time.
12935 * Requests made by this class are asynchronous, and will return immediately. No data from
12936 * the server will be available to the statement immediately following the {@link #request} call.
12937 * To process returned data, use a callback in the request options object, or an event listener.
12939 * Note: If you are doing a file upload, you will not get a normal response object sent back to
12940 * your callback or event handler. Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12941 * The response object is created using the innerHTML of the IFRAME's document as the responseText
12942 * property and, if present, the IFRAME's XML document as the responseXML property.
12944 * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12945 * that it be placed either inside a <textarea> in an HTML document and retrieved from the responseText
12946 * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12947 * standard DOM methods.
12949 * @param {Object} config a configuration object.
12951 Roo.data.Connection = function(config){
12952 Roo.apply(this, config);
12955 * @event beforerequest
12956 * Fires before a network request is made to retrieve a data object.
12957 * @param {Connection} conn This Connection object.
12958 * @param {Object} options The options config object passed to the {@link #request} method.
12960 "beforerequest" : true,
12962 * @event requestcomplete
12963 * Fires if the request was successfully completed.
12964 * @param {Connection} conn This Connection object.
12965 * @param {Object} response The XHR object containing the response data.
12966 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12967 * @param {Object} options The options config object passed to the {@link #request} method.
12969 "requestcomplete" : true,
12971 * @event requestexception
12972 * Fires if an error HTTP status was returned from the server.
12973 * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12974 * @param {Connection} conn This Connection object.
12975 * @param {Object} response The XHR object containing the response data.
12976 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12977 * @param {Object} options The options config object passed to the {@link #request} method.
12979 "requestexception" : true
12981 Roo.data.Connection.superclass.constructor.call(this);
12984 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12986 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12989 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12990 * extra parameters to each request made by this object. (defaults to undefined)
12993 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12994 * to each request made by this object. (defaults to undefined)
12997 * @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)
13000 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13004 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13010 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13013 disableCaching: true,
13016 * Sends an HTTP request to a remote server.
13017 * @param {Object} options An object which may contain the following properties:<ul>
13018 * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13019 * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13020 * request, a url encoded string or a function to call to get either.</li>
13021 * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13022 * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13023 * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13024 * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13025 * <li>options {Object} The parameter to the request call.</li>
13026 * <li>success {Boolean} True if the request succeeded.</li>
13027 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13029 * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13030 * The callback is passed the following parameters:<ul>
13031 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13032 * <li>options {Object} The parameter to the request call.</li>
13034 * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13035 * The callback is passed the following parameters:<ul>
13036 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13037 * <li>options {Object} The parameter to the request call.</li>
13039 * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13040 * for the callback function. Defaults to the browser window.</li>
13041 * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13042 * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13043 * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13044 * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13045 * params for the post data. Any params will be appended to the URL.</li>
13046 * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13048 * @return {Number} transactionId
13050 request : function(o){
13051 if(this.fireEvent("beforerequest", this, o) !== false){
13054 if(typeof p == "function"){
13055 p = p.call(o.scope||window, o);
13057 if(typeof p == "object"){
13058 p = Roo.urlEncode(o.params);
13060 if(this.extraParams){
13061 var extras = Roo.urlEncode(this.extraParams);
13062 p = p ? (p + '&' + extras) : extras;
13065 var url = o.url || this.url;
13066 if(typeof url == 'function'){
13067 url = url.call(o.scope||window, o);
13071 var form = Roo.getDom(o.form);
13072 url = url || form.action;
13074 var enctype = form.getAttribute("enctype");
13077 return this.doFormDataUpload(o, url);
13080 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13081 return this.doFormUpload(o, p, url);
13083 var f = Roo.lib.Ajax.serializeForm(form);
13084 p = p ? (p + '&' + f) : f;
13087 if (!o.form && o.formData) {
13088 o.formData = o.formData === true ? new FormData() : o.formData;
13089 for (var k in o.params) {
13090 o.formData.append(k,o.params[k]);
13093 return this.doFormDataUpload(o, url);
13097 var hs = o.headers;
13098 if(this.defaultHeaders){
13099 hs = Roo.apply(hs || {}, this.defaultHeaders);
13106 success: this.handleResponse,
13107 failure: this.handleFailure,
13109 argument: {options: o},
13110 timeout : o.timeout || this.timeout
13113 var method = o.method||this.method||(p ? "POST" : "GET");
13115 if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13116 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13119 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13123 }else if(this.autoAbort !== false){
13127 if((method == 'GET' && p) || o.xmlData){
13128 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13131 Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13132 this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13133 Roo.lib.Ajax.useDefaultHeader == true;
13134 return this.transId;
13136 Roo.callback(o.callback, o.scope, [o, null, null]);
13142 * Determine whether this object has a request outstanding.
13143 * @param {Number} transactionId (Optional) defaults to the last transaction
13144 * @return {Boolean} True if there is an outstanding request.
13146 isLoading : function(transId){
13148 return Roo.lib.Ajax.isCallInProgress(transId);
13150 return this.transId ? true : false;
13155 * Aborts any outstanding request.
13156 * @param {Number} transactionId (Optional) defaults to the last transaction
13158 abort : function(transId){
13159 if(transId || this.isLoading()){
13160 Roo.lib.Ajax.abort(transId || this.transId);
13165 handleResponse : function(response){
13166 this.transId = false;
13167 var options = response.argument.options;
13168 response.argument = options ? options.argument : null;
13169 this.fireEvent("requestcomplete", this, response, options);
13170 Roo.callback(options.success, options.scope, [response, options]);
13171 Roo.callback(options.callback, options.scope, [options, true, response]);
13175 handleFailure : function(response, e){
13176 this.transId = false;
13177 var options = response.argument.options;
13178 response.argument = options ? options.argument : null;
13179 this.fireEvent("requestexception", this, response, options, e);
13180 Roo.callback(options.failure, options.scope, [response, options]);
13181 Roo.callback(options.callback, options.scope, [options, false, response]);
13185 doFormUpload : function(o, ps, url){
13187 var frame = document.createElement('iframe');
13190 frame.className = 'x-hidden';
13192 frame.src = Roo.SSL_SECURE_URL;
13194 document.body.appendChild(frame);
13197 document.frames[id].name = id;
13200 var form = Roo.getDom(o.form);
13202 form.method = 'POST';
13203 form.enctype = form.encoding = 'multipart/form-data';
13209 if(ps){ // add dynamic params
13211 ps = Roo.urlDecode(ps, false);
13213 if(ps.hasOwnProperty(k)){
13214 hd = document.createElement('input');
13215 hd.type = 'hidden';
13218 form.appendChild(hd);
13225 var r = { // bogus response object
13230 r.argument = o ? o.argument : null;
13235 doc = frame.contentWindow.document;
13237 doc = (frame.contentDocument || window.frames[id].document);
13239 if(doc && doc.body){
13240 r.responseText = doc.body.innerHTML;
13242 if(doc && doc.XMLDocument){
13243 r.responseXML = doc.XMLDocument;
13245 r.responseXML = doc;
13252 Roo.EventManager.removeListener(frame, 'load', cb, this);
13254 this.fireEvent("requestcomplete", this, r, o);
13255 Roo.callback(o.success, o.scope, [r, o]);
13256 Roo.callback(o.callback, o.scope, [o, true, r]);
13258 setTimeout(function(){document.body.removeChild(frame);}, 100);
13261 Roo.EventManager.on(frame, 'load', cb, this);
13264 if(hiddens){ // remove dynamic params
13265 for(var i = 0, len = hiddens.length; i < len; i++){
13266 form.removeChild(hiddens[i]);
13270 // this is a 'formdata version???'
13273 doFormDataUpload : function(o, url)
13277 var form = Roo.getDom(o.form);
13278 form.enctype = form.encoding = 'multipart/form-data';
13279 formData = o.formData === true ? new FormData(form) : o.formData;
13281 formData = o.formData === true ? new FormData() : o.formData;
13286 success: this.handleResponse,
13287 failure: this.handleFailure,
13289 argument: {options: o},
13290 timeout : o.timeout || this.timeout
13293 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13297 }else if(this.autoAbort !== false){
13301 //Roo.lib.Ajax.defaultPostHeader = null;
13302 Roo.lib.Ajax.useDefaultHeader = false;
13303 this.transId = Roo.lib.Ajax.request( "POST", url, cb, formData, o);
13304 Roo.lib.Ajax.useDefaultHeader = true;
13312 * Ext JS Library 1.1.1
13313 * Copyright(c) 2006-2007, Ext JS, LLC.
13315 * Originally Released Under LGPL - original licence link has changed is not relivant.
13318 * <script type="text/javascript">
13322 * Global Ajax request class.
13325 * @extends Roo.data.Connection
13328 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
13329 * @cfg {Object} extraParams An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13330 * @cfg {Object} defaultHeaders An object containing request headers which are added to each request made by this object. (defaults to undefined)
13331 * @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)
13332 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13333 * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13334 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13336 Roo.Ajax = new Roo.data.Connection({
13345 * Serialize the passed form into a url encoded string
13347 * @param {String/HTMLElement} form
13350 serializeForm : function(form){
13351 return Roo.lib.Ajax.serializeForm(form);
13355 * Ext JS Library 1.1.1
13356 * Copyright(c) 2006-2007, Ext JS, LLC.
13358 * Originally Released Under LGPL - original licence link has changed is not relivant.
13361 * <script type="text/javascript">
13366 * @class Roo.UpdateManager
13367 * @extends Roo.util.Observable
13368 * Provides AJAX-style update for Element object.<br><br>
13371 * // Get it from a Roo.Element object
13372 * var el = Roo.get("foo");
13373 * var mgr = el.getUpdateManager();
13374 * mgr.update("http://myserver.com/index.php", "param1=1&param2=2");
13376 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13378 * // or directly (returns the same UpdateManager instance)
13379 * var mgr = new Roo.UpdateManager("myElementId");
13380 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13381 * mgr.on("update", myFcnNeedsToKnow);
13383 // short handed call directly from the element object
13384 Roo.get("foo").load({
13388 text: "Loading Foo..."
13392 * Create new UpdateManager directly.
13393 * @param {String/HTMLElement/Roo.Element} el The element to update
13394 * @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).
13396 Roo.UpdateManager = function(el, forceNew){
13398 if(!forceNew && el.updateManager){
13399 return el.updateManager;
13402 * The Element object
13403 * @type Roo.Element
13407 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13410 this.defaultUrl = null;
13414 * @event beforeupdate
13415 * Fired before an update is made, return false from your handler and the update is cancelled.
13416 * @param {Roo.Element} el
13417 * @param {String/Object/Function} url
13418 * @param {String/Object} params
13420 "beforeupdate": true,
13423 * Fired after successful update is made.
13424 * @param {Roo.Element} el
13425 * @param {Object} oResponseObject The response Object
13430 * Fired on update failure.
13431 * @param {Roo.Element} el
13432 * @param {Object} oResponseObject The response Object
13436 var d = Roo.UpdateManager.defaults;
13438 * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13441 this.sslBlankUrl = d.sslBlankUrl;
13443 * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13446 this.disableCaching = d.disableCaching;
13448 * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '<div class="loading-indicator">Loading...</div>').
13451 this.indicatorText = d.indicatorText;
13453 * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13456 this.showLoadIndicator = d.showLoadIndicator;
13458 * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13461 this.timeout = d.timeout;
13464 * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13467 this.loadScripts = d.loadScripts;
13470 * Transaction object of current executing transaction
13472 this.transaction = null;
13477 this.autoRefreshProcId = null;
13479 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13482 this.refreshDelegate = this.refresh.createDelegate(this);
13484 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13487 this.updateDelegate = this.update.createDelegate(this);
13489 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13492 this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13496 this.successDelegate = this.processSuccess.createDelegate(this);
13500 this.failureDelegate = this.processFailure.createDelegate(this);
13502 if(!this.renderer){
13504 * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13506 this.renderer = new Roo.UpdateManager.BasicRenderer();
13509 Roo.UpdateManager.superclass.constructor.call(this);
13512 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13514 * Get the Element this UpdateManager is bound to
13515 * @return {Roo.Element} The element
13517 getEl : function(){
13521 * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13522 * @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:
13525 url: "your-url.php",<br/>
13526 params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13527 callback: yourFunction,<br/>
13528 scope: yourObject, //(optional scope) <br/>
13529 discardUrl: false, <br/>
13530 nocache: false,<br/>
13531 text: "Loading...",<br/>
13533 scripts: false<br/>
13536 * The only required property is url. The optional properties nocache, text and scripts
13537 * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13538 * @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}
13539 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13540 * @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.
13542 update : function(url, params, callback, discardUrl){
13543 if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13544 var method = this.method,
13546 if(typeof url == "object"){ // must be config object
13549 params = params || cfg.params;
13550 callback = callback || cfg.callback;
13551 discardUrl = discardUrl || cfg.discardUrl;
13552 if(callback && cfg.scope){
13553 callback = callback.createDelegate(cfg.scope);
13555 if(typeof cfg.method != "undefined"){method = cfg.method;};
13556 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13557 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13558 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13559 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13561 this.showLoading();
13563 this.defaultUrl = url;
13565 if(typeof url == "function"){
13566 url = url.call(this);
13569 method = method || (params ? "POST" : "GET");
13570 if(method == "GET"){
13571 url = this.prepareUrl(url);
13574 var o = Roo.apply(cfg ||{}, {
13577 success: this.successDelegate,
13578 failure: this.failureDelegate,
13579 callback: undefined,
13580 timeout: (this.timeout*1000),
13581 argument: {"url": url, "form": null, "callback": callback, "params": params}
13583 Roo.log("updated manager called with timeout of " + o.timeout);
13584 this.transaction = Roo.Ajax.request(o);
13589 * 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.
13590 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13591 * @param {String/HTMLElement} form The form Id or form element
13592 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13593 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13594 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13596 formUpdate : function(form, url, reset, callback){
13597 if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13598 if(typeof url == "function"){
13599 url = url.call(this);
13601 form = Roo.getDom(form);
13602 this.transaction = Roo.Ajax.request({
13605 success: this.successDelegate,
13606 failure: this.failureDelegate,
13607 timeout: (this.timeout*1000),
13608 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13610 this.showLoading.defer(1, this);
13615 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13616 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13618 refresh : function(callback){
13619 if(this.defaultUrl == null){
13622 this.update(this.defaultUrl, null, callback, true);
13626 * Set this element to auto refresh.
13627 * @param {Number} interval How often to update (in seconds).
13628 * @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)
13629 * @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}
13630 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13631 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13633 startAutoRefresh : function(interval, url, params, callback, refreshNow){
13635 this.update(url || this.defaultUrl, params, callback, true);
13637 if(this.autoRefreshProcId){
13638 clearInterval(this.autoRefreshProcId);
13640 this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13644 * Stop auto refresh on this element.
13646 stopAutoRefresh : function(){
13647 if(this.autoRefreshProcId){
13648 clearInterval(this.autoRefreshProcId);
13649 delete this.autoRefreshProcId;
13653 isAutoRefreshing : function(){
13654 return this.autoRefreshProcId ? true : false;
13657 * Called to update the element to "Loading" state. Override to perform custom action.
13659 showLoading : function(){
13660 if(this.showLoadIndicator){
13661 this.el.update(this.indicatorText);
13666 * Adds unique parameter to query string if disableCaching = true
13669 prepareUrl : function(url){
13670 if(this.disableCaching){
13671 var append = "_dc=" + (new Date().getTime());
13672 if(url.indexOf("?") !== -1){
13673 url += "&" + append;
13675 url += "?" + append;
13684 processSuccess : function(response){
13685 this.transaction = null;
13686 if(response.argument.form && response.argument.reset){
13687 try{ // put in try/catch since some older FF releases had problems with this
13688 response.argument.form.reset();
13691 if(this.loadScripts){
13692 this.renderer.render(this.el, response, this,
13693 this.updateComplete.createDelegate(this, [response]));
13695 this.renderer.render(this.el, response, this);
13696 this.updateComplete(response);
13700 updateComplete : function(response){
13701 this.fireEvent("update", this.el, response);
13702 if(typeof response.argument.callback == "function"){
13703 response.argument.callback(this.el, true, response);
13710 processFailure : function(response){
13711 this.transaction = null;
13712 this.fireEvent("failure", this.el, response);
13713 if(typeof response.argument.callback == "function"){
13714 response.argument.callback(this.el, false, response);
13719 * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13720 * @param {Object} renderer The object implementing the render() method
13722 setRenderer : function(renderer){
13723 this.renderer = renderer;
13726 getRenderer : function(){
13727 return this.renderer;
13731 * Set the defaultUrl used for updates
13732 * @param {String/Function} defaultUrl The url or a function to call to get the url
13734 setDefaultUrl : function(defaultUrl){
13735 this.defaultUrl = defaultUrl;
13739 * Aborts the executing transaction
13741 abort : function(){
13742 if(this.transaction){
13743 Roo.Ajax.abort(this.transaction);
13748 * Returns true if an update is in progress
13749 * @return {Boolean}
13751 isUpdating : function(){
13752 if(this.transaction){
13753 return Roo.Ajax.isLoading(this.transaction);
13760 * @class Roo.UpdateManager.defaults
13761 * @static (not really - but it helps the doc tool)
13762 * The defaults collection enables customizing the default properties of UpdateManager
13764 Roo.UpdateManager.defaults = {
13766 * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13772 * True to process scripts by default (Defaults to false).
13775 loadScripts : false,
13778 * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13781 sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13783 * Whether to append unique parameter on get request to disable caching (Defaults to false).
13786 disableCaching : false,
13788 * Whether to show indicatorText when loading (Defaults to true).
13791 showLoadIndicator : true,
13793 * Text for loading indicator (Defaults to '<div class="loading-indicator">Loading...</div>').
13796 indicatorText : '<div class="loading-indicator">Loading...</div>'
13800 * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13802 * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13803 * @param {String/HTMLElement/Roo.Element} el The element to update
13804 * @param {String} url The url
13805 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13806 * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13809 * @member Roo.UpdateManager
13811 Roo.UpdateManager.updateElement = function(el, url, params, options){
13812 var um = Roo.get(el, true).getUpdateManager();
13813 Roo.apply(um, options);
13814 um.update(url, params, options ? options.callback : null);
13816 // alias for backwards compat
13817 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13819 * @class Roo.UpdateManager.BasicRenderer
13820 * Default Content renderer. Updates the elements innerHTML with the responseText.
13822 Roo.UpdateManager.BasicRenderer = function(){};
13824 Roo.UpdateManager.BasicRenderer.prototype = {
13826 * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13827 * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13828 * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13829 * @param {Roo.Element} el The element being rendered
13830 * @param {Object} response The YUI Connect response object
13831 * @param {UpdateManager} updateManager The calling update manager
13832 * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13834 render : function(el, response, updateManager, callback){
13835 el.update(response.responseText, updateManager.loadScripts, callback);
13841 * (c)) Alan Knowles
13847 * @class Roo.DomTemplate
13848 * @extends Roo.Template
13849 * An effort at a dom based template engine..
13851 * Similar to XTemplate, except it uses dom parsing to create the template..
13853 * Supported features:
13858 {a_variable} - output encoded.
13859 {a_variable.format:("Y-m-d")} - call a method on the variable
13860 {a_variable:raw} - unencoded output
13861 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13862 {a_variable:this.method_on_template(...)} - call a method on the template object.
13867 <div roo-for="a_variable or condition.."></div>
13868 <div roo-if="a_variable or condition"></div>
13869 <div roo-exec="some javascript"></div>
13870 <div roo-name="named_template"></div>
13875 Roo.DomTemplate = function()
13877 Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13884 Roo.extend(Roo.DomTemplate, Roo.Template, {
13886 * id counter for sub templates.
13890 * flag to indicate if dom parser is inside a pre,
13891 * it will strip whitespace if not.
13896 * The various sub templates
13904 * basic tag replacing syntax
13907 * // you can fake an object call by doing this
13911 re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13912 //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13914 iterChild : function (node, method) {
13916 var oldPre = this.inPre;
13917 if (node.tagName == 'PRE') {
13920 for( var i = 0; i < node.childNodes.length; i++) {
13921 method.call(this, node.childNodes[i]);
13923 this.inPre = oldPre;
13929 * compile the template
13931 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13934 compile: function()
13938 // covert the html into DOM...
13942 doc = document.implementation.createHTMLDocument("");
13943 doc.documentElement.innerHTML = this.html ;
13944 div = doc.documentElement;
13946 // old IE... - nasty -- it causes all sorts of issues.. with
13947 // images getting pulled from server..
13948 div = document.createElement('div');
13949 div.innerHTML = this.html;
13951 //doc.documentElement.innerHTML = htmlBody
13957 this.iterChild(div, function(n) {_t.compileNode(n, true); });
13959 var tpls = this.tpls;
13961 // create a top level template from the snippet..
13963 //Roo.log(div.innerHTML);
13970 body : div.innerHTML,
13983 Roo.each(tpls, function(tp){
13984 this.compileTpl(tp);
13985 this.tpls[tp.id] = tp;
13988 this.master = tpls[0];
13994 compileNode : function(node, istop) {
13999 // skip anything not a tag..
14000 if (node.nodeType != 1) {
14001 if (node.nodeType == 3 && !this.inPre) {
14002 // reduce white space..
14003 node.nodeValue = node.nodeValue.replace(/\s+/g, ' ');
14026 case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14027 case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14028 case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14029 case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14035 // just itterate children..
14036 this.iterChild(node,this.compileNode);
14039 tpl.uid = this.id++;
14040 tpl.value = node.getAttribute('roo-' + tpl.attr);
14041 node.removeAttribute('roo-'+ tpl.attr);
14042 if (tpl.attr != 'name') {
14043 var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14044 node.parentNode.replaceChild(placeholder, node);
14047 var placeholder = document.createElement('span');
14048 placeholder.className = 'roo-tpl-' + tpl.value;
14049 node.parentNode.replaceChild(placeholder, node);
14052 // parent now sees '{domtplXXXX}
14053 this.iterChild(node,this.compileNode);
14055 // we should now have node body...
14056 var div = document.createElement('div');
14057 div.appendChild(node);
14059 // this has the unfortunate side effect of converting tagged attributes
14060 // eg. href="{...}" into %7C...%7D
14061 // this has been fixed by searching for those combo's although it's a bit hacky..
14064 tpl.body = div.innerHTML;
14071 switch (tpl.value) {
14072 case '.': tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14073 case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14074 default: tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14079 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14083 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14087 tpl.id = tpl.value; // replace non characters???
14093 this.tpls.push(tpl);
14103 * Compile a segment of the template into a 'sub-template'
14109 compileTpl : function(tpl)
14111 var fm = Roo.util.Format;
14112 var useF = this.disableFormats !== true;
14114 var sep = Roo.isGecko ? "+\n" : ",\n";
14116 var undef = function(str) {
14117 Roo.debug && Roo.log("Property not found :" + str);
14121 //Roo.log(tpl.body);
14125 var fn = function(m, lbrace, name, format, args)
14128 //Roo.log(arguments);
14129 args = args ? args.replace(/\\'/g,"'") : args;
14130 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14131 if (typeof(format) == 'undefined') {
14132 format = 'htmlEncode';
14134 if (format == 'raw' ) {
14138 if(name.substr(0, 6) == 'domtpl'){
14139 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14142 // build an array of options to determine if value is undefined..
14144 // basically get 'xxxx.yyyy' then do
14145 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14146 // (function () { Roo.log("Property not found"); return ''; })() :
14151 Roo.each(name.split('.'), function(st) {
14152 lookfor += (lookfor.length ? '.': '') + st;
14153 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
14156 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14159 if(format && useF){
14161 args = args ? ',' + args : "";
14163 if(format.substr(0, 5) != "this."){
14164 format = "fm." + format + '(';
14166 format = 'this.call("'+ format.substr(5) + '", ';
14170 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
14173 if (args && args.length) {
14174 // called with xxyx.yuu:(test,test)
14176 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
14178 // raw.. - :raw modifier..
14179 return "'"+ sep + udef_st + name + ")"+sep+"'";
14183 // branched to use + in gecko and [].join() in others
14185 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
14186 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14189 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
14190 body.push(tpl.body.replace(/(\r\n|\n)/g,
14191 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14192 body.push("'].join('');};};");
14193 body = body.join('');
14196 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14198 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
14205 * same as applyTemplate, except it's done to one of the subTemplates
14206 * when using named templates, you can do:
14208 * var str = pl.applySubTemplate('your-name', values);
14211 * @param {Number} id of the template
14212 * @param {Object} values to apply to template
14213 * @param {Object} parent (normaly the instance of this object)
14215 applySubTemplate : function(id, values, parent)
14219 var t = this.tpls[id];
14223 if(t.ifCall && !t.ifCall.call(this, values, parent)){
14224 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14228 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14235 if(t.execCall && t.execCall.call(this, values, parent)){
14239 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14245 var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14246 parent = t.target ? values : parent;
14247 if(t.forCall && vs instanceof Array){
14249 for(var i = 0, len = vs.length; i < len; i++){
14251 buf[buf.length] = t.compiled.call(this, vs[i], parent);
14253 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14255 //Roo.log(t.compiled);
14259 return buf.join('');
14262 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14267 return t.compiled.call(this, vs, parent);
14269 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14271 //Roo.log(t.compiled);
14279 applyTemplate : function(values){
14280 return this.master.compiled.call(this, values, {});
14281 //var s = this.subs;
14284 apply : function(){
14285 return this.applyTemplate.apply(this, arguments);
14290 Roo.DomTemplate.from = function(el){
14291 el = Roo.getDom(el);
14292 return new Roo.Domtemplate(el.value || el.innerHTML);
14295 * Ext JS Library 1.1.1
14296 * Copyright(c) 2006-2007, Ext JS, LLC.
14298 * Originally Released Under LGPL - original licence link has changed is not relivant.
14301 * <script type="text/javascript">
14305 * @class Roo.util.DelayedTask
14306 * Provides a convenient method of performing setTimeout where a new
14307 * timeout cancels the old timeout. An example would be performing validation on a keypress.
14308 * You can use this class to buffer
14309 * the keypress events for a certain number of milliseconds, and perform only if they stop
14310 * for that amount of time.
14311 * @constructor The parameters to this constructor serve as defaults and are not required.
14312 * @param {Function} fn (optional) The default function to timeout
14313 * @param {Object} scope (optional) The default scope of that timeout
14314 * @param {Array} args (optional) The default Array of arguments
14316 Roo.util.DelayedTask = function(fn, scope, args){
14317 var id = null, d, t;
14319 var call = function(){
14320 var now = new Date().getTime();
14324 fn.apply(scope, args || []);
14328 * Cancels any pending timeout and queues a new one
14329 * @param {Number} delay The milliseconds to delay
14330 * @param {Function} newFn (optional) Overrides function passed to constructor
14331 * @param {Object} newScope (optional) Overrides scope passed to constructor
14332 * @param {Array} newArgs (optional) Overrides args passed to constructor
14334 this.delay = function(delay, newFn, newScope, newArgs){
14335 if(id && delay != d){
14339 t = new Date().getTime();
14341 scope = newScope || scope;
14342 args = newArgs || args;
14344 id = setInterval(call, d);
14349 * Cancel the last queued timeout
14351 this.cancel = function(){
14359 * Ext JS Library 1.1.1
14360 * Copyright(c) 2006-2007, Ext JS, LLC.
14362 * Originally Released Under LGPL - original licence link has changed is not relivant.
14365 * <script type="text/javascript">
14368 * @class Roo.util.TaskRunner
14369 * Manage background tasks - not sure why this is better that setInterval?
14374 Roo.util.TaskRunner = function(interval){
14375 interval = interval || 10;
14376 var tasks = [], removeQueue = [];
14378 var running = false;
14380 var stopThread = function(){
14386 var startThread = function(){
14389 id = setInterval(runTasks, interval);
14393 var removeTask = function(task){
14394 removeQueue.push(task);
14400 var runTasks = function(){
14401 if(removeQueue.length > 0){
14402 for(var i = 0, len = removeQueue.length; i < len; i++){
14403 tasks.remove(removeQueue[i]);
14406 if(tasks.length < 1){
14411 var now = new Date().getTime();
14412 for(var i = 0, len = tasks.length; i < len; ++i){
14414 var itime = now - t.taskRunTime;
14415 if(t.interval <= itime){
14416 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14417 t.taskRunTime = now;
14418 if(rt === false || t.taskRunCount === t.repeat){
14423 if(t.duration && t.duration <= (now - t.taskStartTime)){
14430 * Queues a new task.
14431 * @param {Object} task
14433 * Task property : interval = how frequent to run.
14434 * Task object should implement
14436 * Task object may implement
14437 * function onStop()
14439 this.start = function(task){
14441 task.taskStartTime = new Date().getTime();
14442 task.taskRunTime = 0;
14443 task.taskRunCount = 0;
14449 * @param {Object} task
14451 this.stop = function(task){
14458 this.stopAll = function(){
14460 for(var i = 0, len = tasks.length; i < len; i++){
14461 if(tasks[i].onStop){
14470 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14472 * Ext JS Library 1.1.1
14473 * Copyright(c) 2006-2007, Ext JS, LLC.
14475 * Originally Released Under LGPL - original licence link has changed is not relivant.
14478 * <script type="text/javascript">
14483 * @class Roo.util.MixedCollection
14484 * @extends Roo.util.Observable
14485 * A Collection class that maintains both numeric indexes and keys and exposes events.
14487 * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14488 * collection (defaults to false)
14489 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14490 * and return the key value for that item. This is used when available to look up the key on items that
14491 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
14492 * equivalent to providing an implementation for the {@link #getKey} method.
14494 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14502 * Fires when the collection is cleared.
14507 * Fires when an item is added to the collection.
14508 * @param {Number} index The index at which the item was added.
14509 * @param {Object} o The item added.
14510 * @param {String} key The key associated with the added item.
14515 * Fires when an item is replaced in the collection.
14516 * @param {String} key he key associated with the new added.
14517 * @param {Object} old The item being replaced.
14518 * @param {Object} new The new item.
14523 * Fires when an item is removed from the collection.
14524 * @param {Object} o The item being removed.
14525 * @param {String} key (optional) The key associated with the removed item.
14530 this.allowFunctions = allowFunctions === true;
14532 this.getKey = keyFn;
14534 Roo.util.MixedCollection.superclass.constructor.call(this);
14537 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14538 allowFunctions : false,
14541 * Adds an item to the collection.
14542 * @param {String} key The key to associate with the item
14543 * @param {Object} o The item to add.
14544 * @return {Object} The item added.
14546 add : function(key, o){
14547 if(arguments.length == 1){
14549 key = this.getKey(o);
14551 if(typeof key == "undefined" || key === null){
14553 this.items.push(o);
14554 this.keys.push(null);
14556 var old = this.map[key];
14558 return this.replace(key, o);
14561 this.items.push(o);
14563 this.keys.push(key);
14565 this.fireEvent("add", this.length-1, o, key);
14570 * MixedCollection has a generic way to fetch keys if you implement getKey.
14573 var mc = new Roo.util.MixedCollection();
14574 mc.add(someEl.dom.id, someEl);
14575 mc.add(otherEl.dom.id, otherEl);
14579 var mc = new Roo.util.MixedCollection();
14580 mc.getKey = function(el){
14586 // or via the constructor
14587 var mc = new Roo.util.MixedCollection(false, function(el){
14593 * @param o {Object} The item for which to find the key.
14594 * @return {Object} The key for the passed item.
14596 getKey : function(o){
14601 * Replaces an item in the collection.
14602 * @param {String} key The key associated with the item to replace, or the item to replace.
14603 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14604 * @return {Object} The new item.
14606 replace : function(key, o){
14607 if(arguments.length == 1){
14609 key = this.getKey(o);
14611 var old = this.item(key);
14612 if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14613 return this.add(key, o);
14615 var index = this.indexOfKey(key);
14616 this.items[index] = o;
14618 this.fireEvent("replace", key, old, o);
14623 * Adds all elements of an Array or an Object to the collection.
14624 * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14625 * an Array of values, each of which are added to the collection.
14627 addAll : function(objs){
14628 if(arguments.length > 1 || objs instanceof Array){
14629 var args = arguments.length > 1 ? arguments : objs;
14630 for(var i = 0, len = args.length; i < len; i++){
14634 for(var key in objs){
14635 if(this.allowFunctions || typeof objs[key] != "function"){
14636 this.add(key, objs[key]);
14643 * Executes the specified function once for every item in the collection, passing each
14644 * item as the first and only parameter. returning false from the function will stop the iteration.
14645 * @param {Function} fn The function to execute for each item.
14646 * @param {Object} scope (optional) The scope in which to execute the function.
14648 each : function(fn, scope){
14649 var items = [].concat(this.items); // each safe for removal
14650 for(var i = 0, len = items.length; i < len; i++){
14651 if(fn.call(scope || items[i], items[i], i, len) === false){
14658 * Executes the specified function once for every key in the collection, passing each
14659 * key, and its associated item as the first two parameters.
14660 * @param {Function} fn The function to execute for each item.
14661 * @param {Object} scope (optional) The scope in which to execute the function.
14663 eachKey : function(fn, scope){
14664 for(var i = 0, len = this.keys.length; i < len; i++){
14665 fn.call(scope || window, this.keys[i], this.items[i], i, len);
14670 * Returns the first item in the collection which elicits a true return value from the
14671 * passed selection function.
14672 * @param {Function} fn The selection function to execute for each item.
14673 * @param {Object} scope (optional) The scope in which to execute the function.
14674 * @return {Object} The first item in the collection which returned true from the selection function.
14676 find : function(fn, scope){
14677 for(var i = 0, len = this.items.length; i < len; i++){
14678 if(fn.call(scope || window, this.items[i], this.keys[i])){
14679 return this.items[i];
14686 * Inserts an item at the specified index in the collection.
14687 * @param {Number} index The index to insert the item at.
14688 * @param {String} key The key to associate with the new item, or the item itself.
14689 * @param {Object} o (optional) If the second parameter was a key, the new item.
14690 * @return {Object} The item inserted.
14692 insert : function(index, key, o){
14693 if(arguments.length == 2){
14695 key = this.getKey(o);
14697 if(index >= this.length){
14698 return this.add(key, o);
14701 this.items.splice(index, 0, o);
14702 if(typeof key != "undefined" && key != null){
14705 this.keys.splice(index, 0, key);
14706 this.fireEvent("add", index, o, key);
14711 * Removed an item from the collection.
14712 * @param {Object} o The item to remove.
14713 * @return {Object} The item removed.
14715 remove : function(o){
14716 return this.removeAt(this.indexOf(o));
14720 * Remove an item from a specified index in the collection.
14721 * @param {Number} index The index within the collection of the item to remove.
14723 removeAt : function(index){
14724 if(index < this.length && index >= 0){
14726 var o = this.items[index];
14727 this.items.splice(index, 1);
14728 var key = this.keys[index];
14729 if(typeof key != "undefined"){
14730 delete this.map[key];
14732 this.keys.splice(index, 1);
14733 this.fireEvent("remove", o, key);
14738 * Removed an item associated with the passed key fom the collection.
14739 * @param {String} key The key of the item to remove.
14741 removeKey : function(key){
14742 return this.removeAt(this.indexOfKey(key));
14746 * Returns the number of items in the collection.
14747 * @return {Number} the number of items in the collection.
14749 getCount : function(){
14750 return this.length;
14754 * Returns index within the collection of the passed Object.
14755 * @param {Object} o The item to find the index of.
14756 * @return {Number} index of the item.
14758 indexOf : function(o){
14759 if(!this.items.indexOf){
14760 for(var i = 0, len = this.items.length; i < len; i++){
14761 if(this.items[i] == o) {
14767 return this.items.indexOf(o);
14772 * Returns index within the collection of the passed key.
14773 * @param {String} key The key to find the index of.
14774 * @return {Number} index of the key.
14776 indexOfKey : function(key){
14777 if(!this.keys.indexOf){
14778 for(var i = 0, len = this.keys.length; i < len; i++){
14779 if(this.keys[i] == key) {
14785 return this.keys.indexOf(key);
14790 * Returns the item associated with the passed key OR index. Key has priority over index.
14791 * @param {String/Number} key The key or index of the item.
14792 * @return {Object} The item associated with the passed key.
14794 item : function(key){
14795 if (key === 'length') {
14798 var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14799 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14803 * Returns the item at the specified index.
14804 * @param {Number} index The index of the item.
14807 itemAt : function(index){
14808 return this.items[index];
14812 * Returns the item associated with the passed key.
14813 * @param {String/Number} key The key of the item.
14814 * @return {Object} The item associated with the passed key.
14816 key : function(key){
14817 return this.map[key];
14821 * Returns true if the collection contains the passed Object as an item.
14822 * @param {Object} o The Object to look for in the collection.
14823 * @return {Boolean} True if the collection contains the Object as an item.
14825 contains : function(o){
14826 return this.indexOf(o) != -1;
14830 * Returns true if the collection contains the passed Object as a key.
14831 * @param {String} key The key to look for in the collection.
14832 * @return {Boolean} True if the collection contains the Object as a key.
14834 containsKey : function(key){
14835 return typeof this.map[key] != "undefined";
14839 * Removes all items from the collection.
14841 clear : function(){
14846 this.fireEvent("clear");
14850 * Returns the first item in the collection.
14851 * @return {Object} the first item in the collection..
14853 first : function(){
14854 return this.items[0];
14858 * Returns the last item in the collection.
14859 * @return {Object} the last item in the collection..
14862 return this.items[this.length-1];
14865 _sort : function(property, dir, fn){
14866 var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14867 fn = fn || function(a, b){
14870 var c = [], k = this.keys, items = this.items;
14871 for(var i = 0, len = items.length; i < len; i++){
14872 c[c.length] = {key: k[i], value: items[i], index: i};
14874 c.sort(function(a, b){
14875 var v = fn(a[property], b[property]) * dsc;
14877 v = (a.index < b.index ? -1 : 1);
14881 for(var i = 0, len = c.length; i < len; i++){
14882 items[i] = c[i].value;
14885 this.fireEvent("sort", this);
14889 * Sorts this collection with the passed comparison function
14890 * @param {String} direction (optional) "ASC" or "DESC"
14891 * @param {Function} fn (optional) comparison function
14893 sort : function(dir, fn){
14894 this._sort("value", dir, fn);
14898 * Sorts this collection by keys
14899 * @param {String} direction (optional) "ASC" or "DESC"
14900 * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14902 keySort : function(dir, fn){
14903 this._sort("key", dir, fn || function(a, b){
14904 return String(a).toUpperCase()-String(b).toUpperCase();
14909 * Returns a range of items in this collection
14910 * @param {Number} startIndex (optional) defaults to 0
14911 * @param {Number} endIndex (optional) default to the last item
14912 * @return {Array} An array of items
14914 getRange : function(start, end){
14915 var items = this.items;
14916 if(items.length < 1){
14919 start = start || 0;
14920 end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14923 for(var i = start; i <= end; i++) {
14924 r[r.length] = items[i];
14927 for(var i = start; i >= end; i--) {
14928 r[r.length] = items[i];
14935 * Filter the <i>objects</i> in this collection by a specific property.
14936 * Returns a new collection that has been filtered.
14937 * @param {String} property A property on your objects
14938 * @param {String/RegExp} value Either string that the property values
14939 * should start with or a RegExp to test against the property
14940 * @return {MixedCollection} The new filtered collection
14942 filter : function(property, value){
14943 if(!value.exec){ // not a regex
14944 value = String(value);
14945 if(value.length == 0){
14946 return this.clone();
14948 value = new RegExp("^" + Roo.escapeRe(value), "i");
14950 return this.filterBy(function(o){
14951 return o && value.test(o[property]);
14956 * Filter by a function. * Returns a new collection that has been filtered.
14957 * The passed function will be called with each
14958 * object in the collection. If the function returns true, the value is included
14959 * otherwise it is filtered.
14960 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14961 * @param {Object} scope (optional) The scope of the function (defaults to this)
14962 * @return {MixedCollection} The new filtered collection
14964 filterBy : function(fn, scope){
14965 var r = new Roo.util.MixedCollection();
14966 r.getKey = this.getKey;
14967 var k = this.keys, it = this.items;
14968 for(var i = 0, len = it.length; i < len; i++){
14969 if(fn.call(scope||this, it[i], k[i])){
14970 r.add(k[i], it[i]);
14977 * Creates a duplicate of this collection
14978 * @return {MixedCollection}
14980 clone : function(){
14981 var r = new Roo.util.MixedCollection();
14982 var k = this.keys, it = this.items;
14983 for(var i = 0, len = it.length; i < len; i++){
14984 r.add(k[i], it[i]);
14986 r.getKey = this.getKey;
14991 * Returns the item associated with the passed key or index.
14993 * @param {String/Number} key The key or index of the item.
14994 * @return {Object} The item associated with the passed key.
14996 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14998 * Ext JS Library 1.1.1
14999 * Copyright(c) 2006-2007, Ext JS, LLC.
15001 * Originally Released Under LGPL - original licence link has changed is not relivant.
15004 * <script type="text/javascript">
15007 * @class Roo.util.JSON
15008 * Modified version of Douglas Crockford"s json.js that doesn"t
15009 * mess with the Object prototype
15010 * http://www.json.org/js.html
15013 Roo.util.JSON = new (function(){
15014 var useHasOwn = {}.hasOwnProperty ? true : false;
15016 // crashes Safari in some instances
15017 //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15019 var pad = function(n) {
15020 return n < 10 ? "0" + n : n;
15033 var encodeString = function(s){
15034 if (/["\\\x00-\x1f]/.test(s)) {
15035 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15040 c = b.charCodeAt();
15042 Math.floor(c / 16).toString(16) +
15043 (c % 16).toString(16);
15046 return '"' + s + '"';
15049 var encodeArray = function(o){
15050 var a = ["["], b, i, l = o.length, v;
15051 for (i = 0; i < l; i += 1) {
15053 switch (typeof v) {
15062 a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15070 var encodeDate = function(o){
15071 return '"' + o.getFullYear() + "-" +
15072 pad(o.getMonth() + 1) + "-" +
15073 pad(o.getDate()) + "T" +
15074 pad(o.getHours()) + ":" +
15075 pad(o.getMinutes()) + ":" +
15076 pad(o.getSeconds()) + '"';
15080 * Encodes an Object, Array or other value
15081 * @param {Mixed} o The variable to encode
15082 * @return {String} The JSON string
15084 this.encode = function(o)
15086 // should this be extended to fully wrap stringify..
15088 if(typeof o == "undefined" || o === null){
15090 }else if(o instanceof Array){
15091 return encodeArray(o);
15092 }else if(o instanceof Date){
15093 return encodeDate(o);
15094 }else if(typeof o == "string"){
15095 return encodeString(o);
15096 }else if(typeof o == "number"){
15097 return isFinite(o) ? String(o) : "null";
15098 }else if(typeof o == "boolean"){
15101 var a = ["{"], b, i, v;
15103 if(!useHasOwn || o.hasOwnProperty(i)) {
15105 switch (typeof v) {
15114 a.push(this.encode(i), ":",
15115 v === null ? "null" : this.encode(v));
15126 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15127 * @param {String} json The JSON string
15128 * @return {Object} The resulting object
15130 this.decode = function(json){
15132 return /** eval:var:json */ eval("(" + json + ')');
15136 * Shorthand for {@link Roo.util.JSON#encode}
15137 * @member Roo encode
15139 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15141 * Shorthand for {@link Roo.util.JSON#decode}
15142 * @member Roo decode
15144 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15147 * Ext JS Library 1.1.1
15148 * Copyright(c) 2006-2007, Ext JS, LLC.
15150 * Originally Released Under LGPL - original licence link has changed is not relivant.
15153 * <script type="text/javascript">
15157 * @class Roo.util.Format
15158 * Reusable data formatting functions
15161 Roo.util.Format = function(){
15162 var trimRe = /^\s+|\s+$/g;
15165 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15166 * @param {String} value The string to truncate
15167 * @param {Number} length The maximum length to allow before truncating
15168 * @return {String} The converted text
15170 ellipsis : function(value, len){
15171 if(value && value.length > len){
15172 return value.substr(0, len-3)+"...";
15178 * Checks a reference and converts it to empty string if it is undefined
15179 * @param {Mixed} value Reference to check
15180 * @return {Mixed} Empty string if converted, otherwise the original value
15182 undef : function(value){
15183 return typeof value != "undefined" ? value : "";
15187 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15188 * @param {String} value The string to encode
15189 * @return {String} The encoded text
15191 htmlEncode : function(value){
15192 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
15196 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15197 * @param {String} value The string to decode
15198 * @return {String} The decoded text
15200 htmlDecode : function(value){
15201 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"');
15205 * Trims any whitespace from either side of a string
15206 * @param {String} value The text to trim
15207 * @return {String} The trimmed text
15209 trim : function(value){
15210 return String(value).replace(trimRe, "");
15214 * Returns a substring from within an original string
15215 * @param {String} value The original text
15216 * @param {Number} start The start index of the substring
15217 * @param {Number} length The length of the substring
15218 * @return {String} The substring
15220 substr : function(value, start, length){
15221 return String(value).substr(start, length);
15225 * Converts a string to all lower case letters
15226 * @param {String} value The text to convert
15227 * @return {String} The converted text
15229 lowercase : function(value){
15230 return String(value).toLowerCase();
15234 * Converts a string to all upper case letters
15235 * @param {String} value The text to convert
15236 * @return {String} The converted text
15238 uppercase : function(value){
15239 return String(value).toUpperCase();
15243 * Converts the first character only of a string to upper case
15244 * @param {String} value The text to convert
15245 * @return {String} The converted text
15247 capitalize : function(value){
15248 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15252 call : function(value, fn){
15253 if(arguments.length > 2){
15254 var args = Array.prototype.slice.call(arguments, 2);
15255 args.unshift(value);
15257 return /** eval:var:value */ eval(fn).apply(window, args);
15259 /** eval:var:value */
15260 return /** eval:var:value */ eval(fn).call(window, value);
15266 * safer version of Math.toFixed..??/
15267 * @param {Number/String} value The numeric value to format
15268 * @param {Number/String} value Decimal places
15269 * @return {String} The formatted currency string
15271 toFixed : function(v, n)
15273 // why not use to fixed - precision is buggered???
15275 return Math.round(v-0);
15277 var fact = Math.pow(10,n+1);
15278 v = (Math.round((v-0)*fact))/fact;
15279 var z = (''+fact).substring(2);
15280 if (v == Math.floor(v)) {
15281 return Math.floor(v) + '.' + z;
15284 // now just padd decimals..
15285 var ps = String(v).split('.');
15286 var fd = (ps[1] + z);
15287 var r = fd.substring(0,n);
15288 var rm = fd.substring(n);
15290 return ps[0] + '.' + r;
15292 r*=1; // turn it into a number;
15294 if (String(r).length != n) {
15297 r = String(r).substring(1); // chop the end off.
15300 return ps[0] + '.' + r;
15305 * Format a number as US currency
15306 * @param {Number/String} value The numeric value to format
15307 * @return {String} The formatted currency string
15309 usMoney : function(v){
15310 return '$' + Roo.util.Format.number(v);
15315 * eventually this should probably emulate php's number_format
15316 * @param {Number/String} value The numeric value to format
15317 * @param {Number} decimals number of decimal places
15318 * @param {String} delimiter for thousands (default comma)
15319 * @return {String} The formatted currency string
15321 number : function(v, decimals, thousandsDelimiter)
15323 // multiply and round.
15324 decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15325 thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15327 var mul = Math.pow(10, decimals);
15328 var zero = String(mul).substring(1);
15329 v = (Math.round((v-0)*mul))/mul;
15331 // if it's '0' number.. then
15333 //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15335 var ps = v.split('.');
15338 var r = /(\d+)(\d{3})/;
15341 if(thousandsDelimiter.length != 0) {
15342 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15347 (decimals ? ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15348 // does not have decimals
15349 (decimals ? ('.' + zero) : '');
15352 return whole + sub ;
15356 * Parse a value into a formatted date using the specified format pattern.
15357 * @param {Mixed} value The value to format
15358 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15359 * @return {String} The formatted date string
15361 date : function(v, format){
15365 if(!(v instanceof Date)){
15366 v = new Date(Date.parse(v));
15368 return v.dateFormat(format || Roo.util.Format.defaults.date);
15372 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15373 * @param {String} format Any valid date format string
15374 * @return {Function} The date formatting function
15376 dateRenderer : function(format){
15377 return function(v){
15378 return Roo.util.Format.date(v, format);
15383 stripTagsRE : /<\/?[^>]+>/gi,
15386 * Strips all HTML tags
15387 * @param {Mixed} value The text from which to strip tags
15388 * @return {String} The stripped text
15390 stripTags : function(v){
15391 return !v ? v : String(v).replace(this.stripTagsRE, "");
15395 * Size in Mb,Gb etc.
15396 * @param {Number} value The number to be formated
15397 * @param {number} decimals how many decimal places
15398 * @return {String} the formated string
15400 size : function(value, decimals)
15402 var sizes = ['b', 'k', 'M', 'G', 'T'];
15406 var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15407 return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals) + sizes[i];
15414 Roo.util.Format.defaults = {
15418 * Ext JS Library 1.1.1
15419 * Copyright(c) 2006-2007, Ext JS, LLC.
15421 * Originally Released Under LGPL - original licence link has changed is not relivant.
15424 * <script type="text/javascript">
15431 * @class Roo.MasterTemplate
15432 * @extends Roo.Template
15433 * Provides a template that can have child templates. The syntax is:
15435 var t = new Roo.MasterTemplate(
15436 '<select name="{name}">',
15437 '<tpl name="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
15440 t.add('options', {value: 'foo', text: 'bar'});
15441 // or you can add multiple child elements in one shot
15442 t.addAll('options', [
15443 {value: 'foo', text: 'bar'},
15444 {value: 'foo2', text: 'bar2'},
15445 {value: 'foo3', text: 'bar3'}
15447 // then append, applying the master template values
15448 t.append('my-form', {name: 'my-select'});
15450 * A name attribute for the child template is not required if you have only one child
15451 * template or you want to refer to them by index.
15453 Roo.MasterTemplate = function(){
15454 Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15455 this.originalHtml = this.html;
15457 var m, re = this.subTemplateRe;
15460 while(m = re.exec(this.html)){
15461 var name = m[1], content = m[2];
15466 tpl : new Roo.Template(content)
15469 st[name] = st[subIndex];
15471 st[subIndex].tpl.compile();
15472 st[subIndex].tpl.call = this.call.createDelegate(this);
15475 this.subCount = subIndex;
15478 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15480 * The regular expression used to match sub templates
15484 subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15487 * Applies the passed values to a child template.
15488 * @param {String/Number} name (optional) The name or index of the child template
15489 * @param {Array/Object} values The values to be applied to the template
15490 * @return {MasterTemplate} this
15492 add : function(name, values){
15493 if(arguments.length == 1){
15494 values = arguments[0];
15497 var s = this.subs[name];
15498 s.buffer[s.buffer.length] = s.tpl.apply(values);
15503 * Applies all the passed values to a child template.
15504 * @param {String/Number} name (optional) The name or index of the child template
15505 * @param {Array} values The values to be applied to the template, this should be an array of objects.
15506 * @param {Boolean} reset (optional) True to reset the template first
15507 * @return {MasterTemplate} this
15509 fill : function(name, values, reset){
15511 if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15519 for(var i = 0, len = values.length; i < len; i++){
15520 this.add(name, values[i]);
15526 * Resets the template for reuse
15527 * @return {MasterTemplate} this
15529 reset : function(){
15531 for(var i = 0; i < this.subCount; i++){
15537 applyTemplate : function(values){
15539 var replaceIndex = -1;
15540 this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15541 return s[++replaceIndex].buffer.join("");
15543 return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15546 apply : function(){
15547 return this.applyTemplate.apply(this, arguments);
15550 compile : function(){return this;}
15554 * Alias for fill().
15557 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15559 * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15560 * var tpl = Roo.MasterTemplate.from('element-id');
15561 * @param {String/HTMLElement} el
15562 * @param {Object} config
15565 Roo.MasterTemplate.from = function(el, config){
15566 el = Roo.getDom(el);
15567 return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15570 * Ext JS Library 1.1.1
15571 * Copyright(c) 2006-2007, Ext JS, LLC.
15573 * Originally Released Under LGPL - original licence link has changed is not relivant.
15576 * <script type="text/javascript">
15581 * @class Roo.util.CSS
15582 * Utility class for manipulating CSS rules
15586 Roo.util.CSS = function(){
15588 var doc = document;
15590 var camelRe = /(-[a-z])/gi;
15591 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15595 * Very simple dynamic creation of stylesheets from a text blob of rules. The text will wrapped in a style
15596 * tag and appended to the HEAD of the document.
15597 * @param {String|Object} cssText The text containing the css rules
15598 * @param {String} id An id to add to the stylesheet for later removal
15599 * @return {StyleSheet}
15601 createStyleSheet : function(cssText, id){
15603 var head = doc.getElementsByTagName("head")[0];
15604 var nrules = doc.createElement("style");
15605 nrules.setAttribute("type", "text/css");
15607 nrules.setAttribute("id", id);
15609 if (typeof(cssText) != 'string') {
15610 // support object maps..
15611 // not sure if this a good idea..
15612 // perhaps it should be merged with the general css handling
15613 // and handle js style props.
15614 var cssTextNew = [];
15615 for(var n in cssText) {
15617 for(var k in cssText[n]) {
15618 citems.push( k + ' : ' +cssText[n][k] + ';' );
15620 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15623 cssText = cssTextNew.join("\n");
15629 head.appendChild(nrules);
15630 ss = nrules.styleSheet;
15631 ss.cssText = cssText;
15634 nrules.appendChild(doc.createTextNode(cssText));
15636 nrules.cssText = cssText;
15638 head.appendChild(nrules);
15639 ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15641 this.cacheStyleSheet(ss);
15646 * Removes a style or link tag by id
15647 * @param {String} id The id of the tag
15649 removeStyleSheet : function(id){
15650 var existing = doc.getElementById(id);
15652 existing.parentNode.removeChild(existing);
15657 * Dynamically swaps an existing stylesheet reference for a new one
15658 * @param {String} id The id of an existing link tag to remove
15659 * @param {String} url The href of the new stylesheet to include
15661 swapStyleSheet : function(id, url){
15662 this.removeStyleSheet(id);
15663 var ss = doc.createElement("link");
15664 ss.setAttribute("rel", "stylesheet");
15665 ss.setAttribute("type", "text/css");
15666 ss.setAttribute("id", id);
15667 ss.setAttribute("href", url);
15668 doc.getElementsByTagName("head")[0].appendChild(ss);
15672 * Refresh the rule cache if you have dynamically added stylesheets
15673 * @return {Object} An object (hash) of rules indexed by selector
15675 refreshCache : function(){
15676 return this.getRules(true);
15680 cacheStyleSheet : function(stylesheet){
15684 try{// try catch for cross domain access issue
15685 var ssRules = stylesheet.cssRules || stylesheet.rules;
15686 for(var j = ssRules.length-1; j >= 0; --j){
15687 rules[ssRules[j].selectorText] = ssRules[j];
15693 * Gets all css rules for the document
15694 * @param {Boolean} refreshCache true to refresh the internal cache
15695 * @return {Object} An object (hash) of rules indexed by selector
15697 getRules : function(refreshCache){
15698 if(rules == null || refreshCache){
15700 var ds = doc.styleSheets;
15701 for(var i =0, len = ds.length; i < len; i++){
15703 this.cacheStyleSheet(ds[i]);
15711 * Gets an an individual CSS rule by selector(s)
15712 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15713 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15714 * @return {CSSRule} The CSS rule or null if one is not found
15716 getRule : function(selector, refreshCache){
15717 var rs = this.getRules(refreshCache);
15718 if(!(selector instanceof Array)){
15719 return rs[selector];
15721 for(var i = 0; i < selector.length; i++){
15722 if(rs[selector[i]]){
15723 return rs[selector[i]];
15731 * Updates a rule property
15732 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15733 * @param {String} property The css property
15734 * @param {String} value The new value for the property
15735 * @return {Boolean} true If a rule was found and updated
15737 updateRule : function(selector, property, value){
15738 if(!(selector instanceof Array)){
15739 var rule = this.getRule(selector);
15741 rule.style[property.replace(camelRe, camelFn)] = value;
15745 for(var i = 0; i < selector.length; i++){
15746 if(this.updateRule(selector[i], property, value)){
15756 * Ext JS Library 1.1.1
15757 * Copyright(c) 2006-2007, Ext JS, LLC.
15759 * Originally Released Under LGPL - original licence link has changed is not relivant.
15762 * <script type="text/javascript">
15768 * @class Roo.util.ClickRepeater
15769 * @extends Roo.util.Observable
15771 * A wrapper class which can be applied to any element. Fires a "click" event while the
15772 * mouse is pressed. The interval between firings may be specified in the config but
15773 * defaults to 10 milliseconds.
15775 * Optionally, a CSS class may be applied to the element during the time it is pressed.
15777 * @cfg {String/HTMLElement/Element} el The element to act as a button.
15778 * @cfg {Number} delay The initial delay before the repeating event begins firing.
15779 * Similar to an autorepeat key delay.
15780 * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15781 * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15782 * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15783 * "interval" and "delay" are ignored. "immediate" is honored.
15784 * @cfg {Boolean} preventDefault True to prevent the default click event
15785 * @cfg {Boolean} stopDefault True to stop the default click event
15788 * 2007-02-02 jvs Original code contributed by Nige "Animal" White
15789 * 2007-02-02 jvs Renamed to ClickRepeater
15790 * 2007-02-03 jvs Modifications for FF Mac and Safari
15793 * @param {String/HTMLElement/Element} el The element to listen on
15794 * @param {Object} config
15796 Roo.util.ClickRepeater = function(el, config)
15798 this.el = Roo.get(el);
15799 this.el.unselectable();
15801 Roo.apply(this, config);
15806 * Fires when the mouse button is depressed.
15807 * @param {Roo.util.ClickRepeater} this
15809 "mousedown" : true,
15812 * Fires on a specified interval during the time the element is pressed.
15813 * @param {Roo.util.ClickRepeater} this
15818 * Fires when the mouse key is released.
15819 * @param {Roo.util.ClickRepeater} this
15824 this.el.on("mousedown", this.handleMouseDown, this);
15825 if(this.preventDefault || this.stopDefault){
15826 this.el.on("click", function(e){
15827 if(this.preventDefault){
15828 e.preventDefault();
15830 if(this.stopDefault){
15836 // allow inline handler
15838 this.on("click", this.handler, this.scope || this);
15841 Roo.util.ClickRepeater.superclass.constructor.call(this);
15844 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15847 preventDefault : true,
15848 stopDefault : false,
15852 handleMouseDown : function(){
15853 clearTimeout(this.timer);
15855 if(this.pressClass){
15856 this.el.addClass(this.pressClass);
15858 this.mousedownTime = new Date();
15860 Roo.get(document).on("mouseup", this.handleMouseUp, this);
15861 this.el.on("mouseout", this.handleMouseOut, this);
15863 this.fireEvent("mousedown", this);
15864 this.fireEvent("click", this);
15866 this.timer = this.click.defer(this.delay || this.interval, this);
15870 click : function(){
15871 this.fireEvent("click", this);
15872 this.timer = this.click.defer(this.getInterval(), this);
15876 getInterval: function(){
15877 if(!this.accelerate){
15878 return this.interval;
15880 var pressTime = this.mousedownTime.getElapsed();
15881 if(pressTime < 500){
15883 }else if(pressTime < 1700){
15885 }else if(pressTime < 2600){
15887 }else if(pressTime < 3500){
15889 }else if(pressTime < 4400){
15891 }else if(pressTime < 5300){
15893 }else if(pressTime < 6200){
15901 handleMouseOut : function(){
15902 clearTimeout(this.timer);
15903 if(this.pressClass){
15904 this.el.removeClass(this.pressClass);
15906 this.el.on("mouseover", this.handleMouseReturn, this);
15910 handleMouseReturn : function(){
15911 this.el.un("mouseover", this.handleMouseReturn);
15912 if(this.pressClass){
15913 this.el.addClass(this.pressClass);
15919 handleMouseUp : function(){
15920 clearTimeout(this.timer);
15921 this.el.un("mouseover", this.handleMouseReturn);
15922 this.el.un("mouseout", this.handleMouseOut);
15923 Roo.get(document).un("mouseup", this.handleMouseUp);
15924 this.el.removeClass(this.pressClass);
15925 this.fireEvent("mouseup", this);
15928 * @class Roo.util.Clipboard
15934 Roo.util.Clipboard = {
15936 * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15937 * @param {String} text to copy to clipboard
15939 write : function(text) {
15940 // navigator clipboard api needs a secure context (https)
15941 if (navigator.clipboard && window.isSecureContext) {
15942 // navigator clipboard api method'
15943 navigator.clipboard.writeText(text);
15946 // text area method
15947 var ta = document.createElement("textarea");
15949 // make the textarea out of viewport
15950 ta.style.position = "fixed";
15951 ta.style.left = "-999999px";
15952 ta.style.top = "-999999px";
15953 document.body.appendChild(ta);
15956 document.execCommand('copy');
15966 * Ext JS Library 1.1.1
15967 * Copyright(c) 2006-2007, Ext JS, LLC.
15969 * Originally Released Under LGPL - original licence link has changed is not relivant.
15972 * <script type="text/javascript">
15977 * @class Roo.KeyNav
15978 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
15979 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15980 * way to implement custom navigation schemes for any UI component.</p>
15981 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15982 * pageUp, pageDown, del, home, end. Usage:</p>
15984 var nav = new Roo.KeyNav("my-element", {
15985 "left" : function(e){
15986 this.moveLeft(e.ctrlKey);
15988 "right" : function(e){
15989 this.moveRight(e.ctrlKey);
15991 "enter" : function(e){
15998 * @param {String/HTMLElement/Roo.Element} el The element to bind to
15999 * @param {Object} config The config
16001 Roo.KeyNav = function(el, config){
16002 this.el = Roo.get(el);
16003 Roo.apply(this, config);
16004 if(!this.disabled){
16005 this.disabled = true;
16010 Roo.KeyNav.prototype = {
16012 * @cfg {Boolean} disabled
16013 * True to disable this KeyNav instance (defaults to false)
16017 * @cfg {String} defaultEventAction
16018 * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key. Valid values are
16019 * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16020 * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16022 defaultEventAction: "stopEvent",
16024 * @cfg {Boolean} forceKeyDown
16025 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
16026 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16027 * handle keydown instead of keypress.
16029 forceKeyDown : false,
16032 prepareEvent : function(e){
16033 var k = e.getKey();
16034 var h = this.keyToHandler[k];
16035 //if(h && this[h]){
16036 // e.stopPropagation();
16038 if(Roo.isSafari && h && k >= 37 && k <= 40){
16044 relay : function(e){
16045 var k = e.getKey();
16046 var h = this.keyToHandler[k];
16048 if(this.doRelay(e, this[h], h) !== true){
16049 e[this.defaultEventAction]();
16055 doRelay : function(e, h, hname){
16056 return h.call(this.scope || this, e);
16059 // possible handlers
16073 // quick lookup hash
16090 * Enable this KeyNav
16092 enable: function(){
16094 // ie won't do special keys on keypress, no one else will repeat keys with keydown
16095 // the EventObject will normalize Safari automatically
16096 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16097 this.el.on("keydown", this.relay, this);
16099 this.el.on("keydown", this.prepareEvent, this);
16100 this.el.on("keypress", this.relay, this);
16102 this.disabled = false;
16107 * Disable this KeyNav
16109 disable: function(){
16110 if(!this.disabled){
16111 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16112 this.el.un("keydown", this.relay);
16114 this.el.un("keydown", this.prepareEvent);
16115 this.el.un("keypress", this.relay);
16117 this.disabled = true;
16122 * Ext JS Library 1.1.1
16123 * Copyright(c) 2006-2007, Ext JS, LLC.
16125 * Originally Released Under LGPL - original licence link has changed is not relivant.
16128 * <script type="text/javascript">
16133 * @class Roo.KeyMap
16134 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16135 * The constructor accepts the same config object as defined by {@link #addBinding}.
16136 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16137 * combination it will call the function with this signature (if the match is a multi-key
16138 * combination the callback will still be called only once): (String key, Roo.EventObject e)
16139 * A KeyMap can also handle a string representation of keys.<br />
16142 // map one key by key code
16143 var map = new Roo.KeyMap("my-element", {
16144 key: 13, // or Roo.EventObject.ENTER
16149 // map multiple keys to one action by string
16150 var map = new Roo.KeyMap("my-element", {
16156 // map multiple keys to multiple actions by strings and array of codes
16157 var map = new Roo.KeyMap("my-element", [
16160 fn: function(){ alert("Return was pressed"); }
16163 fn: function(){ alert('a, b or c was pressed'); }
16168 fn: function(){ alert('Control + shift + tab was pressed.'); }
16172 * <b>Note: A KeyMap starts enabled</b>
16174 * @param {String/HTMLElement/Roo.Element} el The element to bind to
16175 * @param {Object} config The config (see {@link #addBinding})
16176 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16178 Roo.KeyMap = function(el, config, eventName){
16179 this.el = Roo.get(el);
16180 this.eventName = eventName || "keydown";
16181 this.bindings = [];
16183 this.addBinding(config);
16188 Roo.KeyMap.prototype = {
16190 * True to stop the event from bubbling and prevent the default browser action if the
16191 * key was handled by the KeyMap (defaults to false)
16197 * Add a new binding to this KeyMap. The following config object properties are supported:
16199 Property Type Description
16200 ---------- --------------- ----------------------------------------------------------------------
16201 key String/Array A single keycode or an array of keycodes to handle
16202 shift Boolean True to handle key only when shift is pressed (defaults to false)
16203 ctrl Boolean True to handle key only when ctrl is pressed (defaults to false)
16204 alt Boolean True to handle key only when alt is pressed (defaults to false)
16205 fn Function The function to call when KeyMap finds the expected key combination
16206 scope Object The scope of the callback function
16212 var map = new Roo.KeyMap(document, {
16213 key: Roo.EventObject.ENTER,
16218 //Add a new binding to the existing KeyMap later
16226 * @param {Object/Array} config A single KeyMap config or an array of configs
16228 addBinding : function(config){
16229 if(config instanceof Array){
16230 for(var i = 0, len = config.length; i < len; i++){
16231 this.addBinding(config[i]);
16235 var keyCode = config.key,
16236 shift = config.shift,
16237 ctrl = config.ctrl,
16240 scope = config.scope;
16241 if(typeof keyCode == "string"){
16243 var keyString = keyCode.toUpperCase();
16244 for(var j = 0, len = keyString.length; j < len; j++){
16245 ks.push(keyString.charCodeAt(j));
16249 var keyArray = keyCode instanceof Array;
16250 var handler = function(e){
16251 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
16252 var k = e.getKey();
16254 for(var i = 0, len = keyCode.length; i < len; i++){
16255 if(keyCode[i] == k){
16256 if(this.stopEvent){
16259 fn.call(scope || window, k, e);
16265 if(this.stopEvent){
16268 fn.call(scope || window, k, e);
16273 this.bindings.push(handler);
16277 * Shorthand for adding a single key listener
16278 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16279 * following options:
16280 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16281 * @param {Function} fn The function to call
16282 * @param {Object} scope (optional) The scope of the function
16284 on : function(key, fn, scope){
16285 var keyCode, shift, ctrl, alt;
16286 if(typeof key == "object" && !(key instanceof Array)){
16305 handleKeyDown : function(e){
16306 if(this.enabled){ //just in case
16307 var b = this.bindings;
16308 for(var i = 0, len = b.length; i < len; i++){
16309 b[i].call(this, e);
16315 * Returns true if this KeyMap is enabled
16316 * @return {Boolean}
16318 isEnabled : function(){
16319 return this.enabled;
16323 * Enables this KeyMap
16325 enable: function(){
16327 this.el.on(this.eventName, this.handleKeyDown, this);
16328 this.enabled = true;
16333 * Disable this KeyMap
16335 disable: function(){
16337 this.el.removeListener(this.eventName, this.handleKeyDown, this);
16338 this.enabled = false;
16343 * Ext JS Library 1.1.1
16344 * Copyright(c) 2006-2007, Ext JS, LLC.
16346 * Originally Released Under LGPL - original licence link has changed is not relivant.
16349 * <script type="text/javascript">
16354 * @class Roo.util.TextMetrics
16355 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16356 * wide, in pixels, a given block of text will be.
16359 Roo.util.TextMetrics = function(){
16363 * Measures the size of the specified text
16364 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16365 * that can affect the size of the rendered text
16366 * @param {String} text The text to measure
16367 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16368 * in order to accurately measure the text height
16369 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16371 measure : function(el, text, fixedWidth){
16373 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16376 shared.setFixedWidth(fixedWidth || 'auto');
16377 return shared.getSize(text);
16381 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
16382 * the overhead of multiple calls to initialize the style properties on each measurement.
16383 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16384 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16385 * in order to accurately measure the text height
16386 * @return {Roo.util.TextMetrics.Instance} instance The new instance
16388 createInstance : function(el, fixedWidth){
16389 return Roo.util.TextMetrics.Instance(el, fixedWidth);
16395 * @class Roo.util.TextMetrics.Instance
16396 * Instance of TextMetrics Calcuation
16398 * Create a new TextMetrics Instance
16399 * @param {Object} bindto
16400 * @param {Boolean} fixedWidth
16403 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16405 var ml = new Roo.Element(document.createElement('div'));
16406 document.body.appendChild(ml.dom);
16407 ml.position('absolute');
16408 ml.setLeftTop(-1000, -1000);
16412 ml.setWidth(fixedWidth);
16417 * Returns the size of the specified text based on the internal element's style and width properties
16418 * @param {String} text The text to measure
16419 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16421 getSize : function(text){
16423 var s = ml.getSize();
16429 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16430 * that can affect the size of the rendered text
16431 * @param {String/HTMLElement} el The element, dom node or id
16433 bind : function(el){
16435 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16440 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
16441 * to set a fixed width in order to accurately measure the text height.
16442 * @param {Number} width The width to set on the element
16444 setFixedWidth : function(width){
16445 ml.setWidth(width);
16449 * Returns the measured width of the specified text
16450 * @param {String} text The text to measure
16451 * @return {Number} width The width in pixels
16453 getWidth : function(text){
16454 ml.dom.style.width = 'auto';
16455 return this.getSize(text).width;
16459 * Returns the measured height of the specified text. For multiline text, be sure to call
16460 * {@link #setFixedWidth} if necessary.
16461 * @param {String} text The text to measure
16462 * @return {Number} height The height in pixels
16464 getHeight : function(text){
16465 return this.getSize(text).height;
16469 instance.bind(bindTo);
16474 // backwards compat
16475 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16477 * Ext JS Library 1.1.1
16478 * Copyright(c) 2006-2007, Ext JS, LLC.
16480 * Originally Released Under LGPL - original licence link has changed is not relivant.
16483 * <script type="text/javascript">
16487 * @class Roo.state.Provider
16488 * Abstract base class for state provider implementations. This class provides methods
16489 * for encoding and decoding <b>typed</b> variables including dates and defines the
16490 * Provider interface.
16492 Roo.state.Provider = function(){
16494 * @event statechange
16495 * Fires when a state change occurs.
16496 * @param {Provider} this This state provider
16497 * @param {String} key The state key which was changed
16498 * @param {String} value The encoded value for the state
16501 "statechange": true
16504 Roo.state.Provider.superclass.constructor.call(this);
16506 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16508 * Returns the current value for a key
16509 * @param {String} name The key name
16510 * @param {Mixed} defaultValue A default value to return if the key's value is not found
16511 * @return {Mixed} The state data
16513 get : function(name, defaultValue){
16514 return typeof this.state[name] == "undefined" ?
16515 defaultValue : this.state[name];
16519 * Clears a value from the state
16520 * @param {String} name The key name
16522 clear : function(name){
16523 delete this.state[name];
16524 this.fireEvent("statechange", this, name, null);
16528 * Sets the value for a key
16529 * @param {String} name The key name
16530 * @param {Mixed} value The value to set
16532 set : function(name, value){
16533 this.state[name] = value;
16534 this.fireEvent("statechange", this, name, value);
16538 * Decodes a string previously encoded with {@link #encodeValue}.
16539 * @param {String} value The value to decode
16540 * @return {Mixed} The decoded value
16542 decodeValue : function(cookie){
16543 var re = /^(a|n|d|b|s|o)\:(.*)$/;
16544 var matches = re.exec(unescape(cookie));
16545 if(!matches || !matches[1]) {
16546 return; // non state cookie
16548 var type = matches[1];
16549 var v = matches[2];
16552 return parseFloat(v);
16554 return new Date(Date.parse(v));
16559 var values = v.split("^");
16560 for(var i = 0, len = values.length; i < len; i++){
16561 all.push(this.decodeValue(values[i]));
16566 var values = v.split("^");
16567 for(var i = 0, len = values.length; i < len; i++){
16568 var kv = values[i].split("=");
16569 all[kv[0]] = this.decodeValue(kv[1]);
16578 * Encodes a value including type information. Decode with {@link #decodeValue}.
16579 * @param {Mixed} value The value to encode
16580 * @return {String} The encoded value
16582 encodeValue : function(v){
16584 if(typeof v == "number"){
16586 }else if(typeof v == "boolean"){
16587 enc = "b:" + (v ? "1" : "0");
16588 }else if(v instanceof Date){
16589 enc = "d:" + v.toGMTString();
16590 }else if(v instanceof Array){
16592 for(var i = 0, len = v.length; i < len; i++){
16593 flat += this.encodeValue(v[i]);
16599 }else if(typeof v == "object"){
16602 if(typeof v[key] != "function"){
16603 flat += key + "=" + this.encodeValue(v[key]) + "^";
16606 enc = "o:" + flat.substring(0, flat.length-1);
16610 return escape(enc);
16616 * Ext JS Library 1.1.1
16617 * Copyright(c) 2006-2007, Ext JS, LLC.
16619 * Originally Released Under LGPL - original licence link has changed is not relivant.
16622 * <script type="text/javascript">
16625 * @class Roo.state.Manager
16626 * This is the global state manager. By default all components that are "state aware" check this class
16627 * for state information if you don't pass them a custom state provider. In order for this class
16628 * to be useful, it must be initialized with a provider when your application initializes.
16630 // in your initialization function
16632 Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16634 // supposed you have a {@link Roo.BorderLayout}
16635 var layout = new Roo.BorderLayout(...);
16636 layout.restoreState();
16637 // or a {Roo.BasicDialog}
16638 var dialog = new Roo.BasicDialog(...);
16639 dialog.restoreState();
16643 Roo.state.Manager = function(){
16644 var provider = new Roo.state.Provider();
16648 * Configures the default state provider for your application
16649 * @param {Provider} stateProvider The state provider to set
16651 setProvider : function(stateProvider){
16652 provider = stateProvider;
16656 * Returns the current value for a key
16657 * @param {String} name The key name
16658 * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16659 * @return {Mixed} The state data
16661 get : function(key, defaultValue){
16662 return provider.get(key, defaultValue);
16666 * Sets the value for a key
16667 * @param {String} name The key name
16668 * @param {Mixed} value The state data
16670 set : function(key, value){
16671 provider.set(key, value);
16675 * Clears a value from the state
16676 * @param {String} name The key name
16678 clear : function(key){
16679 provider.clear(key);
16683 * Gets the currently configured state provider
16684 * @return {Provider} The state provider
16686 getProvider : function(){
16693 * Ext JS Library 1.1.1
16694 * Copyright(c) 2006-2007, Ext JS, LLC.
16696 * Originally Released Under LGPL - original licence link has changed is not relivant.
16699 * <script type="text/javascript">
16702 * @class Roo.state.CookieProvider
16703 * @extends Roo.state.Provider
16704 * The default Provider implementation which saves state via cookies.
16707 var cp = new Roo.state.CookieProvider({
16709 expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16710 domain: "roojs.com"
16712 Roo.state.Manager.setProvider(cp);
16714 * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16715 * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16716 * @cfg {String} domain The domain to save the cookie for. Note that you cannot specify a different domain than
16717 * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16718 * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16719 * domain the page is running on including the 'www' like 'www.roojs.com')
16720 * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16722 * Create a new CookieProvider
16723 * @param {Object} config The configuration object
16725 Roo.state.CookieProvider = function(config){
16726 Roo.state.CookieProvider.superclass.constructor.call(this);
16728 this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16729 this.domain = null;
16730 this.secure = false;
16731 Roo.apply(this, config);
16732 this.state = this.readCookies();
16735 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16737 set : function(name, value){
16738 if(typeof value == "undefined" || value === null){
16742 this.setCookie(name, value);
16743 Roo.state.CookieProvider.superclass.set.call(this, name, value);
16747 clear : function(name){
16748 this.clearCookie(name);
16749 Roo.state.CookieProvider.superclass.clear.call(this, name);
16753 readCookies : function(){
16755 var c = document.cookie + ";";
16756 var re = /\s?(.*?)=(.*?);/g;
16758 while((matches = re.exec(c)) != null){
16759 var name = matches[1];
16760 var value = matches[2];
16761 if(name && name.substring(0,3) == "ys-"){
16762 cookies[name.substr(3)] = this.decodeValue(value);
16769 setCookie : function(name, value){
16770 document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16771 ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16772 ((this.path == null) ? "" : ("; path=" + this.path)) +
16773 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16774 ((this.secure == true) ? "; secure" : "");
16778 clearCookie : function(name){
16779 document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16780 ((this.path == null) ? "" : ("; path=" + this.path)) +
16781 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16782 ((this.secure == true) ? "; secure" : "");
16786 * Ext JS Library 1.1.1
16787 * Copyright(c) 2006-2007, Ext JS, LLC.
16789 * Originally Released Under LGPL - original licence link has changed is not relivant.
16792 * <script type="text/javascript">
16797 * @class Roo.ComponentMgr
16798 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16801 Roo.ComponentMgr = function(){
16802 var all = new Roo.util.MixedCollection();
16806 * Registers a component.
16807 * @param {Roo.Component} c The component
16809 register : function(c){
16814 * Unregisters a component.
16815 * @param {Roo.Component} c The component
16817 unregister : function(c){
16822 * Returns a component by id
16823 * @param {String} id The component id
16825 get : function(id){
16826 return all.get(id);
16830 * Registers a function that will be called when a specified component is added to ComponentMgr
16831 * @param {String} id The component id
16832 * @param {Funtction} fn The callback function
16833 * @param {Object} scope The scope of the callback
16835 onAvailable : function(id, fn, scope){
16836 all.on("add", function(index, o){
16838 fn.call(scope || o, o);
16839 all.un("add", fn, scope);
16846 * Ext JS Library 1.1.1
16847 * Copyright(c) 2006-2007, Ext JS, LLC.
16849 * Originally Released Under LGPL - original licence link has changed is not relivant.
16852 * <script type="text/javascript">
16856 * @class Roo.Component
16857 * @extends Roo.util.Observable
16858 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
16859 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
16860 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16861 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16862 * All visual components (widgets) that require rendering into a layout should subclass Component.
16864 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
16865 * 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
16866 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
16868 Roo.Component = function(config){
16869 config = config || {};
16870 if(config.tagName || config.dom || typeof config == "string"){ // element object
16871 config = {el: config, id: config.id || config};
16873 this.initialConfig = config;
16875 Roo.apply(this, config);
16879 * Fires after the component is disabled.
16880 * @param {Roo.Component} this
16885 * Fires after the component is enabled.
16886 * @param {Roo.Component} this
16890 * @event beforeshow
16891 * Fires before the component is shown. Return false to stop the show.
16892 * @param {Roo.Component} this
16897 * Fires after the component is shown.
16898 * @param {Roo.Component} this
16902 * @event beforehide
16903 * Fires before the component is hidden. Return false to stop the hide.
16904 * @param {Roo.Component} this
16909 * Fires after the component is hidden.
16910 * @param {Roo.Component} this
16914 * @event beforerender
16915 * Fires before the component is rendered. Return false to stop the render.
16916 * @param {Roo.Component} this
16918 beforerender : true,
16921 * Fires after the component is rendered.
16922 * @param {Roo.Component} this
16926 * @event beforedestroy
16927 * Fires before the component is destroyed. Return false to stop the destroy.
16928 * @param {Roo.Component} this
16930 beforedestroy : true,
16933 * Fires after the component is destroyed.
16934 * @param {Roo.Component} this
16939 this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16941 Roo.ComponentMgr.register(this);
16942 Roo.Component.superclass.constructor.call(this);
16943 this.initComponent();
16944 if(this.renderTo){ // not supported by all components yet. use at your own risk!
16945 this.render(this.renderTo);
16946 delete this.renderTo;
16951 Roo.Component.AUTO_ID = 1000;
16953 Roo.extend(Roo.Component, Roo.util.Observable, {
16955 * @scope Roo.Component.prototype
16957 * true if this component is hidden. Read-only.
16962 * true if this component is disabled. Read-only.
16967 * true if this component has been rendered. Read-only.
16971 /** @cfg {String} disableClass
16972 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16974 disabledClass : "x-item-disabled",
16975 /** @cfg {Boolean} allowDomMove
16976 * Whether the component can move the Dom node when rendering (defaults to true).
16978 allowDomMove : true,
16979 /** @cfg {String} hideMode (display|visibility)
16980 * How this component should hidden. Supported values are
16981 * "visibility" (css visibility), "offsets" (negative offset position) and
16982 * "display" (css display) - defaults to "display".
16984 hideMode: 'display',
16987 ctype : "Roo.Component",
16990 * @cfg {String} actionMode
16991 * which property holds the element that used for hide() / show() / disable() / enable()
16992 * default is 'el' for forms you probably want to set this to fieldEl
16997 getActionEl : function(){
16998 return this[this.actionMode];
17001 initComponent : Roo.emptyFn,
17003 * If this is a lazy rendering component, render it to its container element.
17004 * @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.
17006 render : function(container, position){
17012 if(this.fireEvent("beforerender", this) === false){
17016 if(!container && this.el){
17017 this.el = Roo.get(this.el);
17018 container = this.el.dom.parentNode;
17019 this.allowDomMove = false;
17021 this.container = Roo.get(container);
17022 this.rendered = true;
17023 if(position !== undefined){
17024 if(typeof position == 'number'){
17025 position = this.container.dom.childNodes[position];
17027 position = Roo.getDom(position);
17030 this.onRender(this.container, position || null);
17032 this.el.addClass(this.cls);
17036 this.el.applyStyles(this.style);
17039 this.fireEvent("render", this);
17040 this.afterRender(this.container);
17053 // default function is not really useful
17054 onRender : function(ct, position){
17056 this.el = Roo.get(this.el);
17057 if(this.allowDomMove !== false){
17058 ct.dom.insertBefore(this.el.dom, position);
17064 getAutoCreate : function(){
17065 var cfg = typeof this.autoCreate == "object" ?
17066 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17067 if(this.id && !cfg.id){
17074 afterRender : Roo.emptyFn,
17077 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17078 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17080 destroy : function(){
17081 if(this.fireEvent("beforedestroy", this) !== false){
17082 this.purgeListeners();
17083 this.beforeDestroy();
17085 this.el.removeAllListeners();
17087 if(this.actionMode == "container"){
17088 this.container.remove();
17092 Roo.ComponentMgr.unregister(this);
17093 this.fireEvent("destroy", this);
17098 beforeDestroy : function(){
17103 onDestroy : function(){
17108 * Returns the underlying {@link Roo.Element}.
17109 * @return {Roo.Element} The element
17111 getEl : function(){
17116 * Returns the id of this component.
17119 getId : function(){
17124 * Try to focus this component.
17125 * @param {Boolean} selectText True to also select the text in this component (if applicable)
17126 * @return {Roo.Component} this
17128 focus : function(selectText){
17131 if(selectText === true){
17132 this.el.dom.select();
17147 * Disable this component.
17148 * @return {Roo.Component} this
17150 disable : function(){
17154 this.disabled = true;
17155 this.fireEvent("disable", this);
17160 onDisable : function(){
17161 this.getActionEl().addClass(this.disabledClass);
17162 this.el.dom.disabled = true;
17166 * Enable this component.
17167 * @return {Roo.Component} this
17169 enable : function(){
17173 this.disabled = false;
17174 this.fireEvent("enable", this);
17179 onEnable : function(){
17180 this.getActionEl().removeClass(this.disabledClass);
17181 this.el.dom.disabled = false;
17185 * Convenience function for setting disabled/enabled by boolean.
17186 * @param {Boolean} disabled
17188 setDisabled : function(disabled){
17189 this[disabled ? "disable" : "enable"]();
17193 * Show this component.
17194 * @return {Roo.Component} this
17197 if(this.fireEvent("beforeshow", this) !== false){
17198 this.hidden = false;
17202 this.fireEvent("show", this);
17208 onShow : function(){
17209 var ae = this.getActionEl();
17210 if(this.hideMode == 'visibility'){
17211 ae.dom.style.visibility = "visible";
17212 }else if(this.hideMode == 'offsets'){
17213 ae.removeClass('x-hidden');
17215 ae.dom.style.display = "";
17220 * Hide this component.
17221 * @return {Roo.Component} this
17224 if(this.fireEvent("beforehide", this) !== false){
17225 this.hidden = true;
17229 this.fireEvent("hide", this);
17235 onHide : function(){
17236 var ae = this.getActionEl();
17237 if(this.hideMode == 'visibility'){
17238 ae.dom.style.visibility = "hidden";
17239 }else if(this.hideMode == 'offsets'){
17240 ae.addClass('x-hidden');
17242 ae.dom.style.display = "none";
17247 * Convenience function to hide or show this component by boolean.
17248 * @param {Boolean} visible True to show, false to hide
17249 * @return {Roo.Component} this
17251 setVisible: function(visible){
17261 * Returns true if this component is visible.
17263 isVisible : function(){
17264 return this.getActionEl().isVisible();
17267 cloneConfig : function(overrides){
17268 overrides = overrides || {};
17269 var id = overrides.id || Roo.id();
17270 var cfg = Roo.applyIf(overrides, this.initialConfig);
17271 cfg.id = id; // prevent dup id
17272 return new this.constructor(cfg);
17276 * Ext JS Library 1.1.1
17277 * Copyright(c) 2006-2007, Ext JS, LLC.
17279 * Originally Released Under LGPL - original licence link has changed is not relivant.
17282 * <script type="text/javascript">
17286 * @class Roo.BoxComponent
17287 * @extends Roo.Component
17288 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
17289 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
17290 * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17291 * layout containers.
17293 * @param {Roo.Element/String/Object} config The configuration options.
17295 Roo.BoxComponent = function(config){
17296 Roo.Component.call(this, config);
17300 * Fires after the component is resized.
17301 * @param {Roo.Component} this
17302 * @param {Number} adjWidth The box-adjusted width that was set
17303 * @param {Number} adjHeight The box-adjusted height that was set
17304 * @param {Number} rawWidth The width that was originally specified
17305 * @param {Number} rawHeight The height that was originally specified
17310 * Fires after the component is moved.
17311 * @param {Roo.Component} this
17312 * @param {Number} x The new x position
17313 * @param {Number} y The new y position
17319 Roo.extend(Roo.BoxComponent, Roo.Component, {
17320 // private, set in afterRender to signify that the component has been rendered
17322 // private, used to defer height settings to subclasses
17323 deferHeight: false,
17324 /** @cfg {Number} width
17325 * width (optional) size of component
17327 /** @cfg {Number} height
17328 * height (optional) size of component
17332 * Sets the width and height of the component. This method fires the resize event. This method can accept
17333 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17334 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17335 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17336 * @return {Roo.BoxComponent} this
17338 setSize : function(w, h){
17339 // support for standard size objects
17340 if(typeof w == 'object'){
17345 if(!this.boxReady){
17351 // prevent recalcs when not needed
17352 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17355 this.lastSize = {width: w, height: h};
17357 var adj = this.adjustSize(w, h);
17358 var aw = adj.width, ah = adj.height;
17359 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17360 var rz = this.getResizeEl();
17361 if(!this.deferHeight && aw !== undefined && ah !== undefined){
17362 rz.setSize(aw, ah);
17363 }else if(!this.deferHeight && ah !== undefined){
17365 }else if(aw !== undefined){
17368 this.onResize(aw, ah, w, h);
17369 this.fireEvent('resize', this, aw, ah, w, h);
17375 * Gets the current size of the component's underlying element.
17376 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17378 getSize : function(){
17379 return this.el.getSize();
17383 * Gets the current XY position of the component's underlying element.
17384 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17385 * @return {Array} The XY position of the element (e.g., [100, 200])
17387 getPosition : function(local){
17388 if(local === true){
17389 return [this.el.getLeft(true), this.el.getTop(true)];
17391 return this.xy || this.el.getXY();
17395 * Gets the current box measurements of the component's underlying element.
17396 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17397 * @returns {Object} box An object in the format {x, y, width, height}
17399 getBox : function(local){
17400 var s = this.el.getSize();
17402 s.x = this.el.getLeft(true);
17403 s.y = this.el.getTop(true);
17405 var xy = this.xy || this.el.getXY();
17413 * Sets the current box measurements of the component's underlying element.
17414 * @param {Object} box An object in the format {x, y, width, height}
17415 * @returns {Roo.BoxComponent} this
17417 updateBox : function(box){
17418 this.setSize(box.width, box.height);
17419 this.setPagePosition(box.x, box.y);
17424 getResizeEl : function(){
17425 return this.resizeEl || this.el;
17429 getPositionEl : function(){
17430 return this.positionEl || this.el;
17434 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
17435 * This method fires the move event.
17436 * @param {Number} left The new left
17437 * @param {Number} top The new top
17438 * @returns {Roo.BoxComponent} this
17440 setPosition : function(x, y){
17443 if(!this.boxReady){
17446 var adj = this.adjustPosition(x, y);
17447 var ax = adj.x, ay = adj.y;
17449 var el = this.getPositionEl();
17450 if(ax !== undefined || ay !== undefined){
17451 if(ax !== undefined && ay !== undefined){
17452 el.setLeftTop(ax, ay);
17453 }else if(ax !== undefined){
17455 }else if(ay !== undefined){
17458 this.onPosition(ax, ay);
17459 this.fireEvent('move', this, ax, ay);
17465 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
17466 * This method fires the move event.
17467 * @param {Number} x The new x position
17468 * @param {Number} y The new y position
17469 * @returns {Roo.BoxComponent} this
17471 setPagePosition : function(x, y){
17474 if(!this.boxReady){
17477 if(x === undefined || y === undefined){ // cannot translate undefined points
17480 var p = this.el.translatePoints(x, y);
17481 this.setPosition(p.left, p.top);
17486 onRender : function(ct, position){
17487 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17489 this.resizeEl = Roo.get(this.resizeEl);
17491 if(this.positionEl){
17492 this.positionEl = Roo.get(this.positionEl);
17497 afterRender : function(){
17498 Roo.BoxComponent.superclass.afterRender.call(this);
17499 this.boxReady = true;
17500 this.setSize(this.width, this.height);
17501 if(this.x || this.y){
17502 this.setPosition(this.x, this.y);
17504 if(this.pageX || this.pageY){
17505 this.setPagePosition(this.pageX, this.pageY);
17510 * Force the component's size to recalculate based on the underlying element's current height and width.
17511 * @returns {Roo.BoxComponent} this
17513 syncSize : function(){
17514 delete this.lastSize;
17515 this.setSize(this.el.getWidth(), this.el.getHeight());
17520 * Called after the component is resized, this method is empty by default but can be implemented by any
17521 * subclass that needs to perform custom logic after a resize occurs.
17522 * @param {Number} adjWidth The box-adjusted width that was set
17523 * @param {Number} adjHeight The box-adjusted height that was set
17524 * @param {Number} rawWidth The width that was originally specified
17525 * @param {Number} rawHeight The height that was originally specified
17527 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17532 * Called after the component is moved, this method is empty by default but can be implemented by any
17533 * subclass that needs to perform custom logic after a move occurs.
17534 * @param {Number} x The new x position
17535 * @param {Number} y The new y position
17537 onPosition : function(x, y){
17542 adjustSize : function(w, h){
17543 if(this.autoWidth){
17546 if(this.autoHeight){
17549 return {width : w, height: h};
17553 adjustPosition : function(x, y){
17554 return {x : x, y: y};
17558 * Ext JS Library 1.1.1
17559 * Copyright(c) 2006-2007, Ext JS, LLC.
17561 * Originally Released Under LGPL - original licence link has changed is not relivant.
17564 * <script type="text/javascript">
17569 * @extends Roo.Element
17570 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17571 * automatic maintaining of shadow/shim positions.
17572 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17573 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17574 * you can pass a string with a CSS class name. False turns off the shadow.
17575 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17576 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17577 * @cfg {String} cls CSS class to add to the element
17578 * @cfg {Number} zindex Starting z-index (defaults to 11000)
17579 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17581 * @param {Object} config An object with config options.
17582 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17585 Roo.Layer = function(config, existingEl){
17586 config = config || {};
17587 var dh = Roo.DomHelper;
17588 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17590 this.dom = Roo.getDom(existingEl);
17593 var o = config.dh || {tag: "div", cls: "x-layer"};
17594 this.dom = dh.append(pel, o);
17597 this.addClass(config.cls);
17599 this.constrain = config.constrain !== false;
17600 this.visibilityMode = Roo.Element.VISIBILITY;
17602 this.id = this.dom.id = config.id;
17604 this.id = Roo.id(this.dom);
17606 this.zindex = config.zindex || this.getZIndex();
17607 this.position("absolute", this.zindex);
17609 this.shadowOffset = config.shadowOffset || 4;
17610 this.shadow = new Roo.Shadow({
17611 offset : this.shadowOffset,
17612 mode : config.shadow
17615 this.shadowOffset = 0;
17617 this.useShim = config.shim !== false && Roo.useShims;
17618 this.useDisplay = config.useDisplay;
17622 var supr = Roo.Element.prototype;
17624 // shims are shared among layer to keep from having 100 iframes
17627 Roo.extend(Roo.Layer, Roo.Element, {
17629 getZIndex : function(){
17630 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17633 getShim : function(){
17640 var shim = shims.shift();
17642 shim = this.createShim();
17643 shim.enableDisplayMode('block');
17644 shim.dom.style.display = 'none';
17645 shim.dom.style.visibility = 'visible';
17647 var pn = this.dom.parentNode;
17648 if(shim.dom.parentNode != pn){
17649 pn.insertBefore(shim.dom, this.dom);
17651 shim.setStyle('z-index', this.getZIndex()-2);
17656 hideShim : function(){
17658 this.shim.setDisplayed(false);
17659 shims.push(this.shim);
17664 disableShadow : function(){
17666 this.shadowDisabled = true;
17667 this.shadow.hide();
17668 this.lastShadowOffset = this.shadowOffset;
17669 this.shadowOffset = 0;
17673 enableShadow : function(show){
17675 this.shadowDisabled = false;
17676 this.shadowOffset = this.lastShadowOffset;
17677 delete this.lastShadowOffset;
17685 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17686 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17687 sync : function(doShow){
17688 var sw = this.shadow;
17689 if(!this.updating && this.isVisible() && (sw || this.useShim)){
17690 var sh = this.getShim();
17692 var w = this.getWidth(),
17693 h = this.getHeight();
17695 var l = this.getLeft(true),
17696 t = this.getTop(true);
17698 if(sw && !this.shadowDisabled){
17699 if(doShow && !sw.isVisible()){
17702 sw.realign(l, t, w, h);
17708 // fit the shim behind the shadow, so it is shimmed too
17709 var a = sw.adjusts, s = sh.dom.style;
17710 s.left = (Math.min(l, l+a.l))+"px";
17711 s.top = (Math.min(t, t+a.t))+"px";
17712 s.width = (w+a.w)+"px";
17713 s.height = (h+a.h)+"px";
17720 sh.setLeftTop(l, t);
17727 destroy : function(){
17730 this.shadow.hide();
17732 this.removeAllListeners();
17733 var pn = this.dom.parentNode;
17735 pn.removeChild(this.dom);
17737 Roo.Element.uncache(this.id);
17740 remove : function(){
17745 beginUpdate : function(){
17746 this.updating = true;
17750 endUpdate : function(){
17751 this.updating = false;
17756 hideUnders : function(negOffset){
17758 this.shadow.hide();
17764 constrainXY : function(){
17765 if(this.constrain){
17766 var vw = Roo.lib.Dom.getViewWidth(),
17767 vh = Roo.lib.Dom.getViewHeight();
17768 var s = Roo.get(document).getScroll();
17770 var xy = this.getXY();
17771 var x = xy[0], y = xy[1];
17772 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17773 // only move it if it needs it
17775 // first validate right/bottom
17776 if((x + w) > vw+s.left){
17777 x = vw - w - this.shadowOffset;
17780 if((y + h) > vh+s.top){
17781 y = vh - h - this.shadowOffset;
17784 // then make sure top/left isn't negative
17795 var ay = this.avoidY;
17796 if(y <= ay && (y+h) >= ay){
17802 supr.setXY.call(this, xy);
17808 isVisible : function(){
17809 return this.visible;
17813 showAction : function(){
17814 this.visible = true; // track visibility to prevent getStyle calls
17815 if(this.useDisplay === true){
17816 this.setDisplayed("");
17817 }else if(this.lastXY){
17818 supr.setXY.call(this, this.lastXY);
17819 }else if(this.lastLT){
17820 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17825 hideAction : function(){
17826 this.visible = false;
17827 if(this.useDisplay === true){
17828 this.setDisplayed(false);
17830 this.setLeftTop(-10000,-10000);
17834 // overridden Element method
17835 setVisible : function(v, a, d, c, e){
17840 var cb = function(){
17845 }.createDelegate(this);
17846 supr.setVisible.call(this, true, true, d, cb, e);
17849 this.hideUnders(true);
17858 }.createDelegate(this);
17860 supr.setVisible.call(this, v, a, d, cb, e);
17869 storeXY : function(xy){
17870 delete this.lastLT;
17874 storeLeftTop : function(left, top){
17875 delete this.lastXY;
17876 this.lastLT = [left, top];
17880 beforeFx : function(){
17881 this.beforeAction();
17882 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17886 afterFx : function(){
17887 Roo.Layer.superclass.afterFx.apply(this, arguments);
17888 this.sync(this.isVisible());
17892 beforeAction : function(){
17893 if(!this.updating && this.shadow){
17894 this.shadow.hide();
17898 // overridden Element method
17899 setLeft : function(left){
17900 this.storeLeftTop(left, this.getTop(true));
17901 supr.setLeft.apply(this, arguments);
17905 setTop : function(top){
17906 this.storeLeftTop(this.getLeft(true), top);
17907 supr.setTop.apply(this, arguments);
17911 setLeftTop : function(left, top){
17912 this.storeLeftTop(left, top);
17913 supr.setLeftTop.apply(this, arguments);
17917 setXY : function(xy, a, d, c, e){
17919 this.beforeAction();
17921 var cb = this.createCB(c);
17922 supr.setXY.call(this, xy, a, d, cb, e);
17929 createCB : function(c){
17940 // overridden Element method
17941 setX : function(x, a, d, c, e){
17942 this.setXY([x, this.getY()], a, d, c, e);
17945 // overridden Element method
17946 setY : function(y, a, d, c, e){
17947 this.setXY([this.getX(), y], a, d, c, e);
17950 // overridden Element method
17951 setSize : function(w, h, a, d, c, e){
17952 this.beforeAction();
17953 var cb = this.createCB(c);
17954 supr.setSize.call(this, w, h, a, d, cb, e);
17960 // overridden Element method
17961 setWidth : function(w, a, d, c, e){
17962 this.beforeAction();
17963 var cb = this.createCB(c);
17964 supr.setWidth.call(this, w, a, d, cb, e);
17970 // overridden Element method
17971 setHeight : function(h, a, d, c, e){
17972 this.beforeAction();
17973 var cb = this.createCB(c);
17974 supr.setHeight.call(this, h, a, d, cb, e);
17980 // overridden Element method
17981 setBounds : function(x, y, w, h, a, d, c, e){
17982 this.beforeAction();
17983 var cb = this.createCB(c);
17985 this.storeXY([x, y]);
17986 supr.setXY.call(this, [x, y]);
17987 supr.setSize.call(this, w, h, a, d, cb, e);
17990 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17996 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17997 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17998 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17999 * @param {Number} zindex The new z-index to set
18000 * @return {this} The Layer
18002 setZIndex : function(zindex){
18003 this.zindex = zindex;
18004 this.setStyle("z-index", zindex + 2);
18006 this.shadow.setZIndex(zindex + 1);
18009 this.shim.setStyle("z-index", zindex);
18014 * Original code for Roojs - LGPL
18015 * <script type="text/javascript">
18019 * @class Roo.XComponent
18020 * A delayed Element creator...
18021 * Or a way to group chunks of interface together.
18022 * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18023 * used in conjunction with XComponent.build() it will create an instance of each element,
18024 * then call addxtype() to build the User interface.
18026 * Mypart.xyx = new Roo.XComponent({
18028 parent : 'Mypart.xyz', // empty == document.element.!!
18032 disabled : function() {}
18034 tree : function() { // return an tree of xtype declared components
18038 xtype : 'NestedLayoutPanel',
18045 * It can be used to build a big heiracy, with parent etc.
18046 * or you can just use this to render a single compoent to a dom element
18047 * MYPART.render(Roo.Element | String(id) | dom_element )
18054 * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18055 * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18057 * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18059 * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18060 * - if mulitple topModules exist, the last one is defined as the top module.
18064 * When the top level or multiple modules are to embedded into a existing HTML page,
18065 * the parent element can container '#id' of the element where the module will be drawn.
18069 * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18070 * it relies more on a include mechanism, where sub modules are included into an outer page.
18071 * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18073 * Bootstrap Roo Included elements
18075 * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18076 * hence confusing the component builder as it thinks there are multiple top level elements.
18078 * String Over-ride & Translations
18080 * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18081 * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18082 * are needed. @see Roo.XComponent.overlayString
18086 * @extends Roo.util.Observable
18088 * @param cfg {Object} configuration of component
18091 Roo.XComponent = function(cfg) {
18092 Roo.apply(this, cfg);
18096 * Fires when this the componnt is built
18097 * @param {Roo.XComponent} c the component
18102 this.region = this.region || 'center'; // default..
18103 Roo.XComponent.register(this);
18104 this.modules = false;
18105 this.el = false; // where the layout goes..
18109 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18112 * The created element (with Roo.factory())
18113 * @type {Roo.Layout}
18119 * for BC - use el in new code
18120 * @type {Roo.Layout}
18126 * for BC - use el in new code
18127 * @type {Roo.Layout}
18132 * @cfg {Function|boolean} disabled
18133 * If this module is disabled by some rule, return true from the funtion
18138 * @cfg {String} parent
18139 * Name of parent element which it get xtype added to..
18144 * @cfg {String} order
18145 * Used to set the order in which elements are created (usefull for multiple tabs)
18150 * @cfg {String} name
18151 * String to display while loading.
18155 * @cfg {String} region
18156 * Region to render component to (defaults to center)
18161 * @cfg {Array} items
18162 * A single item array - the first element is the root of the tree..
18163 * It's done this way to stay compatible with the Xtype system...
18169 * The method that retuns the tree of parts that make up this compoennt
18176 * render element to dom or tree
18177 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18180 render : function(el)
18184 var hp = this.parent ? 1 : 0;
18185 Roo.debug && Roo.log(this);
18187 var tree = this._tree ? this._tree() : this.tree();
18190 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18191 // if parent is a '#.....' string, then let's use that..
18192 var ename = this.parent.substr(1);
18193 this.parent = false;
18194 Roo.debug && Roo.log(ename);
18196 case 'bootstrap-body':
18197 if (typeof(tree.el) != 'undefined' && tree.el == document.body) {
18198 // this is the BorderLayout standard?
18199 this.parent = { el : true };
18202 if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype) > -1) {
18203 // need to insert stuff...
18205 el : new Roo.bootstrap.layout.Border({
18206 el : document.body,
18212 tabPosition: 'top',
18213 //resizeTabs: true,
18214 alwaysShowTabs: true,
18224 if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18225 this.parent = { el : new Roo.bootstrap.Body() };
18226 Roo.debug && Roo.log("setting el to doc body");
18229 throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18233 this.parent = { el : true};
18236 el = Roo.get(ename);
18237 if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18238 this.parent = { el : true};
18245 if (!el && !this.parent) {
18246 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18251 Roo.debug && Roo.log("EL:");
18252 Roo.debug && Roo.log(el);
18253 Roo.debug && Roo.log("this.parent.el:");
18254 Roo.debug && Roo.log(this.parent.el);
18257 // altertive root elements ??? - we need a better way to indicate these.
18258 var is_alt = Roo.XComponent.is_alt ||
18259 (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18260 (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18261 (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18265 if (!this.parent && is_alt) {
18266 //el = Roo.get(document.body);
18267 this.parent = { el : true };
18272 if (!this.parent) {
18274 Roo.debug && Roo.log("no parent - creating one");
18276 el = el ? Roo.get(el) : false;
18278 if (typeof(Roo.BorderLayout) == 'undefined' ) {
18281 el : new Roo.bootstrap.layout.Border({
18282 el: el || document.body,
18288 tabPosition: 'top',
18289 //resizeTabs: true,
18290 alwaysShowTabs: false,
18293 overflow: 'visible'
18299 // it's a top level one..
18301 el : new Roo.BorderLayout(el || document.body, {
18306 tabPosition: 'top',
18307 //resizeTabs: true,
18308 alwaysShowTabs: el && hp? false : true,
18309 hideTabs: el || !hp ? true : false,
18317 if (!this.parent.el) {
18318 // probably an old style ctor, which has been disabled.
18322 // The 'tree' method is '_tree now'
18324 tree.region = tree.region || this.region;
18325 var is_body = false;
18326 if (this.parent.el === true) {
18327 // bootstrap... - body..
18331 this.parent.el = Roo.factory(tree);
18335 this.el = this.parent.el.addxtype(tree, undefined, is_body);
18336 this.fireEvent('built', this);
18338 this.panel = this.el;
18339 this.layout = this.panel.layout;
18340 this.parentLayout = this.parent.layout || false;
18346 Roo.apply(Roo.XComponent, {
18348 * @property hideProgress
18349 * true to disable the building progress bar.. usefull on single page renders.
18352 hideProgress : false,
18354 * @property buildCompleted
18355 * True when the builder has completed building the interface.
18358 buildCompleted : false,
18361 * @property topModule
18362 * the upper most module - uses document.element as it's constructor.
18369 * @property modules
18370 * array of modules to be created by registration system.
18371 * @type {Array} of Roo.XComponent
18376 * @property elmodules
18377 * array of modules to be created by which use #ID
18378 * @type {Array} of Roo.XComponent
18385 * Is an alternative Root - normally used by bootstrap or other systems,
18386 * where the top element in the tree can wrap 'body'
18387 * @type {boolean} (default false)
18392 * @property build_from_html
18393 * Build elements from html - used by bootstrap HTML stuff
18394 * - this is cleared after build is completed
18395 * @type {boolean} (default false)
18398 build_from_html : false,
18400 * Register components to be built later.
18402 * This solves the following issues
18403 * - Building is not done on page load, but after an authentication process has occured.
18404 * - Interface elements are registered on page load
18405 * - Parent Interface elements may not be loaded before child, so this handles that..
18412 module : 'Pman.Tab.projectMgr',
18414 parent : 'Pman.layout',
18415 disabled : false, // or use a function..
18418 * * @param {Object} details about module
18420 register : function(obj) {
18422 Roo.XComponent.event.fireEvent('register', obj);
18423 switch(typeof(obj.disabled) ) {
18429 if ( obj.disabled() ) {
18435 if (obj.disabled || obj.region == '#disabled') {
18441 this.modules.push(obj);
18445 * convert a string to an object..
18446 * eg. 'AAA.BBB' -> finds AAA.BBB
18450 toObject : function(str)
18452 if (!str || typeof(str) == 'object') {
18455 if (str.substring(0,1) == '#') {
18459 var ar = str.split('.');
18464 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18466 throw "Module not found : " + str;
18470 throw "Module not found : " + str;
18472 Roo.each(ar, function(e) {
18473 if (typeof(o[e]) == 'undefined') {
18474 throw "Module not found : " + str;
18485 * move modules into their correct place in the tree..
18488 preBuild : function ()
18491 Roo.each(this.modules , function (obj)
18493 Roo.XComponent.event.fireEvent('beforebuild', obj);
18495 var opar = obj.parent;
18497 obj.parent = this.toObject(opar);
18499 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18504 Roo.debug && Roo.log("GOT top level module");
18505 Roo.debug && Roo.log(obj);
18506 obj.modules = new Roo.util.MixedCollection(false,
18507 function(o) { return o.order + '' }
18509 this.topModule = obj;
18512 // parent is a string (usually a dom element name..)
18513 if (typeof(obj.parent) == 'string') {
18514 this.elmodules.push(obj);
18517 if (obj.parent.constructor != Roo.XComponent) {
18518 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18520 if (!obj.parent.modules) {
18521 obj.parent.modules = new Roo.util.MixedCollection(false,
18522 function(o) { return o.order + '' }
18525 if (obj.parent.disabled) {
18526 obj.disabled = true;
18528 obj.parent.modules.add(obj);
18533 * make a list of modules to build.
18534 * @return {Array} list of modules.
18537 buildOrder : function()
18540 var cmp = function(a,b) {
18541 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18543 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18544 throw "No top level modules to build";
18547 // make a flat list in order of modules to build.
18548 var mods = this.topModule ? [ this.topModule ] : [];
18551 // elmodules (is a list of DOM based modules )
18552 Roo.each(this.elmodules, function(e) {
18554 if (!this.topModule &&
18555 typeof(e.parent) == 'string' &&
18556 e.parent.substring(0,1) == '#' &&
18557 Roo.get(e.parent.substr(1))
18560 _this.topModule = e;
18566 // add modules to their parents..
18567 var addMod = function(m) {
18568 Roo.debug && Roo.log("build Order: add: " + m.name);
18571 if (m.modules && !m.disabled) {
18572 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18573 m.modules.keySort('ASC', cmp );
18574 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18576 m.modules.each(addMod);
18578 Roo.debug && Roo.log("build Order: no child modules");
18580 // not sure if this is used any more..
18582 m.finalize.name = m.name + " (clean up) ";
18583 mods.push(m.finalize);
18587 if (this.topModule && this.topModule.modules) {
18588 this.topModule.modules.keySort('ASC', cmp );
18589 this.topModule.modules.each(addMod);
18595 * Build the registered modules.
18596 * @param {Object} parent element.
18597 * @param {Function} optional method to call after module has been added.
18601 build : function(opts)
18604 if (typeof(opts) != 'undefined') {
18605 Roo.apply(this,opts);
18609 var mods = this.buildOrder();
18611 //this.allmods = mods;
18612 //Roo.debug && Roo.log(mods);
18614 if (!mods.length) { // should not happen
18615 throw "NO modules!!!";
18619 var msg = "Building Interface...";
18620 // flash it up as modal - so we store the mask!?
18621 if (!this.hideProgress && Roo.MessageBox) {
18622 Roo.MessageBox.show({ title: 'loading' });
18623 Roo.MessageBox.show({
18624 title: "Please wait...",
18634 var total = mods.length;
18637 var progressRun = function() {
18638 if (!mods.length) {
18639 Roo.debug && Roo.log('hide?');
18640 if (!this.hideProgress && Roo.MessageBox) {
18641 Roo.MessageBox.hide();
18643 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18645 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18651 var m = mods.shift();
18654 Roo.debug && Roo.log(m);
18655 // not sure if this is supported any more.. - modules that are are just function
18656 if (typeof(m) == 'function') {
18658 return progressRun.defer(10, _this);
18662 msg = "Building Interface " + (total - mods.length) +
18664 (m.name ? (' - ' + m.name) : '');
18665 Roo.debug && Roo.log(msg);
18666 if (!_this.hideProgress && Roo.MessageBox) {
18667 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
18671 // is the module disabled?
18672 var disabled = (typeof(m.disabled) == 'function') ?
18673 m.disabled.call(m.module.disabled) : m.disabled;
18677 return progressRun(); // we do not update the display!
18685 // it's 10 on top level, and 1 on others??? why...
18686 return progressRun.defer(10, _this);
18689 progressRun.defer(1, _this);
18695 * Overlay a set of modified strings onto a component
18696 * This is dependant on our builder exporting the strings and 'named strings' elements.
18698 * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18699 * @param {Object} associative array of 'named' string and it's new value.
18702 overlayStrings : function( component, strings )
18704 if (typeof(component['_named_strings']) == 'undefined') {
18705 throw "ERROR: component does not have _named_strings";
18707 for ( var k in strings ) {
18708 var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18709 if (md !== false) {
18710 component['_strings'][md] = strings[k];
18712 Roo.log('could not find named string: ' + k + ' in');
18713 Roo.log(component);
18728 * wrapper for event.on - aliased later..
18729 * Typically use to register a event handler for register:
18731 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18740 Roo.XComponent.event = new Roo.util.Observable({
18744 * Fires when an Component is registered,
18745 * set the disable property on the Component to stop registration.
18746 * @param {Roo.XComponent} c the component being registerd.
18751 * @event beforebuild
18752 * Fires before each Component is built
18753 * can be used to apply permissions.
18754 * @param {Roo.XComponent} c the component being registerd.
18757 'beforebuild' : true,
18759 * @event buildcomplete
18760 * Fires on the top level element when all elements have been built
18761 * @param {Roo.XComponent} the top level component.
18763 'buildcomplete' : true
18768 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
18771 * marked - a markdown parser
18772 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18773 * https://github.com/chjj/marked
18779 * Roo.Markdown - is a very crude wrapper around marked..
18783 * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18785 * Note: move the sample code to the bottom of this
18786 * file before uncommenting it.
18791 Roo.Markdown.toHtml = function(text) {
18793 var c = new Roo.Markdown.marked.setOptions({
18794 renderer: new Roo.Markdown.marked.Renderer(),
18805 text = text.replace(/\\\n/g,' ');
18806 return Roo.Markdown.marked(text);
18811 // Wraps all "globals" so that the only thing
18812 // exposed is makeHtml().
18818 * eval:var:unescape
18826 var escape = function (html, encode) {
18828 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18829 .replace(/</g, '<')
18830 .replace(/>/g, '>')
18831 .replace(/"/g, '"')
18832 .replace(/'/g, ''');
18835 var unescape = function (html) {
18836 // explicitly match decimal, hex, and named HTML entities
18837 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18838 n = n.toLowerCase();
18839 if (n === 'colon') { return ':'; }
18840 if (n.charAt(0) === '#') {
18841 return n.charAt(1) === 'x'
18842 ? String.fromCharCode(parseInt(n.substring(2), 16))
18843 : String.fromCharCode(+n.substring(1));
18849 var replace = function (regex, opt) {
18850 regex = regex.source;
18852 return function self(name, val) {
18853 if (!name) { return new RegExp(regex, opt); }
18854 val = val.source || val;
18855 val = val.replace(/(^|[^\[])\^/g, '$1');
18856 regex = regex.replace(name, val);
18865 var noop = function () {}
18871 var merge = function (obj) {
18876 for (; i < arguments.length; i++) {
18877 target = arguments[i];
18878 for (key in target) {
18879 if (Object.prototype.hasOwnProperty.call(target, key)) {
18880 obj[key] = target[key];
18890 * Block-Level Grammar
18898 code: /^( {4}[^\n]+\n*)+/,
18900 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18901 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18903 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18904 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18905 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18906 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18907 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18909 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18913 block.bullet = /(?:[*+-]|\d+\.)/;
18914 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18915 block.item = replace(block.item, 'gm')
18916 (/bull/g, block.bullet)
18919 block.list = replace(block.list)
18920 (/bull/g, block.bullet)
18921 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18922 ('def', '\\n+(?=' + block.def.source + ')')
18925 block.blockquote = replace(block.blockquote)
18929 block._tag = '(?!(?:'
18930 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18931 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18932 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18934 block.html = replace(block.html)
18935 ('comment', /<!--[\s\S]*?-->/)
18936 ('closed', /<(tag)[\s\S]+?<\/\1>/)
18937 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18938 (/tag/g, block._tag)
18941 block.paragraph = replace(block.paragraph)
18943 ('heading', block.heading)
18944 ('lheading', block.lheading)
18945 ('blockquote', block.blockquote)
18946 ('tag', '<' + block._tag)
18951 * Normal Block Grammar
18954 block.normal = merge({}, block);
18957 * GFM Block Grammar
18960 block.gfm = merge({}, block.normal, {
18961 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18963 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18966 block.gfm.paragraph = replace(block.paragraph)
18968 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18969 + block.list.source.replace('\\1', '\\3') + '|')
18973 * GFM + Tables Block Grammar
18976 block.tables = merge({}, block.gfm, {
18977 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18978 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18985 var Lexer = function (options) {
18987 this.tokens.links = {};
18988 this.options = options || marked.defaults;
18989 this.rules = block.normal;
18991 if (this.options.gfm) {
18992 if (this.options.tables) {
18993 this.rules = block.tables;
18995 this.rules = block.gfm;
19001 * Expose Block Rules
19004 Lexer.rules = block;
19007 * Static Lex Method
19010 Lexer.lex = function(src, options) {
19011 var lexer = new Lexer(options);
19012 return lexer.lex(src);
19019 Lexer.prototype.lex = function(src) {
19021 .replace(/\r\n|\r/g, '\n')
19022 .replace(/\t/g, ' ')
19023 .replace(/\u00a0/g, ' ')
19024 .replace(/\u2424/g, '\n');
19026 return this.token(src, true);
19033 Lexer.prototype.token = function(src, top, bq) {
19034 var src = src.replace(/^ +$/gm, '')
19047 if (cap = this.rules.newline.exec(src)) {
19048 src = src.substring(cap[0].length);
19049 if (cap[0].length > 1) {
19057 if (cap = this.rules.code.exec(src)) {
19058 src = src.substring(cap[0].length);
19059 cap = cap[0].replace(/^ {4}/gm, '');
19062 text: !this.options.pedantic
19063 ? cap.replace(/\n+$/, '')
19070 if (cap = this.rules.fences.exec(src)) {
19071 src = src.substring(cap[0].length);
19081 if (cap = this.rules.heading.exec(src)) {
19082 src = src.substring(cap[0].length);
19085 depth: cap[1].length,
19091 // table no leading pipe (gfm)
19092 if (top && (cap = this.rules.nptable.exec(src))) {
19093 src = src.substring(cap[0].length);
19097 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19098 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19099 cells: cap[3].replace(/\n$/, '').split('\n')
19102 for (i = 0; i < item.align.length; i++) {
19103 if (/^ *-+: *$/.test(item.align[i])) {
19104 item.align[i] = 'right';
19105 } else if (/^ *:-+: *$/.test(item.align[i])) {
19106 item.align[i] = 'center';
19107 } else if (/^ *:-+ *$/.test(item.align[i])) {
19108 item.align[i] = 'left';
19110 item.align[i] = null;
19114 for (i = 0; i < item.cells.length; i++) {
19115 item.cells[i] = item.cells[i].split(/ *\| */);
19118 this.tokens.push(item);
19124 if (cap = this.rules.lheading.exec(src)) {
19125 src = src.substring(cap[0].length);
19128 depth: cap[2] === '=' ? 1 : 2,
19135 if (cap = this.rules.hr.exec(src)) {
19136 src = src.substring(cap[0].length);
19144 if (cap = this.rules.blockquote.exec(src)) {
19145 src = src.substring(cap[0].length);
19148 type: 'blockquote_start'
19151 cap = cap[0].replace(/^ *> ?/gm, '');
19153 // Pass `top` to keep the current
19154 // "toplevel" state. This is exactly
19155 // how markdown.pl works.
19156 this.token(cap, top, true);
19159 type: 'blockquote_end'
19166 if (cap = this.rules.list.exec(src)) {
19167 src = src.substring(cap[0].length);
19171 type: 'list_start',
19172 ordered: bull.length > 1
19175 // Get each top-level item.
19176 cap = cap[0].match(this.rules.item);
19182 for (; i < l; i++) {
19185 // Remove the list item's bullet
19186 // so it is seen as the next token.
19187 space = item.length;
19188 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19190 // Outdent whatever the
19191 // list item contains. Hacky.
19192 if (~item.indexOf('\n ')) {
19193 space -= item.length;
19194 item = !this.options.pedantic
19195 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19196 : item.replace(/^ {1,4}/gm, '');
19199 // Determine whether the next list item belongs here.
19200 // Backpedal if it does not belong in this list.
19201 if (this.options.smartLists && i !== l - 1) {
19202 b = block.bullet.exec(cap[i + 1])[0];
19203 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19204 src = cap.slice(i + 1).join('\n') + src;
19209 // Determine whether item is loose or not.
19210 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19211 // for discount behavior.
19212 loose = next || /\n\n(?!\s*$)/.test(item);
19214 next = item.charAt(item.length - 1) === '\n';
19215 if (!loose) { loose = next; }
19220 ? 'loose_item_start'
19221 : 'list_item_start'
19225 this.token(item, false, bq);
19228 type: 'list_item_end'
19240 if (cap = this.rules.html.exec(src)) {
19241 src = src.substring(cap[0].length);
19243 type: this.options.sanitize
19246 pre: !this.options.sanitizer
19247 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19254 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19255 src = src.substring(cap[0].length);
19256 this.tokens.links[cap[1].toLowerCase()] = {
19264 if (top && (cap = this.rules.table.exec(src))) {
19265 src = src.substring(cap[0].length);
19269 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19270 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19271 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19274 for (i = 0; i < item.align.length; i++) {
19275 if (/^ *-+: *$/.test(item.align[i])) {
19276 item.align[i] = 'right';
19277 } else if (/^ *:-+: *$/.test(item.align[i])) {
19278 item.align[i] = 'center';
19279 } else if (/^ *:-+ *$/.test(item.align[i])) {
19280 item.align[i] = 'left';
19282 item.align[i] = null;
19286 for (i = 0; i < item.cells.length; i++) {
19287 item.cells[i] = item.cells[i]
19288 .replace(/^ *\| *| *\| *$/g, '')
19292 this.tokens.push(item);
19297 // top-level paragraph
19298 if (top && (cap = this.rules.paragraph.exec(src))) {
19299 src = src.substring(cap[0].length);
19302 text: cap[1].charAt(cap[1].length - 1) === '\n'
19303 ? cap[1].slice(0, -1)
19310 if (cap = this.rules.text.exec(src)) {
19311 // Top-level should never reach here.
19312 src = src.substring(cap[0].length);
19322 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19326 return this.tokens;
19330 * Inline-Level Grammar
19334 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19335 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19337 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19338 link: /^!?\[(inside)\]\(href\)/,
19339 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19340 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19341 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19342 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19343 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19344 br: /^ {2,}\n(?!\s*$)/,
19346 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19349 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19350 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19352 inline.link = replace(inline.link)
19353 ('inside', inline._inside)
19354 ('href', inline._href)
19357 inline.reflink = replace(inline.reflink)
19358 ('inside', inline._inside)
19362 * Normal Inline Grammar
19365 inline.normal = merge({}, inline);
19368 * Pedantic Inline Grammar
19371 inline.pedantic = merge({}, inline.normal, {
19372 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19373 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19377 * GFM Inline Grammar
19380 inline.gfm = merge({}, inline.normal, {
19381 escape: replace(inline.escape)('])', '~|])')(),
19382 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19383 del: /^~~(?=\S)([\s\S]*?\S)~~/,
19384 text: replace(inline.text)
19386 ('|', '|https?://|')
19391 * GFM + Line Breaks Inline Grammar
19394 inline.breaks = merge({}, inline.gfm, {
19395 br: replace(inline.br)('{2,}', '*')(),
19396 text: replace(inline.gfm.text)('{2,}', '*')()
19400 * Inline Lexer & Compiler
19403 var InlineLexer = function (links, options) {
19404 this.options = options || marked.defaults;
19405 this.links = links;
19406 this.rules = inline.normal;
19407 this.renderer = this.options.renderer || new Renderer;
19408 this.renderer.options = this.options;
19412 Error('Tokens array requires a `links` property.');
19415 if (this.options.gfm) {
19416 if (this.options.breaks) {
19417 this.rules = inline.breaks;
19419 this.rules = inline.gfm;
19421 } else if (this.options.pedantic) {
19422 this.rules = inline.pedantic;
19427 * Expose Inline Rules
19430 InlineLexer.rules = inline;
19433 * Static Lexing/Compiling Method
19436 InlineLexer.output = function(src, links, options) {
19437 var inline = new InlineLexer(links, options);
19438 return inline.output(src);
19445 InlineLexer.prototype.output = function(src) {
19454 if (cap = this.rules.escape.exec(src)) {
19455 src = src.substring(cap[0].length);
19461 if (cap = this.rules.autolink.exec(src)) {
19462 src = src.substring(cap[0].length);
19463 if (cap[2] === '@') {
19464 text = cap[1].charAt(6) === ':'
19465 ? this.mangle(cap[1].substring(7))
19466 : this.mangle(cap[1]);
19467 href = this.mangle('mailto:') + text;
19469 text = escape(cap[1]);
19472 out += this.renderer.link(href, null, text);
19477 if (!this.inLink && (cap = this.rules.url.exec(src))) {
19478 src = src.substring(cap[0].length);
19479 text = escape(cap[1]);
19481 out += this.renderer.link(href, null, text);
19486 if (cap = this.rules.tag.exec(src)) {
19487 if (!this.inLink && /^<a /i.test(cap[0])) {
19488 this.inLink = true;
19489 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19490 this.inLink = false;
19492 src = src.substring(cap[0].length);
19493 out += this.options.sanitize
19494 ? this.options.sanitizer
19495 ? this.options.sanitizer(cap[0])
19502 if (cap = this.rules.link.exec(src)) {
19503 src = src.substring(cap[0].length);
19504 this.inLink = true;
19505 out += this.outputLink(cap, {
19509 this.inLink = false;
19514 if ((cap = this.rules.reflink.exec(src))
19515 || (cap = this.rules.nolink.exec(src))) {
19516 src = src.substring(cap[0].length);
19517 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19518 link = this.links[link.toLowerCase()];
19519 if (!link || !link.href) {
19520 out += cap[0].charAt(0);
19521 src = cap[0].substring(1) + src;
19524 this.inLink = true;
19525 out += this.outputLink(cap, link);
19526 this.inLink = false;
19531 if (cap = this.rules.strong.exec(src)) {
19532 src = src.substring(cap[0].length);
19533 out += this.renderer.strong(this.output(cap[2] || cap[1]));
19538 if (cap = this.rules.em.exec(src)) {
19539 src = src.substring(cap[0].length);
19540 out += this.renderer.em(this.output(cap[2] || cap[1]));
19545 if (cap = this.rules.code.exec(src)) {
19546 src = src.substring(cap[0].length);
19547 out += this.renderer.codespan(escape(cap[2], true));
19552 if (cap = this.rules.br.exec(src)) {
19553 src = src.substring(cap[0].length);
19554 out += this.renderer.br();
19559 if (cap = this.rules.del.exec(src)) {
19560 src = src.substring(cap[0].length);
19561 out += this.renderer.del(this.output(cap[1]));
19566 if (cap = this.rules.text.exec(src)) {
19567 src = src.substring(cap[0].length);
19568 out += this.renderer.text(escape(this.smartypants(cap[0])));
19574 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19585 InlineLexer.prototype.outputLink = function(cap, link) {
19586 var href = escape(link.href)
19587 , title = link.title ? escape(link.title) : null;
19589 return cap[0].charAt(0) !== '!'
19590 ? this.renderer.link(href, title, this.output(cap[1]))
19591 : this.renderer.image(href, title, escape(cap[1]));
19595 * Smartypants Transformations
19598 InlineLexer.prototype.smartypants = function(text) {
19599 if (!this.options.smartypants) { return text; }
19602 .replace(/---/g, '\u2014')
19604 .replace(/--/g, '\u2013')
19606 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19607 // closing singles & apostrophes
19608 .replace(/'/g, '\u2019')
19610 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19612 .replace(/"/g, '\u201d')
19614 .replace(/\.{3}/g, '\u2026');
19621 InlineLexer.prototype.mangle = function(text) {
19622 if (!this.options.mangle) { return text; }
19628 for (; i < l; i++) {
19629 ch = text.charCodeAt(i);
19630 if (Math.random() > 0.5) {
19631 ch = 'x' + ch.toString(16);
19633 out += '&#' + ch + ';';
19644 * eval:var:Renderer
19647 var Renderer = function (options) {
19648 this.options = options || {};
19651 Renderer.prototype.code = function(code, lang, escaped) {
19652 if (this.options.highlight) {
19653 var out = this.options.highlight(code, lang);
19654 if (out != null && out !== code) {
19659 // hack!!! - it's already escapeD?
19664 return '<pre><code>'
19665 + (escaped ? code : escape(code, true))
19666 + '\n</code></pre>';
19669 return '<pre><code class="'
19670 + this.options.langPrefix
19671 + escape(lang, true)
19673 + (escaped ? code : escape(code, true))
19674 + '\n</code></pre>\n';
19677 Renderer.prototype.blockquote = function(quote) {
19678 return '<blockquote>\n' + quote + '</blockquote>\n';
19681 Renderer.prototype.html = function(html) {
19685 Renderer.prototype.heading = function(text, level, raw) {
19689 + this.options.headerPrefix
19690 + raw.toLowerCase().replace(/[^\w]+/g, '-')
19698 Renderer.prototype.hr = function() {
19699 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19702 Renderer.prototype.list = function(body, ordered) {
19703 var type = ordered ? 'ol' : 'ul';
19704 return '<' + type + '>\n' + body + '</' + type + '>\n';
19707 Renderer.prototype.listitem = function(text) {
19708 return '<li>' + text + '</li>\n';
19711 Renderer.prototype.paragraph = function(text) {
19712 return '<p>' + text + '</p>\n';
19715 Renderer.prototype.table = function(header, body) {
19716 return '<table class="table table-striped">\n'
19726 Renderer.prototype.tablerow = function(content) {
19727 return '<tr>\n' + content + '</tr>\n';
19730 Renderer.prototype.tablecell = function(content, flags) {
19731 var type = flags.header ? 'th' : 'td';
19732 var tag = flags.align
19733 ? '<' + type + ' style="text-align:' + flags.align + '">'
19734 : '<' + type + '>';
19735 return tag + content + '</' + type + '>\n';
19738 // span level renderer
19739 Renderer.prototype.strong = function(text) {
19740 return '<strong>' + text + '</strong>';
19743 Renderer.prototype.em = function(text) {
19744 return '<em>' + text + '</em>';
19747 Renderer.prototype.codespan = function(text) {
19748 return '<code>' + text + '</code>';
19751 Renderer.prototype.br = function() {
19752 return this.options.xhtml ? '<br/>' : '<br>';
19755 Renderer.prototype.del = function(text) {
19756 return '<del>' + text + '</del>';
19759 Renderer.prototype.link = function(href, title, text) {
19760 if (this.options.sanitize) {
19762 var prot = decodeURIComponent(unescape(href))
19763 .replace(/[^\w:]/g, '')
19768 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19772 var out = '<a href="' + href + '"';
19774 out += ' title="' + title + '"';
19776 out += '>' + text + '</a>';
19780 Renderer.prototype.image = function(href, title, text) {
19781 var out = '<img src="' + href + '" alt="' + text + '"';
19783 out += ' title="' + title + '"';
19785 out += this.options.xhtml ? '/>' : '>';
19789 Renderer.prototype.text = function(text) {
19794 * Parsing & Compiling
19800 var Parser= function (options) {
19803 this.options = options || marked.defaults;
19804 this.options.renderer = this.options.renderer || new Renderer;
19805 this.renderer = this.options.renderer;
19806 this.renderer.options = this.options;
19810 * Static Parse Method
19813 Parser.parse = function(src, options, renderer) {
19814 var parser = new Parser(options, renderer);
19815 return parser.parse(src);
19822 Parser.prototype.parse = function(src) {
19823 this.inline = new InlineLexer(src.links, this.options, this.renderer);
19824 this.tokens = src.reverse();
19827 while (this.next()) {
19838 Parser.prototype.next = function() {
19839 return this.token = this.tokens.pop();
19843 * Preview Next Token
19846 Parser.prototype.peek = function() {
19847 return this.tokens[this.tokens.length - 1] || 0;
19851 * Parse Text Tokens
19854 Parser.prototype.parseText = function() {
19855 var body = this.token.text;
19857 while (this.peek().type === 'text') {
19858 body += '\n' + this.next().text;
19861 return this.inline.output(body);
19865 * Parse Current Token
19868 Parser.prototype.tok = function() {
19869 switch (this.token.type) {
19874 return this.renderer.hr();
19877 return this.renderer.heading(
19878 this.inline.output(this.token.text),
19883 return this.renderer.code(this.token.text,
19885 this.token.escaped);
19898 for (i = 0; i < this.token.header.length; i++) {
19899 flags = { header: true, align: this.token.align[i] };
19900 cell += this.renderer.tablecell(
19901 this.inline.output(this.token.header[i]),
19902 { header: true, align: this.token.align[i] }
19905 header += this.renderer.tablerow(cell);
19907 for (i = 0; i < this.token.cells.length; i++) {
19908 row = this.token.cells[i];
19911 for (j = 0; j < row.length; j++) {
19912 cell += this.renderer.tablecell(
19913 this.inline.output(row[j]),
19914 { header: false, align: this.token.align[j] }
19918 body += this.renderer.tablerow(cell);
19920 return this.renderer.table(header, body);
19922 case 'blockquote_start': {
19925 while (this.next().type !== 'blockquote_end') {
19926 body += this.tok();
19929 return this.renderer.blockquote(body);
19931 case 'list_start': {
19933 , ordered = this.token.ordered;
19935 while (this.next().type !== 'list_end') {
19936 body += this.tok();
19939 return this.renderer.list(body, ordered);
19941 case 'list_item_start': {
19944 while (this.next().type !== 'list_item_end') {
19945 body += this.token.type === 'text'
19950 return this.renderer.listitem(body);
19952 case 'loose_item_start': {
19955 while (this.next().type !== 'list_item_end') {
19956 body += this.tok();
19959 return this.renderer.listitem(body);
19962 var html = !this.token.pre && !this.options.pedantic
19963 ? this.inline.output(this.token.text)
19965 return this.renderer.html(html);
19967 case 'paragraph': {
19968 return this.renderer.paragraph(this.inline.output(this.token.text));
19971 return this.renderer.paragraph(this.parseText());
19983 var marked = function (src, opt, callback) {
19984 if (callback || typeof opt === 'function') {
19990 opt = merge({}, marked.defaults, opt || {});
19992 var highlight = opt.highlight
19998 tokens = Lexer.lex(src, opt)
20000 return callback(e);
20003 pending = tokens.length;
20007 var done = function(err) {
20009 opt.highlight = highlight;
20010 return callback(err);
20016 out = Parser.parse(tokens, opt);
20021 opt.highlight = highlight;
20025 : callback(null, out);
20028 if (!highlight || highlight.length < 3) {
20032 delete opt.highlight;
20034 if (!pending) { return done(); }
20036 for (; i < tokens.length; i++) {
20038 if (token.type !== 'code') {
20039 return --pending || done();
20041 return highlight(token.text, token.lang, function(err, code) {
20042 if (err) { return done(err); }
20043 if (code == null || code === token.text) {
20044 return --pending || done();
20047 token.escaped = true;
20048 --pending || done();
20056 if (opt) { opt = merge({}, marked.defaults, opt); }
20057 return Parser.parse(Lexer.lex(src, opt), opt);
20059 e.message += '\nPlease report this to https://github.com/chjj/marked.';
20060 if ((opt || marked.defaults).silent) {
20061 return '<p>An error occured:</p><pre>'
20062 + escape(e.message + '', true)
20074 marked.setOptions = function(opt) {
20075 merge(marked.defaults, opt);
20079 marked.defaults = {
20090 langPrefix: 'lang-',
20091 smartypants: false,
20093 renderer: new Renderer,
20101 marked.Parser = Parser;
20102 marked.parser = Parser.parse;
20104 marked.Renderer = Renderer;
20106 marked.Lexer = Lexer;
20107 marked.lexer = Lexer.lex;
20109 marked.InlineLexer = InlineLexer;
20110 marked.inlineLexer = InlineLexer.output;
20112 marked.parse = marked;
20114 Roo.Markdown.marked = marked;
20118 * Ext JS Library 1.1.1
20119 * Copyright(c) 2006-2007, Ext JS, LLC.
20121 * Originally Released Under LGPL - original licence link has changed is not relivant.
20124 * <script type="text/javascript">
20130 * These classes are derivatives of the similarly named classes in the YUI Library.
20131 * The original license:
20132 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20133 * Code licensed under the BSD License:
20134 * http://developer.yahoo.net/yui/license.txt
20139 var Event=Roo.EventManager;
20140 var Dom=Roo.lib.Dom;
20143 * @class Roo.dd.DragDrop
20144 * @extends Roo.util.Observable
20145 * Defines the interface and base operation of items that that can be
20146 * dragged or can be drop targets. It was designed to be extended, overriding
20147 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20148 * Up to three html elements can be associated with a DragDrop instance:
20150 * <li>linked element: the element that is passed into the constructor.
20151 * This is the element which defines the boundaries for interaction with
20152 * other DragDrop objects.</li>
20153 * <li>handle element(s): The drag operation only occurs if the element that
20154 * was clicked matches a handle element. By default this is the linked
20155 * element, but there are times that you will want only a portion of the
20156 * linked element to initiate the drag operation, and the setHandleElId()
20157 * method provides a way to define this.</li>
20158 * <li>drag element: this represents the element that would be moved along
20159 * with the cursor during a drag operation. By default, this is the linked
20160 * element itself as in {@link Roo.dd.DD}. setDragElId() lets you define
20161 * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20164 * This class should not be instantiated until the onload event to ensure that
20165 * the associated elements are available.
20166 * The following would define a DragDrop obj that would interact with any
20167 * other DragDrop obj in the "group1" group:
20169 * dd = new Roo.dd.DragDrop("div1", "group1");
20171 * Since none of the event handlers have been implemented, nothing would
20172 * actually happen if you were to run the code above. Normally you would
20173 * override this class or one of the default implementations, but you can
20174 * also override the methods you want on an instance of the class...
20176 * dd.onDragDrop = function(e, id) {
20177 * alert("dd was dropped on " + id);
20181 * @param {String} id of the element that is linked to this instance
20182 * @param {String} sGroup the group of related DragDrop objects
20183 * @param {object} config an object containing configurable attributes
20184 * Valid properties for DragDrop:
20185 * padding, isTarget, maintainOffset, primaryButtonOnly
20187 Roo.dd.DragDrop = function(id, sGroup, config) {
20189 this.init(id, sGroup, config);
20194 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20197 * The id of the element associated with this object. This is what we
20198 * refer to as the "linked element" because the size and position of
20199 * this element is used to determine when the drag and drop objects have
20207 * Configuration attributes passed into the constructor
20214 * The id of the element that will be dragged. By default this is same
20215 * as the linked element , but could be changed to another element. Ex:
20217 * @property dragElId
20224 * the id of the element that initiates the drag operation. By default
20225 * this is the linked element, but could be changed to be a child of this
20226 * element. This lets us do things like only starting the drag when the
20227 * header element within the linked html element is clicked.
20228 * @property handleElId
20235 * An associative array of HTML tags that will be ignored if clicked.
20236 * @property invalidHandleTypes
20237 * @type {string: string}
20239 invalidHandleTypes: null,
20242 * An associative array of ids for elements that will be ignored if clicked
20243 * @property invalidHandleIds
20244 * @type {string: string}
20246 invalidHandleIds: null,
20249 * An indexted array of css class names for elements that will be ignored
20251 * @property invalidHandleClasses
20254 invalidHandleClasses: null,
20257 * The linked element's absolute X position at the time the drag was
20259 * @property startPageX
20266 * The linked element's absolute X position at the time the drag was
20268 * @property startPageY
20275 * The group defines a logical collection of DragDrop objects that are
20276 * related. Instances only get events when interacting with other
20277 * DragDrop object in the same group. This lets us define multiple
20278 * groups using a single DragDrop subclass if we want.
20280 * @type {string: string}
20285 * Individual drag/drop instances can be locked. This will prevent
20286 * onmousedown start drag.
20294 * Lock this instance
20297 lock: function() { this.locked = true; },
20300 * Unlock this instace
20303 unlock: function() { this.locked = false; },
20306 * By default, all insances can be a drop target. This can be disabled by
20307 * setting isTarget to false.
20314 * The padding configured for this drag and drop object for calculating
20315 * the drop zone intersection with this object.
20322 * Cached reference to the linked element
20323 * @property _domRef
20329 * Internal typeof flag
20330 * @property __ygDragDrop
20333 __ygDragDrop: true,
20336 * Set to true when horizontal contraints are applied
20337 * @property constrainX
20344 * Set to true when vertical contraints are applied
20345 * @property constrainY
20352 * The left constraint
20360 * The right constraint
20368 * The up constraint
20377 * The down constraint
20385 * Maintain offsets when we resetconstraints. Set to true when you want
20386 * the position of the element relative to its parent to stay the same
20387 * when the page changes
20389 * @property maintainOffset
20392 maintainOffset: false,
20395 * Array of pixel locations the element will snap to if we specified a
20396 * horizontal graduation/interval. This array is generated automatically
20397 * when you define a tick interval.
20404 * Array of pixel locations the element will snap to if we specified a
20405 * vertical graduation/interval. This array is generated automatically
20406 * when you define a tick interval.
20413 * By default the drag and drop instance will only respond to the primary
20414 * button click (left button for a right-handed mouse). Set to true to
20415 * allow drag and drop to start with any mouse click that is propogated
20417 * @property primaryButtonOnly
20420 primaryButtonOnly: true,
20423 * The availabe property is false until the linked dom element is accessible.
20424 * @property available
20430 * By default, drags can only be initiated if the mousedown occurs in the
20431 * region the linked element is. This is done in part to work around a
20432 * bug in some browsers that mis-report the mousedown if the previous
20433 * mouseup happened outside of the window. This property is set to true
20434 * if outer handles are defined.
20436 * @property hasOuterHandles
20440 hasOuterHandles: false,
20443 * Code that executes immediately before the startDrag event
20444 * @method b4StartDrag
20447 b4StartDrag: function(x, y) { },
20450 * Abstract method called after a drag/drop object is clicked
20451 * and the drag or mousedown time thresholds have beeen met.
20452 * @method startDrag
20453 * @param {int} X click location
20454 * @param {int} Y click location
20456 startDrag: function(x, y) { /* override this */ },
20459 * Code that executes immediately before the onDrag event
20463 b4Drag: function(e) { },
20466 * Abstract method called during the onMouseMove event while dragging an
20469 * @param {Event} e the mousemove event
20471 onDrag: function(e) { /* override this */ },
20474 * Abstract method called when this element fist begins hovering over
20475 * another DragDrop obj
20476 * @method onDragEnter
20477 * @param {Event} e the mousemove event
20478 * @param {String|DragDrop[]} id In POINT mode, the element
20479 * id this is hovering over. In INTERSECT mode, an array of one or more
20480 * dragdrop items being hovered over.
20482 onDragEnter: function(e, id) { /* override this */ },
20485 * Code that executes immediately before the onDragOver event
20486 * @method b4DragOver
20489 b4DragOver: function(e) { },
20492 * Abstract method called when this element is hovering over another
20494 * @method onDragOver
20495 * @param {Event} e the mousemove event
20496 * @param {String|DragDrop[]} id In POINT mode, the element
20497 * id this is hovering over. In INTERSECT mode, an array of dd items
20498 * being hovered over.
20500 onDragOver: function(e, id) { /* override this */ },
20503 * Code that executes immediately before the onDragOut event
20504 * @method b4DragOut
20507 b4DragOut: function(e) { },
20510 * Abstract method called when we are no longer hovering over an element
20511 * @method onDragOut
20512 * @param {Event} e the mousemove event
20513 * @param {String|DragDrop[]} id In POINT mode, the element
20514 * id this was hovering over. In INTERSECT mode, an array of dd items
20515 * that the mouse is no longer over.
20517 onDragOut: function(e, id) { /* override this */ },
20520 * Code that executes immediately before the onDragDrop event
20521 * @method b4DragDrop
20524 b4DragDrop: function(e) { },
20527 * Abstract method called when this item is dropped on another DragDrop
20529 * @method onDragDrop
20530 * @param {Event} e the mouseup event
20531 * @param {String|DragDrop[]} id In POINT mode, the element
20532 * id this was dropped on. In INTERSECT mode, an array of dd items this
20535 onDragDrop: function(e, id) { /* override this */ },
20538 * Abstract method called when this item is dropped on an area with no
20540 * @method onInvalidDrop
20541 * @param {Event} e the mouseup event
20543 onInvalidDrop: function(e) { /* override this */ },
20546 * Code that executes immediately before the endDrag event
20547 * @method b4EndDrag
20550 b4EndDrag: function(e) { },
20553 * Fired when we are done dragging the object
20555 * @param {Event} e the mouseup event
20557 endDrag: function(e) { /* override this */ },
20560 * Code executed immediately before the onMouseDown event
20561 * @method b4MouseDown
20562 * @param {Event} e the mousedown event
20565 b4MouseDown: function(e) { },
20568 * Event handler that fires when a drag/drop obj gets a mousedown
20569 * @method onMouseDown
20570 * @param {Event} e the mousedown event
20572 onMouseDown: function(e) { /* override this */ },
20575 * Event handler that fires when a drag/drop obj gets a mouseup
20576 * @method onMouseUp
20577 * @param {Event} e the mouseup event
20579 onMouseUp: function(e) { /* override this */ },
20582 * Override the onAvailable method to do what is needed after the initial
20583 * position was determined.
20584 * @method onAvailable
20586 onAvailable: function () {
20590 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20593 defaultPadding : {left:0, right:0, top:0, bottom:0},
20596 * Initializes the drag drop object's constraints to restrict movement to a certain element.
20600 var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20601 { dragElId: "existingProxyDiv" });
20602 dd.startDrag = function(){
20603 this.constrainTo("parent-id");
20606 * Or you can initalize it using the {@link Roo.Element} object:
20608 Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20609 startDrag : function(){
20610 this.constrainTo("parent-id");
20614 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20615 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20616 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20617 * an object containing the sides to pad. For example: {right:10, bottom:10}
20618 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20620 constrainTo : function(constrainTo, pad, inContent){
20621 if(typeof pad == "number"){
20622 pad = {left: pad, right:pad, top:pad, bottom:pad};
20624 pad = pad || this.defaultPadding;
20625 var b = Roo.get(this.getEl()).getBox();
20626 var ce = Roo.get(constrainTo);
20627 var s = ce.getScroll();
20628 var c, cd = ce.dom;
20629 if(cd == document.body){
20630 c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20633 c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20637 var topSpace = b.y - c.y;
20638 var leftSpace = b.x - c.x;
20640 this.resetConstraints();
20641 this.setXConstraint(leftSpace - (pad.left||0), // left
20642 c.width - leftSpace - b.width - (pad.right||0) //right
20644 this.setYConstraint(topSpace - (pad.top||0), //top
20645 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20650 * Returns a reference to the linked element
20652 * @return {HTMLElement} the html element
20654 getEl: function() {
20655 if (!this._domRef) {
20656 this._domRef = Roo.getDom(this.id);
20659 return this._domRef;
20663 * Returns a reference to the actual element to drag. By default this is
20664 * the same as the html element, but it can be assigned to another
20665 * element. An example of this can be found in Roo.dd.DDProxy
20666 * @method getDragEl
20667 * @return {HTMLElement} the html element
20669 getDragEl: function() {
20670 return Roo.getDom(this.dragElId);
20674 * Sets up the DragDrop object. Must be called in the constructor of any
20675 * Roo.dd.DragDrop subclass
20677 * @param id the id of the linked element
20678 * @param {String} sGroup the group of related items
20679 * @param {object} config configuration attributes
20681 init: function(id, sGroup, config) {
20682 this.initTarget(id, sGroup, config);
20683 if (!Roo.isTouch) {
20684 Event.on(this.id, "mousedown", this.handleMouseDown, this);
20686 Event.on(this.id, "touchstart", this.handleMouseDown, this);
20687 // Event.on(this.id, "selectstart", Event.preventDefault);
20691 * Initializes Targeting functionality only... the object does not
20692 * get a mousedown handler.
20693 * @method initTarget
20694 * @param id the id of the linked element
20695 * @param {String} sGroup the group of related items
20696 * @param {object} config configuration attributes
20698 initTarget: function(id, sGroup, config) {
20700 // configuration attributes
20701 this.config = config || {};
20703 // create a local reference to the drag and drop manager
20704 this.DDM = Roo.dd.DDM;
20705 // initialize the groups array
20708 // assume that we have an element reference instead of an id if the
20709 // parameter is not a string
20710 if (typeof id !== "string") {
20717 // add to an interaction group
20718 this.addToGroup((sGroup) ? sGroup : "default");
20720 // We don't want to register this as the handle with the manager
20721 // so we just set the id rather than calling the setter.
20722 this.handleElId = id;
20724 // the linked element is the element that gets dragged by default
20725 this.setDragElId(id);
20727 // by default, clicked anchors will not start drag operations.
20728 this.invalidHandleTypes = { A: "A" };
20729 this.invalidHandleIds = {};
20730 this.invalidHandleClasses = [];
20732 this.applyConfig();
20734 this.handleOnAvailable();
20738 * Applies the configuration parameters that were passed into the constructor.
20739 * This is supposed to happen at each level through the inheritance chain. So
20740 * a DDProxy implentation will execute apply config on DDProxy, DD, and
20741 * DragDrop in order to get all of the parameters that are available in
20743 * @method applyConfig
20745 applyConfig: function() {
20747 // configurable properties:
20748 // padding, isTarget, maintainOffset, primaryButtonOnly
20749 this.padding = this.config.padding || [0, 0, 0, 0];
20750 this.isTarget = (this.config.isTarget !== false);
20751 this.maintainOffset = (this.config.maintainOffset);
20752 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20757 * Executed when the linked element is available
20758 * @method handleOnAvailable
20761 handleOnAvailable: function() {
20762 this.available = true;
20763 this.resetConstraints();
20764 this.onAvailable();
20768 * Configures the padding for the target zone in px. Effectively expands
20769 * (or reduces) the virtual object size for targeting calculations.
20770 * Supports css-style shorthand; if only one parameter is passed, all sides
20771 * will have that padding, and if only two are passed, the top and bottom
20772 * will have the first param, the left and right the second.
20773 * @method setPadding
20774 * @param {int} iTop Top pad
20775 * @param {int} iRight Right pad
20776 * @param {int} iBot Bot pad
20777 * @param {int} iLeft Left pad
20779 setPadding: function(iTop, iRight, iBot, iLeft) {
20780 // this.padding = [iLeft, iRight, iTop, iBot];
20781 if (!iRight && 0 !== iRight) {
20782 this.padding = [iTop, iTop, iTop, iTop];
20783 } else if (!iBot && 0 !== iBot) {
20784 this.padding = [iTop, iRight, iTop, iRight];
20786 this.padding = [iTop, iRight, iBot, iLeft];
20791 * Stores the initial placement of the linked element.
20792 * @method setInitialPosition
20793 * @param {int} diffX the X offset, default 0
20794 * @param {int} diffY the Y offset, default 0
20796 setInitPosition: function(diffX, diffY) {
20797 var el = this.getEl();
20799 if (!this.DDM.verifyEl(el)) {
20803 var dx = diffX || 0;
20804 var dy = diffY || 0;
20806 var p = Dom.getXY( el );
20808 this.initPageX = p[0] - dx;
20809 this.initPageY = p[1] - dy;
20811 this.lastPageX = p[0];
20812 this.lastPageY = p[1];
20815 this.setStartPosition(p);
20819 * Sets the start position of the element. This is set when the obj
20820 * is initialized, the reset when a drag is started.
20821 * @method setStartPosition
20822 * @param pos current position (from previous lookup)
20825 setStartPosition: function(pos) {
20826 var p = pos || Dom.getXY( this.getEl() );
20827 this.deltaSetXY = null;
20829 this.startPageX = p[0];
20830 this.startPageY = p[1];
20834 * Add this instance to a group of related drag/drop objects. All
20835 * instances belong to at least one group, and can belong to as many
20836 * groups as needed.
20837 * @method addToGroup
20838 * @param sGroup {string} the name of the group
20840 addToGroup: function(sGroup) {
20841 this.groups[sGroup] = true;
20842 this.DDM.regDragDrop(this, sGroup);
20846 * Remove's this instance from the supplied interaction group
20847 * @method removeFromGroup
20848 * @param {string} sGroup The group to drop
20850 removeFromGroup: function(sGroup) {
20851 if (this.groups[sGroup]) {
20852 delete this.groups[sGroup];
20855 this.DDM.removeDDFromGroup(this, sGroup);
20859 * Allows you to specify that an element other than the linked element
20860 * will be moved with the cursor during a drag
20861 * @method setDragElId
20862 * @param id {string} the id of the element that will be used to initiate the drag
20864 setDragElId: function(id) {
20865 this.dragElId = id;
20869 * Allows you to specify a child of the linked element that should be
20870 * used to initiate the drag operation. An example of this would be if
20871 * you have a content div with text and links. Clicking anywhere in the
20872 * content area would normally start the drag operation. Use this method
20873 * to specify that an element inside of the content div is the element
20874 * that starts the drag operation.
20875 * @method setHandleElId
20876 * @param id {string} the id of the element that will be used to
20877 * initiate the drag.
20879 setHandleElId: function(id) {
20880 if (typeof id !== "string") {
20883 this.handleElId = id;
20884 this.DDM.regHandle(this.id, id);
20888 * Allows you to set an element outside of the linked element as a drag
20890 * @method setOuterHandleElId
20891 * @param id the id of the element that will be used to initiate the drag
20893 setOuterHandleElId: function(id) {
20894 if (typeof id !== "string") {
20897 Event.on(id, "mousedown",
20898 this.handleMouseDown, this);
20899 this.setHandleElId(id);
20901 this.hasOuterHandles = true;
20905 * Remove all drag and drop hooks for this element
20908 unreg: function() {
20909 Event.un(this.id, "mousedown",
20910 this.handleMouseDown);
20911 Event.un(this.id, "touchstart",
20912 this.handleMouseDown);
20913 this._domRef = null;
20914 this.DDM._remove(this);
20917 destroy : function(){
20922 * Returns true if this instance is locked, or the drag drop mgr is locked
20923 * (meaning that all drag/drop is disabled on the page.)
20925 * @return {boolean} true if this obj or all drag/drop is locked, else
20928 isLocked: function() {
20929 return (this.DDM.isLocked() || this.locked);
20933 * Fired when this object is clicked
20934 * @method handleMouseDown
20936 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20939 handleMouseDown: function(e, oDD){
20941 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20942 //Roo.log('not touch/ button !=0');
20945 if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20946 return; // double touch..
20950 if (this.isLocked()) {
20951 //Roo.log('locked');
20955 this.DDM.refreshCache(this.groups);
20956 // Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20957 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20958 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
20959 //Roo.log('no outer handes or not over target');
20962 // Roo.log('check validator');
20963 if (this.clickValidator(e)) {
20964 // Roo.log('validate success');
20965 // set the initial element position
20966 this.setStartPosition();
20969 this.b4MouseDown(e);
20970 this.onMouseDown(e);
20972 this.DDM.handleMouseDown(e, this);
20974 this.DDM.stopEvent(e);
20982 clickValidator: function(e) {
20983 var target = e.getTarget();
20984 return ( this.isValidHandleChild(target) &&
20985 (this.id == this.handleElId ||
20986 this.DDM.handleWasClicked(target, this.id)) );
20990 * Allows you to specify a tag name that should not start a drag operation
20991 * when clicked. This is designed to facilitate embedding links within a
20992 * drag handle that do something other than start the drag.
20993 * @method addInvalidHandleType
20994 * @param {string} tagName the type of element to exclude
20996 addInvalidHandleType: function(tagName) {
20997 var type = tagName.toUpperCase();
20998 this.invalidHandleTypes[type] = type;
21002 * Lets you to specify an element id for a child of a drag handle
21003 * that should not initiate a drag
21004 * @method addInvalidHandleId
21005 * @param {string} id the element id of the element you wish to ignore
21007 addInvalidHandleId: function(id) {
21008 if (typeof id !== "string") {
21011 this.invalidHandleIds[id] = id;
21015 * Lets you specify a css class of elements that will not initiate a drag
21016 * @method addInvalidHandleClass
21017 * @param {string} cssClass the class of the elements you wish to ignore
21019 addInvalidHandleClass: function(cssClass) {
21020 this.invalidHandleClasses.push(cssClass);
21024 * Unsets an excluded tag name set by addInvalidHandleType
21025 * @method removeInvalidHandleType
21026 * @param {string} tagName the type of element to unexclude
21028 removeInvalidHandleType: function(tagName) {
21029 var type = tagName.toUpperCase();
21030 // this.invalidHandleTypes[type] = null;
21031 delete this.invalidHandleTypes[type];
21035 * Unsets an invalid handle id
21036 * @method removeInvalidHandleId
21037 * @param {string} id the id of the element to re-enable
21039 removeInvalidHandleId: function(id) {
21040 if (typeof id !== "string") {
21043 delete this.invalidHandleIds[id];
21047 * Unsets an invalid css class
21048 * @method removeInvalidHandleClass
21049 * @param {string} cssClass the class of the element(s) you wish to
21052 removeInvalidHandleClass: function(cssClass) {
21053 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21054 if (this.invalidHandleClasses[i] == cssClass) {
21055 delete this.invalidHandleClasses[i];
21061 * Checks the tag exclusion list to see if this click should be ignored
21062 * @method isValidHandleChild
21063 * @param {HTMLElement} node the HTMLElement to evaluate
21064 * @return {boolean} true if this is a valid tag type, false if not
21066 isValidHandleChild: function(node) {
21069 // var n = (node.nodeName == "#text") ? node.parentNode : node;
21072 nodeName = node.nodeName.toUpperCase();
21074 nodeName = node.nodeName;
21076 valid = valid && !this.invalidHandleTypes[nodeName];
21077 valid = valid && !this.invalidHandleIds[node.id];
21079 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21080 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21089 * Create the array of horizontal tick marks if an interval was specified
21090 * in setXConstraint().
21091 * @method setXTicks
21094 setXTicks: function(iStartX, iTickSize) {
21096 this.xTickSize = iTickSize;
21100 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21102 this.xTicks[this.xTicks.length] = i;
21107 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21109 this.xTicks[this.xTicks.length] = i;
21114 this.xTicks.sort(this.DDM.numericSort) ;
21118 * Create the array of vertical tick marks if an interval was specified in
21119 * setYConstraint().
21120 * @method setYTicks
21123 setYTicks: function(iStartY, iTickSize) {
21125 this.yTickSize = iTickSize;
21129 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21131 this.yTicks[this.yTicks.length] = i;
21136 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21138 this.yTicks[this.yTicks.length] = i;
21143 this.yTicks.sort(this.DDM.numericSort) ;
21147 * By default, the element can be dragged any place on the screen. Use
21148 * this method to limit the horizontal travel of the element. Pass in
21149 * 0,0 for the parameters if you want to lock the drag to the y axis.
21150 * @method setXConstraint
21151 * @param {int} iLeft the number of pixels the element can move to the left
21152 * @param {int} iRight the number of pixels the element can move to the
21154 * @param {int} iTickSize optional parameter for specifying that the
21156 * should move iTickSize pixels at a time.
21158 setXConstraint: function(iLeft, iRight, iTickSize) {
21159 this.leftConstraint = iLeft;
21160 this.rightConstraint = iRight;
21162 this.minX = this.initPageX - iLeft;
21163 this.maxX = this.initPageX + iRight;
21164 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21166 this.constrainX = true;
21170 * Clears any constraints applied to this instance. Also clears ticks
21171 * since they can't exist independent of a constraint at this time.
21172 * @method clearConstraints
21174 clearConstraints: function() {
21175 this.constrainX = false;
21176 this.constrainY = false;
21181 * Clears any tick interval defined for this instance
21182 * @method clearTicks
21184 clearTicks: function() {
21185 this.xTicks = null;
21186 this.yTicks = null;
21187 this.xTickSize = 0;
21188 this.yTickSize = 0;
21192 * By default, the element can be dragged any place on the screen. Set
21193 * this to limit the vertical travel of the element. Pass in 0,0 for the
21194 * parameters if you want to lock the drag to the x axis.
21195 * @method setYConstraint
21196 * @param {int} iUp the number of pixels the element can move up
21197 * @param {int} iDown the number of pixels the element can move down
21198 * @param {int} iTickSize optional parameter for specifying that the
21199 * element should move iTickSize pixels at a time.
21201 setYConstraint: function(iUp, iDown, iTickSize) {
21202 this.topConstraint = iUp;
21203 this.bottomConstraint = iDown;
21205 this.minY = this.initPageY - iUp;
21206 this.maxY = this.initPageY + iDown;
21207 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21209 this.constrainY = true;
21214 * resetConstraints must be called if you manually reposition a dd element.
21215 * @method resetConstraints
21216 * @param {boolean} maintainOffset
21218 resetConstraints: function() {
21221 // Maintain offsets if necessary
21222 if (this.initPageX || this.initPageX === 0) {
21223 // figure out how much this thing has moved
21224 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21225 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21227 this.setInitPosition(dx, dy);
21229 // This is the first time we have detected the element's position
21231 this.setInitPosition();
21234 if (this.constrainX) {
21235 this.setXConstraint( this.leftConstraint,
21236 this.rightConstraint,
21240 if (this.constrainY) {
21241 this.setYConstraint( this.topConstraint,
21242 this.bottomConstraint,
21248 * Normally the drag element is moved pixel by pixel, but we can specify
21249 * that it move a number of pixels at a time. This method resolves the
21250 * location when we have it set up like this.
21252 * @param {int} val where we want to place the object
21253 * @param {int[]} tickArray sorted array of valid points
21254 * @return {int} the closest tick
21257 getTick: function(val, tickArray) {
21260 // If tick interval is not defined, it is effectively 1 pixel,
21261 // so we return the value passed to us.
21263 } else if (tickArray[0] >= val) {
21264 // The value is lower than the first tick, so we return the first
21266 return tickArray[0];
21268 for (var i=0, len=tickArray.length; i<len; ++i) {
21270 if (tickArray[next] && tickArray[next] >= val) {
21271 var diff1 = val - tickArray[i];
21272 var diff2 = tickArray[next] - val;
21273 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21277 // The value is larger than the last tick, so we return the last
21279 return tickArray[tickArray.length - 1];
21286 * @return {string} string representation of the dd obj
21288 toString: function() {
21289 return ("DragDrop " + this.id);
21297 * Ext JS Library 1.1.1
21298 * Copyright(c) 2006-2007, Ext JS, LLC.
21300 * Originally Released Under LGPL - original licence link has changed is not relivant.
21303 * <script type="text/javascript">
21308 * The drag and drop utility provides a framework for building drag and drop
21309 * applications. In addition to enabling drag and drop for specific elements,
21310 * the drag and drop elements are tracked by the manager class, and the
21311 * interactions between the various elements are tracked during the drag and
21312 * the implementing code is notified about these important moments.
21315 // Only load the library once. Rewriting the manager class would orphan
21316 // existing drag and drop instances.
21317 if (!Roo.dd.DragDropMgr) {
21320 * @class Roo.dd.DragDropMgr
21321 * DragDropMgr is a singleton that tracks the element interaction for
21322 * all DragDrop items in the window. Generally, you will not call
21323 * this class directly, but it does have helper methods that could
21324 * be useful in your DragDrop implementations.
21327 Roo.dd.DragDropMgr = function() {
21329 var Event = Roo.EventManager;
21334 * Two dimensional Array of registered DragDrop objects. The first
21335 * dimension is the DragDrop item group, the second the DragDrop
21338 * @type {string: string}
21345 * Array of element ids defined as drag handles. Used to determine
21346 * if the element that generated the mousedown event is actually the
21347 * handle and not the html element itself.
21348 * @property handleIds
21349 * @type {string: string}
21356 * the DragDrop object that is currently being dragged
21357 * @property dragCurrent
21365 * the DragDrop object(s) that are being hovered over
21366 * @property dragOvers
21374 * the X distance between the cursor and the object being dragged
21383 * the Y distance between the cursor and the object being dragged
21392 * Flag to determine if we should prevent the default behavior of the
21393 * events we define. By default this is true, but this can be set to
21394 * false if you need the default behavior (not recommended)
21395 * @property preventDefault
21399 preventDefault: true,
21402 * Flag to determine if we should stop the propagation of the events
21403 * we generate. This is true by default but you may want to set it to
21404 * false if the html element contains other features that require the
21406 * @property stopPropagation
21410 stopPropagation: true,
21413 * Internal flag that is set to true when drag and drop has been
21415 * @property initialized
21422 * All drag and drop can be disabled.
21430 * Called the first time an element is registered.
21436 this.initialized = true;
21440 * In point mode, drag and drop interaction is defined by the
21441 * location of the cursor during the drag/drop
21449 * In intersect mode, drag and drop interactio nis defined by the
21450 * overlap of two or more drag and drop objects.
21451 * @property INTERSECT
21458 * The current drag and drop mode. Default: POINT
21466 * Runs method on all drag and drop objects
21467 * @method _execOnAll
21471 _execOnAll: function(sMethod, args) {
21472 for (var i in this.ids) {
21473 for (var j in this.ids[i]) {
21474 var oDD = this.ids[i][j];
21475 if (! this.isTypeOfDD(oDD)) {
21478 oDD[sMethod].apply(oDD, args);
21484 * Drag and drop initialization. Sets up the global event handlers
21489 _onLoad: function() {
21493 if (!Roo.isTouch) {
21494 Event.on(document, "mouseup", this.handleMouseUp, this, true);
21495 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21497 Event.on(document, "touchend", this.handleMouseUp, this, true);
21498 Event.on(document, "touchmove", this.handleMouseMove, this, true);
21500 Event.on(window, "unload", this._onUnload, this, true);
21501 Event.on(window, "resize", this._onResize, this, true);
21502 // Event.on(window, "mouseout", this._test);
21507 * Reset constraints on all drag and drop objs
21508 * @method _onResize
21512 _onResize: function(e) {
21513 this._execOnAll("resetConstraints", []);
21517 * Lock all drag and drop functionality
21521 lock: function() { this.locked = true; },
21524 * Unlock all drag and drop functionality
21528 unlock: function() { this.locked = false; },
21531 * Is drag and drop locked?
21533 * @return {boolean} True if drag and drop is locked, false otherwise.
21536 isLocked: function() { return this.locked; },
21539 * Location cache that is set for all drag drop objects when a drag is
21540 * initiated, cleared when the drag is finished.
21541 * @property locationCache
21548 * Set useCache to false if you want to force object the lookup of each
21549 * drag and drop linked element constantly during a drag.
21550 * @property useCache
21557 * The number of pixels that the mouse needs to move after the
21558 * mousedown before the drag is initiated. Default=3;
21559 * @property clickPixelThresh
21563 clickPixelThresh: 3,
21566 * The number of milliseconds after the mousedown event to initiate the
21567 * drag if we don't get a mouseup event. Default=1000
21568 * @property clickTimeThresh
21572 clickTimeThresh: 350,
21575 * Flag that indicates that either the drag pixel threshold or the
21576 * mousdown time threshold has been met
21577 * @property dragThreshMet
21582 dragThreshMet: false,
21585 * Timeout used for the click time threshold
21586 * @property clickTimeout
21591 clickTimeout: null,
21594 * The X position of the mousedown event stored for later use when a
21595 * drag threshold is met.
21604 * The Y position of the mousedown event stored for later use when a
21605 * drag threshold is met.
21614 * Each DragDrop instance must be registered with the DragDropMgr.
21615 * This is executed in DragDrop.init()
21616 * @method regDragDrop
21617 * @param {DragDrop} oDD the DragDrop object to register
21618 * @param {String} sGroup the name of the group this element belongs to
21621 regDragDrop: function(oDD, sGroup) {
21622 if (!this.initialized) { this.init(); }
21624 if (!this.ids[sGroup]) {
21625 this.ids[sGroup] = {};
21627 this.ids[sGroup][oDD.id] = oDD;
21631 * Removes the supplied dd instance from the supplied group. Executed
21632 * by DragDrop.removeFromGroup, so don't call this function directly.
21633 * @method removeDDFromGroup
21637 removeDDFromGroup: function(oDD, sGroup) {
21638 if (!this.ids[sGroup]) {
21639 this.ids[sGroup] = {};
21642 var obj = this.ids[sGroup];
21643 if (obj && obj[oDD.id]) {
21644 delete obj[oDD.id];
21649 * Unregisters a drag and drop item. This is executed in
21650 * DragDrop.unreg, use that method instead of calling this directly.
21655 _remove: function(oDD) {
21656 for (var g in oDD.groups) {
21657 if (g && this.ids[g][oDD.id]) {
21658 delete this.ids[g][oDD.id];
21661 delete this.handleIds[oDD.id];
21665 * Each DragDrop handle element must be registered. This is done
21666 * automatically when executing DragDrop.setHandleElId()
21667 * @method regHandle
21668 * @param {String} sDDId the DragDrop id this element is a handle for
21669 * @param {String} sHandleId the id of the element that is the drag
21673 regHandle: function(sDDId, sHandleId) {
21674 if (!this.handleIds[sDDId]) {
21675 this.handleIds[sDDId] = {};
21677 this.handleIds[sDDId][sHandleId] = sHandleId;
21681 * Utility function to determine if a given element has been
21682 * registered as a drag drop item.
21683 * @method isDragDrop
21684 * @param {String} id the element id to check
21685 * @return {boolean} true if this element is a DragDrop item,
21689 isDragDrop: function(id) {
21690 return ( this.getDDById(id) ) ? true : false;
21694 * Returns the drag and drop instances that are in all groups the
21695 * passed in instance belongs to.
21696 * @method getRelated
21697 * @param {DragDrop} p_oDD the obj to get related data for
21698 * @param {boolean} bTargetsOnly if true, only return targetable objs
21699 * @return {DragDrop[]} the related instances
21702 getRelated: function(p_oDD, bTargetsOnly) {
21704 for (var i in p_oDD.groups) {
21705 for (j in this.ids[i]) {
21706 var dd = this.ids[i][j];
21707 if (! this.isTypeOfDD(dd)) {
21710 if (!bTargetsOnly || dd.isTarget) {
21711 oDDs[oDDs.length] = dd;
21720 * Returns true if the specified dd target is a legal target for
21721 * the specifice drag obj
21722 * @method isLegalTarget
21723 * @param {DragDrop} the drag obj
21724 * @param {DragDrop} the target
21725 * @return {boolean} true if the target is a legal target for the
21729 isLegalTarget: function (oDD, oTargetDD) {
21730 var targets = this.getRelated(oDD, true);
21731 for (var i=0, len=targets.length;i<len;++i) {
21732 if (targets[i].id == oTargetDD.id) {
21741 * My goal is to be able to transparently determine if an object is
21742 * typeof DragDrop, and the exact subclass of DragDrop. typeof
21743 * returns "object", oDD.constructor.toString() always returns
21744 * "DragDrop" and not the name of the subclass. So for now it just
21745 * evaluates a well-known variable in DragDrop.
21746 * @method isTypeOfDD
21747 * @param {Object} the object to evaluate
21748 * @return {boolean} true if typeof oDD = DragDrop
21751 isTypeOfDD: function (oDD) {
21752 return (oDD && oDD.__ygDragDrop);
21756 * Utility function to determine if a given element has been
21757 * registered as a drag drop handle for the given Drag Drop object.
21759 * @param {String} id the element id to check
21760 * @return {boolean} true if this element is a DragDrop handle, false
21764 isHandle: function(sDDId, sHandleId) {
21765 return ( this.handleIds[sDDId] &&
21766 this.handleIds[sDDId][sHandleId] );
21770 * Returns the DragDrop instance for a given id
21771 * @method getDDById
21772 * @param {String} id the id of the DragDrop object
21773 * @return {DragDrop} the drag drop object, null if it is not found
21776 getDDById: function(id) {
21777 for (var i in this.ids) {
21778 if (this.ids[i][id]) {
21779 return this.ids[i][id];
21786 * Fired after a registered DragDrop object gets the mousedown event.
21787 * Sets up the events required to track the object being dragged
21788 * @method handleMouseDown
21789 * @param {Event} e the event
21790 * @param oDD the DragDrop object being dragged
21794 handleMouseDown: function(e, oDD) {
21796 Roo.QuickTips.disable();
21798 this.currentTarget = e.getTarget();
21800 this.dragCurrent = oDD;
21802 var el = oDD.getEl();
21804 // track start position
21805 this.startX = e.getPageX();
21806 this.startY = e.getPageY();
21808 this.deltaX = this.startX - el.offsetLeft;
21809 this.deltaY = this.startY - el.offsetTop;
21811 this.dragThreshMet = false;
21813 this.clickTimeout = setTimeout(
21815 var DDM = Roo.dd.DDM;
21816 DDM.startDrag(DDM.startX, DDM.startY);
21818 this.clickTimeThresh );
21822 * Fired when either the drag pixel threshol or the mousedown hold
21823 * time threshold has been met.
21824 * @method startDrag
21825 * @param x {int} the X position of the original mousedown
21826 * @param y {int} the Y position of the original mousedown
21829 startDrag: function(x, y) {
21830 clearTimeout(this.clickTimeout);
21831 if (this.dragCurrent) {
21832 this.dragCurrent.b4StartDrag(x, y);
21833 this.dragCurrent.startDrag(x, y);
21835 this.dragThreshMet = true;
21839 * Internal function to handle the mouseup event. Will be invoked
21840 * from the context of the document.
21841 * @method handleMouseUp
21842 * @param {Event} e the event
21846 handleMouseUp: function(e) {
21849 Roo.QuickTips.enable();
21851 if (! this.dragCurrent) {
21855 clearTimeout(this.clickTimeout);
21857 if (this.dragThreshMet) {
21858 this.fireEvents(e, true);
21868 * Utility to stop event propagation and event default, if these
21869 * features are turned on.
21870 * @method stopEvent
21871 * @param {Event} e the event as returned by this.getEvent()
21874 stopEvent: function(e){
21875 if(this.stopPropagation) {
21876 e.stopPropagation();
21879 if (this.preventDefault) {
21880 e.preventDefault();
21885 * Internal function to clean up event handlers after the drag
21886 * operation is complete
21888 * @param {Event} e the event
21892 stopDrag: function(e) {
21893 // Fire the drag end event for the item that was dragged
21894 if (this.dragCurrent) {
21895 if (this.dragThreshMet) {
21896 this.dragCurrent.b4EndDrag(e);
21897 this.dragCurrent.endDrag(e);
21900 this.dragCurrent.onMouseUp(e);
21903 this.dragCurrent = null;
21904 this.dragOvers = {};
21908 * Internal function to handle the mousemove event. Will be invoked
21909 * from the context of the html element.
21911 * @TODO figure out what we can do about mouse events lost when the
21912 * user drags objects beyond the window boundary. Currently we can
21913 * detect this in internet explorer by verifying that the mouse is
21914 * down during the mousemove event. Firefox doesn't give us the
21915 * button state on the mousemove event.
21916 * @method handleMouseMove
21917 * @param {Event} e the event
21921 handleMouseMove: function(e) {
21922 if (! this.dragCurrent) {
21926 // var button = e.which || e.button;
21928 // check for IE mouseup outside of page boundary
21929 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21931 return this.handleMouseUp(e);
21934 if (!this.dragThreshMet) {
21935 var diffX = Math.abs(this.startX - e.getPageX());
21936 var diffY = Math.abs(this.startY - e.getPageY());
21937 if (diffX > this.clickPixelThresh ||
21938 diffY > this.clickPixelThresh) {
21939 this.startDrag(this.startX, this.startY);
21943 if (this.dragThreshMet) {
21944 this.dragCurrent.b4Drag(e);
21945 this.dragCurrent.onDrag(e);
21946 if(!this.dragCurrent.moveOnly){
21947 this.fireEvents(e, false);
21957 * Iterates over all of the DragDrop elements to find ones we are
21958 * hovering over or dropping on
21959 * @method fireEvents
21960 * @param {Event} e the event
21961 * @param {boolean} isDrop is this a drop op or a mouseover op?
21965 fireEvents: function(e, isDrop) {
21966 var dc = this.dragCurrent;
21968 // If the user did the mouse up outside of the window, we could
21969 // get here even though we have ended the drag.
21970 if (!dc || dc.isLocked()) {
21974 var pt = e.getPoint();
21976 // cache the previous dragOver array
21982 var enterEvts = [];
21984 // Check to see if the object(s) we were hovering over is no longer
21985 // being hovered over so we can fire the onDragOut event
21986 for (var i in this.dragOvers) {
21988 var ddo = this.dragOvers[i];
21990 if (! this.isTypeOfDD(ddo)) {
21994 if (! this.isOverTarget(pt, ddo, this.mode)) {
21995 outEvts.push( ddo );
21998 oldOvers[i] = true;
21999 delete this.dragOvers[i];
22002 for (var sGroup in dc.groups) {
22004 if ("string" != typeof sGroup) {
22008 for (i in this.ids[sGroup]) {
22009 var oDD = this.ids[sGroup][i];
22010 if (! this.isTypeOfDD(oDD)) {
22014 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22015 if (this.isOverTarget(pt, oDD, this.mode)) {
22016 // look for drop interactions
22018 dropEvts.push( oDD );
22019 // look for drag enter and drag over interactions
22022 // initial drag over: dragEnter fires
22023 if (!oldOvers[oDD.id]) {
22024 enterEvts.push( oDD );
22025 // subsequent drag overs: dragOver fires
22027 overEvts.push( oDD );
22030 this.dragOvers[oDD.id] = oDD;
22038 if (outEvts.length) {
22039 dc.b4DragOut(e, outEvts);
22040 dc.onDragOut(e, outEvts);
22043 if (enterEvts.length) {
22044 dc.onDragEnter(e, enterEvts);
22047 if (overEvts.length) {
22048 dc.b4DragOver(e, overEvts);
22049 dc.onDragOver(e, overEvts);
22052 if (dropEvts.length) {
22053 dc.b4DragDrop(e, dropEvts);
22054 dc.onDragDrop(e, dropEvts);
22058 // fire dragout events
22060 for (i=0, len=outEvts.length; i<len; ++i) {
22061 dc.b4DragOut(e, outEvts[i].id);
22062 dc.onDragOut(e, outEvts[i].id);
22065 // fire enter events
22066 for (i=0,len=enterEvts.length; i<len; ++i) {
22067 // dc.b4DragEnter(e, oDD.id);
22068 dc.onDragEnter(e, enterEvts[i].id);
22071 // fire over events
22072 for (i=0,len=overEvts.length; i<len; ++i) {
22073 dc.b4DragOver(e, overEvts[i].id);
22074 dc.onDragOver(e, overEvts[i].id);
22077 // fire drop events
22078 for (i=0, len=dropEvts.length; i<len; ++i) {
22079 dc.b4DragDrop(e, dropEvts[i].id);
22080 dc.onDragDrop(e, dropEvts[i].id);
22085 // notify about a drop that did not find a target
22086 if (isDrop && !dropEvts.length) {
22087 dc.onInvalidDrop(e);
22093 * Helper function for getting the best match from the list of drag
22094 * and drop objects returned by the drag and drop events when we are
22095 * in INTERSECT mode. It returns either the first object that the
22096 * cursor is over, or the object that has the greatest overlap with
22097 * the dragged element.
22098 * @method getBestMatch
22099 * @param {DragDrop[]} dds The array of drag and drop objects
22101 * @return {DragDrop} The best single match
22104 getBestMatch: function(dds) {
22106 // Return null if the input is not what we expect
22107 //if (!dds || !dds.length || dds.length == 0) {
22109 // If there is only one item, it wins
22110 //} else if (dds.length == 1) {
22112 var len = dds.length;
22117 // Loop through the targeted items
22118 for (var i=0; i<len; ++i) {
22120 // If the cursor is over the object, it wins. If the
22121 // cursor is over multiple matches, the first one we come
22123 if (dd.cursorIsOver) {
22126 // Otherwise the object with the most overlap wins
22129 winner.overlap.getArea() < dd.overlap.getArea()) {
22140 * Refreshes the cache of the top-left and bottom-right points of the
22141 * drag and drop objects in the specified group(s). This is in the
22142 * format that is stored in the drag and drop instance, so typical
22145 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22149 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22151 * @TODO this really should be an indexed array. Alternatively this
22152 * method could accept both.
22153 * @method refreshCache
22154 * @param {Object} groups an associative array of groups to refresh
22157 refreshCache: function(groups) {
22158 for (var sGroup in groups) {
22159 if ("string" != typeof sGroup) {
22162 for (var i in this.ids[sGroup]) {
22163 var oDD = this.ids[sGroup][i];
22165 if (this.isTypeOfDD(oDD)) {
22166 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22167 var loc = this.getLocation(oDD);
22169 this.locationCache[oDD.id] = loc;
22171 delete this.locationCache[oDD.id];
22172 // this will unregister the drag and drop object if
22173 // the element is not in a usable state
22182 * This checks to make sure an element exists and is in the DOM. The
22183 * main purpose is to handle cases where innerHTML is used to remove
22184 * drag and drop objects from the DOM. IE provides an 'unspecified
22185 * error' when trying to access the offsetParent of such an element
22187 * @param {HTMLElement} el the element to check
22188 * @return {boolean} true if the element looks usable
22191 verifyEl: function(el) {
22196 parent = el.offsetParent;
22199 parent = el.offsetParent;
22210 * Returns a Region object containing the drag and drop element's position
22211 * and size, including the padding configured for it
22212 * @method getLocation
22213 * @param {DragDrop} oDD the drag and drop object to get the
22215 * @return {Roo.lib.Region} a Region object representing the total area
22216 * the element occupies, including any padding
22217 * the instance is configured for.
22220 getLocation: function(oDD) {
22221 if (! this.isTypeOfDD(oDD)) {
22225 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22228 pos= Roo.lib.Dom.getXY(el);
22236 x2 = x1 + el.offsetWidth;
22238 y2 = y1 + el.offsetHeight;
22240 t = y1 - oDD.padding[0];
22241 r = x2 + oDD.padding[1];
22242 b = y2 + oDD.padding[2];
22243 l = x1 - oDD.padding[3];
22245 return new Roo.lib.Region( t, r, b, l );
22249 * Checks the cursor location to see if it over the target
22250 * @method isOverTarget
22251 * @param {Roo.lib.Point} pt The point to evaluate
22252 * @param {DragDrop} oTarget the DragDrop object we are inspecting
22253 * @return {boolean} true if the mouse is over the target
22257 isOverTarget: function(pt, oTarget, intersect) {
22258 // use cache if available
22259 var loc = this.locationCache[oTarget.id];
22260 if (!loc || !this.useCache) {
22261 loc = this.getLocation(oTarget);
22262 this.locationCache[oTarget.id] = loc;
22270 oTarget.cursorIsOver = loc.contains( pt );
22272 // DragDrop is using this as a sanity check for the initial mousedown
22273 // in this case we are done. In POINT mode, if the drag obj has no
22274 // contraints, we are also done. Otherwise we need to evaluate the
22275 // location of the target as related to the actual location of the
22276 // dragged element.
22277 var dc = this.dragCurrent;
22278 if (!dc || !dc.getTargetCoord ||
22279 (!intersect && !dc.constrainX && !dc.constrainY)) {
22280 return oTarget.cursorIsOver;
22283 oTarget.overlap = null;
22285 // Get the current location of the drag element, this is the
22286 // location of the mouse event less the delta that represents
22287 // where the original mousedown happened on the element. We
22288 // need to consider constraints and ticks as well.
22289 var pos = dc.getTargetCoord(pt.x, pt.y);
22291 var el = dc.getDragEl();
22292 var curRegion = new Roo.lib.Region( pos.y,
22293 pos.x + el.offsetWidth,
22294 pos.y + el.offsetHeight,
22297 var overlap = curRegion.intersect(loc);
22300 oTarget.overlap = overlap;
22301 return (intersect) ? true : oTarget.cursorIsOver;
22308 * unload event handler
22309 * @method _onUnload
22313 _onUnload: function(e, me) {
22314 Roo.dd.DragDropMgr.unregAll();
22318 * Cleans up the drag and drop events and objects.
22323 unregAll: function() {
22325 if (this.dragCurrent) {
22327 this.dragCurrent = null;
22330 this._execOnAll("unreg", []);
22332 for (i in this.elementCache) {
22333 delete this.elementCache[i];
22336 this.elementCache = {};
22341 * A cache of DOM elements
22342 * @property elementCache
22349 * Get the wrapper for the DOM element specified
22350 * @method getElWrapper
22351 * @param {String} id the id of the element to get
22352 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22354 * @deprecated This wrapper isn't that useful
22357 getElWrapper: function(id) {
22358 var oWrapper = this.elementCache[id];
22359 if (!oWrapper || !oWrapper.el) {
22360 oWrapper = this.elementCache[id] =
22361 new this.ElementWrapper(Roo.getDom(id));
22367 * Returns the actual DOM element
22368 * @method getElement
22369 * @param {String} id the id of the elment to get
22370 * @return {Object} The element
22371 * @deprecated use Roo.getDom instead
22374 getElement: function(id) {
22375 return Roo.getDom(id);
22379 * Returns the style property for the DOM element (i.e.,
22380 * document.getElById(id).style)
22382 * @param {String} id the id of the elment to get
22383 * @return {Object} The style property of the element
22384 * @deprecated use Roo.getDom instead
22387 getCss: function(id) {
22388 var el = Roo.getDom(id);
22389 return (el) ? el.style : null;
22393 * Inner class for cached elements
22394 * @class DragDropMgr.ElementWrapper
22399 ElementWrapper: function(el) {
22404 this.el = el || null;
22409 this.id = this.el && el.id;
22411 * A reference to the style property
22414 this.css = this.el && el.style;
22418 * Returns the X position of an html element
22420 * @param el the element for which to get the position
22421 * @return {int} the X coordinate
22423 * @deprecated use Roo.lib.Dom.getX instead
22426 getPosX: function(el) {
22427 return Roo.lib.Dom.getX(el);
22431 * Returns the Y position of an html element
22433 * @param el the element for which to get the position
22434 * @return {int} the Y coordinate
22435 * @deprecated use Roo.lib.Dom.getY instead
22438 getPosY: function(el) {
22439 return Roo.lib.Dom.getY(el);
22443 * Swap two nodes. In IE, we use the native method, for others we
22444 * emulate the IE behavior
22446 * @param n1 the first node to swap
22447 * @param n2 the other node to swap
22450 swapNode: function(n1, n2) {
22454 var p = n2.parentNode;
22455 var s = n2.nextSibling;
22458 p.insertBefore(n1, n2);
22459 } else if (n2 == n1.nextSibling) {
22460 p.insertBefore(n2, n1);
22462 n1.parentNode.replaceChild(n2, n1);
22463 p.insertBefore(n1, s);
22469 * Returns the current scroll position
22470 * @method getScroll
22474 getScroll: function () {
22475 var t, l, dde=document.documentElement, db=document.body;
22476 if (dde && (dde.scrollTop || dde.scrollLeft)) {
22478 l = dde.scrollLeft;
22485 return { top: t, left: l };
22489 * Returns the specified element style property
22491 * @param {HTMLElement} el the element
22492 * @param {string} styleProp the style property
22493 * @return {string} The value of the style property
22494 * @deprecated use Roo.lib.Dom.getStyle
22497 getStyle: function(el, styleProp) {
22498 return Roo.fly(el).getStyle(styleProp);
22502 * Gets the scrollTop
22503 * @method getScrollTop
22504 * @return {int} the document's scrollTop
22507 getScrollTop: function () { return this.getScroll().top; },
22510 * Gets the scrollLeft
22511 * @method getScrollLeft
22512 * @return {int} the document's scrollTop
22515 getScrollLeft: function () { return this.getScroll().left; },
22518 * Sets the x/y position of an element to the location of the
22521 * @param {HTMLElement} moveEl The element to move
22522 * @param {HTMLElement} targetEl The position reference element
22525 moveToEl: function (moveEl, targetEl) {
22526 var aCoord = Roo.lib.Dom.getXY(targetEl);
22527 Roo.lib.Dom.setXY(moveEl, aCoord);
22531 * Numeric array sort function
22532 * @method numericSort
22535 numericSort: function(a, b) { return (a - b); },
22539 * @property _timeoutCount
22546 * Trying to make the load order less important. Without this we get
22547 * an error if this file is loaded before the Event Utility.
22548 * @method _addListeners
22552 _addListeners: function() {
22553 var DDM = Roo.dd.DDM;
22554 if ( Roo.lib.Event && document ) {
22557 if (DDM._timeoutCount > 2000) {
22559 setTimeout(DDM._addListeners, 10);
22560 if (document && document.body) {
22561 DDM._timeoutCount += 1;
22568 * Recursively searches the immediate parent and all child nodes for
22569 * the handle element in order to determine wheter or not it was
22571 * @method handleWasClicked
22572 * @param node the html element to inspect
22575 handleWasClicked: function(node, id) {
22576 if (this.isHandle(id, node.id)) {
22579 // check to see if this is a text node child of the one we want
22580 var p = node.parentNode;
22583 if (this.isHandle(id, p.id)) {
22598 // shorter alias, save a few bytes
22599 Roo.dd.DDM = Roo.dd.DragDropMgr;
22600 Roo.dd.DDM._addListeners();
22604 * Ext JS Library 1.1.1
22605 * Copyright(c) 2006-2007, Ext JS, LLC.
22607 * Originally Released Under LGPL - original licence link has changed is not relivant.
22610 * <script type="text/javascript">
22615 * A DragDrop implementation where the linked element follows the
22616 * mouse cursor during a drag.
22617 * @extends Roo.dd.DragDrop
22619 * @param {String} id the id of the linked element
22620 * @param {String} sGroup the group of related DragDrop items
22621 * @param {object} config an object containing configurable attributes
22622 * Valid properties for DD:
22625 Roo.dd.DD = function(id, sGroup, config) {
22627 this.init(id, sGroup, config);
22631 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22634 * When set to true, the utility automatically tries to scroll the browser
22635 * window wehn a drag and drop element is dragged near the viewport boundary.
22636 * Defaults to true.
22643 * Sets the pointer offset to the distance between the linked element's top
22644 * left corner and the location the element was clicked
22645 * @method autoOffset
22646 * @param {int} iPageX the X coordinate of the click
22647 * @param {int} iPageY the Y coordinate of the click
22649 autoOffset: function(iPageX, iPageY) {
22650 var x = iPageX - this.startPageX;
22651 var y = iPageY - this.startPageY;
22652 this.setDelta(x, y);
22656 * Sets the pointer offset. You can call this directly to force the
22657 * offset to be in a particular location (e.g., pass in 0,0 to set it
22658 * to the center of the object)
22660 * @param {int} iDeltaX the distance from the left
22661 * @param {int} iDeltaY the distance from the top
22663 setDelta: function(iDeltaX, iDeltaY) {
22664 this.deltaX = iDeltaX;
22665 this.deltaY = iDeltaY;
22669 * Sets the drag element to the location of the mousedown or click event,
22670 * maintaining the cursor location relative to the location on the element
22671 * that was clicked. Override this if you want to place the element in a
22672 * location other than where the cursor is.
22673 * @method setDragElPos
22674 * @param {int} iPageX the X coordinate of the mousedown or drag event
22675 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22677 setDragElPos: function(iPageX, iPageY) {
22678 // the first time we do this, we are going to check to make sure
22679 // the element has css positioning
22681 var el = this.getDragEl();
22682 this.alignElWithMouse(el, iPageX, iPageY);
22686 * Sets the element to the location of the mousedown or click event,
22687 * maintaining the cursor location relative to the location on the element
22688 * that was clicked. Override this if you want to place the element in a
22689 * location other than where the cursor is.
22690 * @method alignElWithMouse
22691 * @param {HTMLElement} el the element to move
22692 * @param {int} iPageX the X coordinate of the mousedown or drag event
22693 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22695 alignElWithMouse: function(el, iPageX, iPageY) {
22696 var oCoord = this.getTargetCoord(iPageX, iPageY);
22697 var fly = el.dom ? el : Roo.fly(el);
22698 if (!this.deltaSetXY) {
22699 var aCoord = [oCoord.x, oCoord.y];
22701 var newLeft = fly.getLeft(true);
22702 var newTop = fly.getTop(true);
22703 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22705 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22708 this.cachePosition(oCoord.x, oCoord.y);
22709 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22714 * Saves the most recent position so that we can reset the constraints and
22715 * tick marks on-demand. We need to know this so that we can calculate the
22716 * number of pixels the element is offset from its original position.
22717 * @method cachePosition
22718 * @param iPageX the current x position (optional, this just makes it so we
22719 * don't have to look it up again)
22720 * @param iPageY the current y position (optional, this just makes it so we
22721 * don't have to look it up again)
22723 cachePosition: function(iPageX, iPageY) {
22725 this.lastPageX = iPageX;
22726 this.lastPageY = iPageY;
22728 var aCoord = Roo.lib.Dom.getXY(this.getEl());
22729 this.lastPageX = aCoord[0];
22730 this.lastPageY = aCoord[1];
22735 * Auto-scroll the window if the dragged object has been moved beyond the
22736 * visible window boundary.
22737 * @method autoScroll
22738 * @param {int} x the drag element's x position
22739 * @param {int} y the drag element's y position
22740 * @param {int} h the height of the drag element
22741 * @param {int} w the width of the drag element
22744 autoScroll: function(x, y, h, w) {
22747 // The client height
22748 var clientH = Roo.lib.Dom.getViewWidth();
22750 // The client width
22751 var clientW = Roo.lib.Dom.getViewHeight();
22753 // The amt scrolled down
22754 var st = this.DDM.getScrollTop();
22756 // The amt scrolled right
22757 var sl = this.DDM.getScrollLeft();
22759 // Location of the bottom of the element
22762 // Location of the right of the element
22765 // The distance from the cursor to the bottom of the visible area,
22766 // adjusted so that we don't scroll if the cursor is beyond the
22767 // element drag constraints
22768 var toBot = (clientH + st - y - this.deltaY);
22770 // The distance from the cursor to the right of the visible area
22771 var toRight = (clientW + sl - x - this.deltaX);
22774 // How close to the edge the cursor must be before we scroll
22775 // var thresh = (document.all) ? 100 : 40;
22778 // How many pixels to scroll per autoscroll op. This helps to reduce
22779 // clunky scrolling. IE is more sensitive about this ... it needs this
22780 // value to be higher.
22781 var scrAmt = (document.all) ? 80 : 30;
22783 // Scroll down if we are near the bottom of the visible page and the
22784 // obj extends below the crease
22785 if ( bot > clientH && toBot < thresh ) {
22786 window.scrollTo(sl, st + scrAmt);
22789 // Scroll up if the window is scrolled down and the top of the object
22790 // goes above the top border
22791 if ( y < st && st > 0 && y - st < thresh ) {
22792 window.scrollTo(sl, st - scrAmt);
22795 // Scroll right if the obj is beyond the right border and the cursor is
22796 // near the border.
22797 if ( right > clientW && toRight < thresh ) {
22798 window.scrollTo(sl + scrAmt, st);
22801 // Scroll left if the window has been scrolled to the right and the obj
22802 // extends past the left border
22803 if ( x < sl && sl > 0 && x - sl < thresh ) {
22804 window.scrollTo(sl - scrAmt, st);
22810 * Finds the location the element should be placed if we want to move
22811 * it to where the mouse location less the click offset would place us.
22812 * @method getTargetCoord
22813 * @param {int} iPageX the X coordinate of the click
22814 * @param {int} iPageY the Y coordinate of the click
22815 * @return an object that contains the coordinates (Object.x and Object.y)
22818 getTargetCoord: function(iPageX, iPageY) {
22821 var x = iPageX - this.deltaX;
22822 var y = iPageY - this.deltaY;
22824 if (this.constrainX) {
22825 if (x < this.minX) { x = this.minX; }
22826 if (x > this.maxX) { x = this.maxX; }
22829 if (this.constrainY) {
22830 if (y < this.minY) { y = this.minY; }
22831 if (y > this.maxY) { y = this.maxY; }
22834 x = this.getTick(x, this.xTicks);
22835 y = this.getTick(y, this.yTicks);
22842 * Sets up config options specific to this class. Overrides
22843 * Roo.dd.DragDrop, but all versions of this method through the
22844 * inheritance chain are called
22846 applyConfig: function() {
22847 Roo.dd.DD.superclass.applyConfig.call(this);
22848 this.scroll = (this.config.scroll !== false);
22852 * Event that fires prior to the onMouseDown event. Overrides
22855 b4MouseDown: function(e) {
22856 // this.resetConstraints();
22857 this.autoOffset(e.getPageX(),
22862 * Event that fires prior to the onDrag event. Overrides
22865 b4Drag: function(e) {
22866 this.setDragElPos(e.getPageX(),
22870 toString: function() {
22871 return ("DD " + this.id);
22874 //////////////////////////////////////////////////////////////////////////
22875 // Debugging ygDragDrop events that can be overridden
22876 //////////////////////////////////////////////////////////////////////////
22878 startDrag: function(x, y) {
22881 onDrag: function(e) {
22884 onDragEnter: function(e, id) {
22887 onDragOver: function(e, id) {
22890 onDragOut: function(e, id) {
22893 onDragDrop: function(e, id) {
22896 endDrag: function(e) {
22903 * Ext JS Library 1.1.1
22904 * Copyright(c) 2006-2007, Ext JS, LLC.
22906 * Originally Released Under LGPL - original licence link has changed is not relivant.
22909 * <script type="text/javascript">
22913 * @class Roo.dd.DDProxy
22914 * A DragDrop implementation that inserts an empty, bordered div into
22915 * the document that follows the cursor during drag operations. At the time of
22916 * the click, the frame div is resized to the dimensions of the linked html
22917 * element, and moved to the exact location of the linked element.
22919 * References to the "frame" element refer to the single proxy element that
22920 * was created to be dragged in place of all DDProxy elements on the
22923 * @extends Roo.dd.DD
22925 * @param {String} id the id of the linked html element
22926 * @param {String} sGroup the group of related DragDrop objects
22927 * @param {object} config an object containing configurable attributes
22928 * Valid properties for DDProxy in addition to those in DragDrop:
22929 * resizeFrame, centerFrame, dragElId
22931 Roo.dd.DDProxy = function(id, sGroup, config) {
22933 this.init(id, sGroup, config);
22939 * The default drag frame div id
22940 * @property Roo.dd.DDProxy.dragElId
22944 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22946 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22949 * By default we resize the drag frame to be the same size as the element
22950 * we want to drag (this is to get the frame effect). We can turn it off
22951 * if we want a different behavior.
22952 * @property resizeFrame
22958 * By default the frame is positioned exactly where the drag element is, so
22959 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
22960 * you do not have constraints on the obj is to have the drag frame centered
22961 * around the cursor. Set centerFrame to true for this effect.
22962 * @property centerFrame
22965 centerFrame: false,
22968 * Creates the proxy element if it does not yet exist
22969 * @method createFrame
22971 createFrame: function() {
22973 var body = document.body;
22975 if (!body || !body.firstChild) {
22976 setTimeout( function() { self.createFrame(); }, 50 );
22980 var div = this.getDragEl();
22983 div = document.createElement("div");
22984 div.id = this.dragElId;
22987 s.position = "absolute";
22988 s.visibility = "hidden";
22990 s.border = "2px solid #aaa";
22993 // appendChild can blow up IE if invoked prior to the window load event
22994 // while rendering a table. It is possible there are other scenarios
22995 // that would cause this to happen as well.
22996 body.insertBefore(div, body.firstChild);
23001 * Initialization for the drag frame element. Must be called in the
23002 * constructor of all subclasses
23003 * @method initFrame
23005 initFrame: function() {
23006 this.createFrame();
23009 applyConfig: function() {
23010 Roo.dd.DDProxy.superclass.applyConfig.call(this);
23012 this.resizeFrame = (this.config.resizeFrame !== false);
23013 this.centerFrame = (this.config.centerFrame);
23014 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23018 * Resizes the drag frame to the dimensions of the clicked object, positions
23019 * it over the object, and finally displays it
23020 * @method showFrame
23021 * @param {int} iPageX X click position
23022 * @param {int} iPageY Y click position
23025 showFrame: function(iPageX, iPageY) {
23026 var el = this.getEl();
23027 var dragEl = this.getDragEl();
23028 var s = dragEl.style;
23030 this._resizeProxy();
23032 if (this.centerFrame) {
23033 this.setDelta( Math.round(parseInt(s.width, 10)/2),
23034 Math.round(parseInt(s.height, 10)/2) );
23037 this.setDragElPos(iPageX, iPageY);
23039 Roo.fly(dragEl).show();
23043 * The proxy is automatically resized to the dimensions of the linked
23044 * element when a drag is initiated, unless resizeFrame is set to false
23045 * @method _resizeProxy
23048 _resizeProxy: function() {
23049 if (this.resizeFrame) {
23050 var el = this.getEl();
23051 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23055 // overrides Roo.dd.DragDrop
23056 b4MouseDown: function(e) {
23057 var x = e.getPageX();
23058 var y = e.getPageY();
23059 this.autoOffset(x, y);
23060 this.setDragElPos(x, y);
23063 // overrides Roo.dd.DragDrop
23064 b4StartDrag: function(x, y) {
23065 // show the drag frame
23066 this.showFrame(x, y);
23069 // overrides Roo.dd.DragDrop
23070 b4EndDrag: function(e) {
23071 Roo.fly(this.getDragEl()).hide();
23074 // overrides Roo.dd.DragDrop
23075 // By default we try to move the element to the last location of the frame.
23076 // This is so that the default behavior mirrors that of Roo.dd.DD.
23077 endDrag: function(e) {
23079 var lel = this.getEl();
23080 var del = this.getDragEl();
23082 // Show the drag frame briefly so we can get its position
23083 del.style.visibility = "";
23086 // Hide the linked element before the move to get around a Safari
23088 lel.style.visibility = "hidden";
23089 Roo.dd.DDM.moveToEl(lel, del);
23090 del.style.visibility = "hidden";
23091 lel.style.visibility = "";
23096 beforeMove : function(){
23100 afterDrag : function(){
23104 toString: function() {
23105 return ("DDProxy " + this.id);
23111 * Ext JS Library 1.1.1
23112 * Copyright(c) 2006-2007, Ext JS, LLC.
23114 * Originally Released Under LGPL - original licence link has changed is not relivant.
23117 * <script type="text/javascript">
23121 * @class Roo.dd.DDTarget
23122 * A DragDrop implementation that does not move, but can be a drop
23123 * target. You would get the same result by simply omitting implementation
23124 * for the event callbacks, but this way we reduce the processing cost of the
23125 * event listener and the callbacks.
23126 * @extends Roo.dd.DragDrop
23128 * @param {String} id the id of the element that is a drop target
23129 * @param {String} sGroup the group of related DragDrop objects
23130 * @param {object} config an object containing configurable attributes
23131 * Valid properties for DDTarget in addition to those in
23135 Roo.dd.DDTarget = function(id, sGroup, config) {
23137 this.initTarget(id, sGroup, config);
23139 if (config && (config.listeners || config.events)) {
23140 Roo.dd.DragDrop.superclass.constructor.call(this, {
23141 listeners : config.listeners || {},
23142 events : config.events || {}
23147 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23148 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23149 toString: function() {
23150 return ("DDTarget " + this.id);
23155 * Ext JS Library 1.1.1
23156 * Copyright(c) 2006-2007, Ext JS, LLC.
23158 * Originally Released Under LGPL - original licence link has changed is not relivant.
23161 * <script type="text/javascript">
23166 * @class Roo.dd.ScrollManager
23167 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23168 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23171 Roo.dd.ScrollManager = function(){
23172 var ddm = Roo.dd.DragDropMgr;
23179 var onStop = function(e){
23184 var triggerRefresh = function(){
23185 if(ddm.dragCurrent){
23186 ddm.refreshCache(ddm.dragCurrent.groups);
23190 var doScroll = function(){
23191 if(ddm.dragCurrent){
23192 var dds = Roo.dd.ScrollManager;
23194 if(proc.el.scroll(proc.dir, dds.increment)){
23198 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23203 var clearProc = function(){
23205 clearInterval(proc.id);
23212 var startProc = function(el, dir){
23213 Roo.log('scroll startproc');
23217 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23220 var onFire = function(e, isDrop){
23222 if(isDrop || !ddm.dragCurrent){ return; }
23223 var dds = Roo.dd.ScrollManager;
23224 if(!dragEl || dragEl != ddm.dragCurrent){
23225 dragEl = ddm.dragCurrent;
23226 // refresh regions on drag start
23227 dds.refreshCache();
23230 var xy = Roo.lib.Event.getXY(e);
23231 var pt = new Roo.lib.Point(xy[0], xy[1]);
23232 for(var id in els){
23233 var el = els[id], r = el._region;
23234 if(r && r.contains(pt) && el.isScrollable()){
23235 if(r.bottom - pt.y <= dds.thresh){
23237 startProc(el, "down");
23240 }else if(r.right - pt.x <= dds.thresh){
23242 startProc(el, "left");
23245 }else if(pt.y - r.top <= dds.thresh){
23247 startProc(el, "up");
23250 }else if(pt.x - r.left <= dds.thresh){
23252 startProc(el, "right");
23261 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23262 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23266 * Registers new overflow element(s) to auto scroll
23267 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23269 register : function(el){
23270 if(el instanceof Array){
23271 for(var i = 0, len = el.length; i < len; i++) {
23272 this.register(el[i]);
23278 Roo.dd.ScrollManager.els = els;
23282 * Unregisters overflow element(s) so they are no longer scrolled
23283 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23285 unregister : function(el){
23286 if(el instanceof Array){
23287 for(var i = 0, len = el.length; i < len; i++) {
23288 this.unregister(el[i]);
23297 * The number of pixels from the edge of a container the pointer needs to be to
23298 * trigger scrolling (defaults to 25)
23304 * The number of pixels to scroll in each scroll increment (defaults to 50)
23310 * The frequency of scrolls in milliseconds (defaults to 500)
23316 * True to animate the scroll (defaults to true)
23322 * The animation duration in seconds -
23323 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23329 * Manually trigger a cache refresh.
23331 refreshCache : function(){
23332 for(var id in els){
23333 if(typeof els[id] == 'object'){ // for people extending the object prototype
23334 els[id]._region = els[id].getRegion();
23341 * Ext JS Library 1.1.1
23342 * Copyright(c) 2006-2007, Ext JS, LLC.
23344 * Originally Released Under LGPL - original licence link has changed is not relivant.
23347 * <script type="text/javascript">
23352 * @class Roo.dd.Registry
23353 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
23354 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23357 Roo.dd.Registry = function(){
23360 var autoIdSeed = 0;
23362 var getId = function(el, autogen){
23363 if(typeof el == "string"){
23367 if(!id && autogen !== false){
23368 id = "roodd-" + (++autoIdSeed);
23376 * Register a drag drop element
23377 * @param {String|HTMLElement} element The id or DOM node to register
23378 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23379 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
23380 * knows how to interpret, plus there are some specific properties known to the Registry that should be
23381 * populated in the data object (if applicable):
23383 Value Description<br />
23384 --------- ------------------------------------------<br />
23385 handles Array of DOM nodes that trigger dragging<br />
23386 for the element being registered<br />
23387 isHandle True if the element passed in triggers<br />
23388 dragging itself, else false
23391 register : function(el, data){
23393 if(typeof el == "string"){
23394 el = document.getElementById(el);
23397 elements[getId(el)] = data;
23398 if(data.isHandle !== false){
23399 handles[data.ddel.id] = data;
23402 var hs = data.handles;
23403 for(var i = 0, len = hs.length; i < len; i++){
23404 handles[getId(hs[i])] = data;
23410 * Unregister a drag drop element
23411 * @param {String|HTMLElement} element The id or DOM node to unregister
23413 unregister : function(el){
23414 var id = getId(el, false);
23415 var data = elements[id];
23417 delete elements[id];
23419 var hs = data.handles;
23420 for(var i = 0, len = hs.length; i < len; i++){
23421 delete handles[getId(hs[i], false)];
23428 * Returns the handle registered for a DOM Node by id
23429 * @param {String|HTMLElement} id The DOM node or id to look up
23430 * @return {Object} handle The custom handle data
23432 getHandle : function(id){
23433 if(typeof id != "string"){ // must be element?
23436 return handles[id];
23440 * Returns the handle that is registered for the DOM node that is the target of the event
23441 * @param {Event} e The event
23442 * @return {Object} handle The custom handle data
23444 getHandleFromEvent : function(e){
23445 var t = Roo.lib.Event.getTarget(e);
23446 return t ? handles[t.id] : null;
23450 * Returns a custom data object that is registered for a DOM node by id
23451 * @param {String|HTMLElement} id The DOM node or id to look up
23452 * @return {Object} data The custom data
23454 getTarget : function(id){
23455 if(typeof id != "string"){ // must be element?
23458 return elements[id];
23462 * Returns a custom data object that is registered for the DOM node that is the target of the event
23463 * @param {Event} e The event
23464 * @return {Object} data The custom data
23466 getTargetFromEvent : function(e){
23467 var t = Roo.lib.Event.getTarget(e);
23468 return t ? elements[t.id] || handles[t.id] : null;
23473 * Ext JS Library 1.1.1
23474 * Copyright(c) 2006-2007, Ext JS, LLC.
23476 * Originally Released Under LGPL - original licence link has changed is not relivant.
23479 * <script type="text/javascript">
23484 * @class Roo.dd.StatusProxy
23485 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
23486 * default drag proxy used by all Roo.dd components.
23488 * @param {Object} config
23490 Roo.dd.StatusProxy = function(config){
23491 Roo.apply(this, config);
23492 this.id = this.id || Roo.id();
23493 this.el = new Roo.Layer({
23495 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23496 {tag: "div", cls: "x-dd-drop-icon"},
23497 {tag: "div", cls: "x-dd-drag-ghost"}
23500 shadow: !config || config.shadow !== false
23502 this.ghost = Roo.get(this.el.dom.childNodes[1]);
23503 this.dropStatus = this.dropNotAllowed;
23506 Roo.dd.StatusProxy.prototype = {
23508 * @cfg {String} dropAllowed
23509 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23511 dropAllowed : "x-dd-drop-ok",
23513 * @cfg {String} dropNotAllowed
23514 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23516 dropNotAllowed : "x-dd-drop-nodrop",
23519 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23520 * over the current target element.
23521 * @param {String} cssClass The css class for the new drop status indicator image
23523 setStatus : function(cssClass){
23524 cssClass = cssClass || this.dropNotAllowed;
23525 if(this.dropStatus != cssClass){
23526 this.el.replaceClass(this.dropStatus, cssClass);
23527 this.dropStatus = cssClass;
23532 * Resets the status indicator to the default dropNotAllowed value
23533 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23535 reset : function(clearGhost){
23536 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23537 this.dropStatus = this.dropNotAllowed;
23539 this.ghost.update("");
23544 * Updates the contents of the ghost element
23545 * @param {String} html The html that will replace the current innerHTML of the ghost element
23547 update : function(html){
23548 if(typeof html == "string"){
23549 this.ghost.update(html);
23551 this.ghost.update("");
23552 html.style.margin = "0";
23553 this.ghost.dom.appendChild(html);
23555 // ensure float = none set?? cant remember why though.
23556 var el = this.ghost.dom.firstChild;
23558 Roo.fly(el).setStyle('float', 'none');
23563 * Returns the underlying proxy {@link Roo.Layer}
23564 * @return {Roo.Layer} el
23566 getEl : function(){
23571 * Returns the ghost element
23572 * @return {Roo.Element} el
23574 getGhost : function(){
23580 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23582 hide : function(clear){
23590 * Stops the repair animation if it's currently running
23593 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23599 * Displays this proxy
23606 * Force the Layer to sync its shadow and shim positions to the element
23613 * Causes the proxy to return to its position of origin via an animation. Should be called after an
23614 * invalid drop operation by the item being dragged.
23615 * @param {Array} xy The XY position of the element ([x, y])
23616 * @param {Function} callback The function to call after the repair is complete
23617 * @param {Object} scope The scope in which to execute the callback
23619 repair : function(xy, callback, scope){
23620 this.callback = callback;
23621 this.scope = scope;
23622 if(xy && this.animRepair !== false){
23623 this.el.addClass("x-dd-drag-repair");
23624 this.el.hideUnders(true);
23625 this.anim = this.el.shift({
23626 duration: this.repairDuration || .5,
23630 callback: this.afterRepair,
23634 this.afterRepair();
23639 afterRepair : function(){
23641 if(typeof this.callback == "function"){
23642 this.callback.call(this.scope || this);
23644 this.callback = null;
23649 * Ext JS Library 1.1.1
23650 * Copyright(c) 2006-2007, Ext JS, LLC.
23652 * Originally Released Under LGPL - original licence link has changed is not relivant.
23655 * <script type="text/javascript">
23659 * @class Roo.dd.DragSource
23660 * @extends Roo.dd.DDProxy
23661 * A simple class that provides the basic implementation needed to make any element draggable.
23663 * @param {String/HTMLElement/Element} el The container element
23664 * @param {Object} config
23666 Roo.dd.DragSource = function(el, config){
23667 this.el = Roo.get(el);
23668 this.dragData = {};
23670 Roo.apply(this, config);
23673 this.proxy = new Roo.dd.StatusProxy();
23676 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23677 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23679 this.dragging = false;
23682 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23684 * @cfg {String} dropAllowed
23685 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23687 dropAllowed : "x-dd-drop-ok",
23689 * @cfg {String} dropNotAllowed
23690 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23692 dropNotAllowed : "x-dd-drop-nodrop",
23695 * Returns the data object associated with this drag source
23696 * @return {Object} data An object containing arbitrary data
23698 getDragData : function(e){
23699 return this.dragData;
23703 onDragEnter : function(e, id){
23704 var target = Roo.dd.DragDropMgr.getDDById(id);
23705 this.cachedTarget = target;
23706 if(this.beforeDragEnter(target, e, id) !== false){
23707 if(target.isNotifyTarget){
23708 var status = target.notifyEnter(this, e, this.dragData);
23709 this.proxy.setStatus(status);
23711 this.proxy.setStatus(this.dropAllowed);
23714 if(this.afterDragEnter){
23716 * An empty function by default, but provided so that you can perform a custom action
23717 * when the dragged item enters the drop target by providing an implementation.
23718 * @param {Roo.dd.DragDrop} target The drop target
23719 * @param {Event} e The event object
23720 * @param {String} id The id of the dragged element
23721 * @method afterDragEnter
23723 this.afterDragEnter(target, e, id);
23729 * An empty function by default, but provided so that you can perform a custom action
23730 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23731 * @param {Roo.dd.DragDrop} target The drop target
23732 * @param {Event} e The event object
23733 * @param {String} id The id of the dragged element
23734 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23736 beforeDragEnter : function(target, e, id){
23741 alignElWithMouse: function() {
23742 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23747 onDragOver : function(e, id){
23748 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23749 if(this.beforeDragOver(target, e, id) !== false){
23750 if(target.isNotifyTarget){
23751 var status = target.notifyOver(this, e, this.dragData);
23752 this.proxy.setStatus(status);
23755 if(this.afterDragOver){
23757 * An empty function by default, but provided so that you can perform a custom action
23758 * while the dragged item is over the drop target by providing an implementation.
23759 * @param {Roo.dd.DragDrop} target The drop target
23760 * @param {Event} e The event object
23761 * @param {String} id The id of the dragged element
23762 * @method afterDragOver
23764 this.afterDragOver(target, e, id);
23770 * An empty function by default, but provided so that you can perform a custom action
23771 * while the dragged item is over the drop target and optionally cancel the onDragOver.
23772 * @param {Roo.dd.DragDrop} target The drop target
23773 * @param {Event} e The event object
23774 * @param {String} id The id of the dragged element
23775 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23777 beforeDragOver : function(target, e, id){
23782 onDragOut : function(e, id){
23783 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23784 if(this.beforeDragOut(target, e, id) !== false){
23785 if(target.isNotifyTarget){
23786 target.notifyOut(this, e, this.dragData);
23788 this.proxy.reset();
23789 if(this.afterDragOut){
23791 * An empty function by default, but provided so that you can perform a custom action
23792 * after the dragged item is dragged out of the target without dropping.
23793 * @param {Roo.dd.DragDrop} target The drop target
23794 * @param {Event} e The event object
23795 * @param {String} id The id of the dragged element
23796 * @method afterDragOut
23798 this.afterDragOut(target, e, id);
23801 this.cachedTarget = null;
23805 * An empty function by default, but provided so that you can perform a custom action before the dragged
23806 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23807 * @param {Roo.dd.DragDrop} target The drop target
23808 * @param {Event} e The event object
23809 * @param {String} id The id of the dragged element
23810 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23812 beforeDragOut : function(target, e, id){
23817 onDragDrop : function(e, id){
23818 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23819 if(this.beforeDragDrop(target, e, id) !== false){
23820 if(target.isNotifyTarget){
23821 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23822 this.onValidDrop(target, e, id);
23824 this.onInvalidDrop(target, e, id);
23827 this.onValidDrop(target, e, id);
23830 if(this.afterDragDrop){
23832 * An empty function by default, but provided so that you can perform a custom action
23833 * after a valid drag drop has occurred by providing an implementation.
23834 * @param {Roo.dd.DragDrop} target The drop target
23835 * @param {Event} e The event object
23836 * @param {String} id The id of the dropped element
23837 * @method afterDragDrop
23839 this.afterDragDrop(target, e, id);
23842 delete this.cachedTarget;
23846 * An empty function by default, but provided so that you can perform a custom action before the dragged
23847 * item is dropped onto the target and optionally cancel the onDragDrop.
23848 * @param {Roo.dd.DragDrop} target The drop target
23849 * @param {Event} e The event object
23850 * @param {String} id The id of the dragged element
23851 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23853 beforeDragDrop : function(target, e, id){
23858 onValidDrop : function(target, e, id){
23860 if(this.afterValidDrop){
23862 * An empty function by default, but provided so that you can perform a custom action
23863 * after a valid drop has occurred by providing an implementation.
23864 * @param {Object} target The target DD
23865 * @param {Event} e The event object
23866 * @param {String} id The id of the dropped element
23867 * @method afterInvalidDrop
23869 this.afterValidDrop(target, e, id);
23874 getRepairXY : function(e, data){
23875 return this.el.getXY();
23879 onInvalidDrop : function(target, e, id){
23880 this.beforeInvalidDrop(target, e, id);
23881 if(this.cachedTarget){
23882 if(this.cachedTarget.isNotifyTarget){
23883 this.cachedTarget.notifyOut(this, e, this.dragData);
23885 this.cacheTarget = null;
23887 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23889 if(this.afterInvalidDrop){
23891 * An empty function by default, but provided so that you can perform a custom action
23892 * after an invalid drop has occurred by providing an implementation.
23893 * @param {Event} e The event object
23894 * @param {String} id The id of the dropped element
23895 * @method afterInvalidDrop
23897 this.afterInvalidDrop(e, id);
23902 afterRepair : function(){
23904 this.el.highlight(this.hlColor || "c3daf9");
23906 this.dragging = false;
23910 * An empty function by default, but provided so that you can perform a custom action after an invalid
23911 * drop has occurred.
23912 * @param {Roo.dd.DragDrop} target The drop target
23913 * @param {Event} e The event object
23914 * @param {String} id The id of the dragged element
23915 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23917 beforeInvalidDrop : function(target, e, id){
23922 handleMouseDown : function(e){
23923 if(this.dragging) {
23926 var data = this.getDragData(e);
23927 if(data && this.onBeforeDrag(data, e) !== false){
23928 this.dragData = data;
23930 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23935 * An empty function by default, but provided so that you can perform a custom action before the initial
23936 * drag event begins and optionally cancel it.
23937 * @param {Object} data An object containing arbitrary data to be shared with drop targets
23938 * @param {Event} e The event object
23939 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23941 onBeforeDrag : function(data, e){
23946 * An empty function by default, but provided so that you can perform a custom action once the initial
23947 * drag event has begun. The drag cannot be canceled from this function.
23948 * @param {Number} x The x position of the click on the dragged object
23949 * @param {Number} y The y position of the click on the dragged object
23951 onStartDrag : Roo.emptyFn,
23953 // private - YUI override
23954 startDrag : function(x, y){
23955 this.proxy.reset();
23956 this.dragging = true;
23957 this.proxy.update("");
23958 this.onInitDrag(x, y);
23963 onInitDrag : function(x, y){
23964 var clone = this.el.dom.cloneNode(true);
23965 clone.id = Roo.id(); // prevent duplicate ids
23966 this.proxy.update(clone);
23967 this.onStartDrag(x, y);
23972 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23973 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23975 getProxy : function(){
23980 * Hides the drag source's {@link Roo.dd.StatusProxy}
23982 hideProxy : function(){
23984 this.proxy.reset(true);
23985 this.dragging = false;
23989 triggerCacheRefresh : function(){
23990 Roo.dd.DDM.refreshCache(this.groups);
23993 // private - override to prevent hiding
23994 b4EndDrag: function(e) {
23997 // private - override to prevent moving
23998 endDrag : function(e){
23999 this.onEndDrag(this.dragData, e);
24003 onEndDrag : function(data, e){
24006 // private - pin to cursor
24007 autoOffset : function(x, y) {
24008 this.setDelta(-12, -20);
24012 * Ext JS Library 1.1.1
24013 * Copyright(c) 2006-2007, Ext JS, LLC.
24015 * Originally Released Under LGPL - original licence link has changed is not relivant.
24018 * <script type="text/javascript">
24023 * @class Roo.dd.DropTarget
24024 * @extends Roo.dd.DDTarget
24025 * A simple class that provides the basic implementation needed to make any element a drop target that can have
24026 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
24028 * @param {String/HTMLElement/Element} el The container element
24029 * @param {Object} config
24031 Roo.dd.DropTarget = function(el, config){
24032 this.el = Roo.get(el);
24034 var listeners = false; ;
24035 if (config && config.listeners) {
24036 listeners= config.listeners;
24037 delete config.listeners;
24039 Roo.apply(this, config);
24041 if(this.containerScroll){
24042 Roo.dd.ScrollManager.register(this.el);
24046 * @scope Roo.dd.DropTarget
24051 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24052 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
24053 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
24055 * IMPORTANT : it should set this.valid to true|false
24057 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24058 * @param {Event} e The event
24059 * @param {Object} data An object containing arbitrary data supplied by the drag source
24065 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24066 * This method will be called on every mouse movement while the drag source is over the drop target.
24067 * This default implementation simply returns the dropAllowed config value.
24069 * IMPORTANT : it should set this.valid to true|false
24071 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24072 * @param {Event} e The event
24073 * @param {Object} data An object containing arbitrary data supplied by the drag source
24079 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24080 * out of the target without dropping. This default implementation simply removes the CSS class specified by
24081 * overClass (if any) from the drop element.
24084 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24085 * @param {Event} e The event
24086 * @param {Object} data An object containing arbitrary data supplied by the drag source
24092 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24093 * been dropped on it. This method has no default implementation and returns false, so you must provide an
24094 * implementation that does something to process the drop event and returns true so that the drag source's
24095 * repair action does not run.
24097 * IMPORTANT : it should set this.success
24099 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24100 * @param {Event} e The event
24101 * @param {Object} data An object containing arbitrary data supplied by the drag source
24107 Roo.dd.DropTarget.superclass.constructor.call( this,
24109 this.ddGroup || this.group,
24112 listeners : listeners || {}
24120 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24122 * @cfg {String} overClass
24123 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24126 * @cfg {String} ddGroup
24127 * The drag drop group to handle drop events for
24131 * @cfg {String} dropAllowed
24132 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24134 dropAllowed : "x-dd-drop-ok",
24136 * @cfg {String} dropNotAllowed
24137 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24139 dropNotAllowed : "x-dd-drop-nodrop",
24141 * @cfg {boolean} success
24142 * set this after drop listener..
24146 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24147 * if the drop point is valid for over/enter..
24154 isNotifyTarget : true,
24159 notifyEnter : function(dd, e, data)
24162 this.fireEvent('enter', dd, e, data);
24163 if(this.overClass){
24164 this.el.addClass(this.overClass);
24166 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24167 this.valid ? this.dropAllowed : this.dropNotAllowed
24174 notifyOver : function(dd, e, data)
24177 this.fireEvent('over', dd, e, data);
24178 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24179 this.valid ? this.dropAllowed : this.dropNotAllowed
24186 notifyOut : function(dd, e, data)
24188 this.fireEvent('out', dd, e, data);
24189 if(this.overClass){
24190 this.el.removeClass(this.overClass);
24197 notifyDrop : function(dd, e, data)
24199 this.success = false;
24200 this.fireEvent('drop', dd, e, data);
24201 return this.success;
24205 * Ext JS Library 1.1.1
24206 * Copyright(c) 2006-2007, Ext JS, LLC.
24208 * Originally Released Under LGPL - original licence link has changed is not relivant.
24211 * <script type="text/javascript">
24216 * @class Roo.dd.DragZone
24217 * @extends Roo.dd.DragSource
24218 * This class provides a container DD instance that proxies for multiple child node sources.<br />
24219 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24221 * @param {String/HTMLElement/Element} el The container element
24222 * @param {Object} config
24224 Roo.dd.DragZone = function(el, config){
24225 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24226 if(this.containerScroll){
24227 Roo.dd.ScrollManager.register(this.el);
24231 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24233 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24234 * for auto scrolling during drag operations.
24237 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24238 * method after a failed drop (defaults to "c3daf9" - light blue)
24242 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24243 * for a valid target to drag based on the mouse down. Override this method
24244 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24245 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24246 * @param {EventObject} e The mouse down event
24247 * @return {Object} The dragData
24249 getDragData : function(e){
24250 return Roo.dd.Registry.getHandleFromEvent(e);
24254 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24255 * this.dragData.ddel
24256 * @param {Number} x The x position of the click on the dragged object
24257 * @param {Number} y The y position of the click on the dragged object
24258 * @return {Boolean} true to continue the drag, false to cancel
24260 onInitDrag : function(x, y){
24261 this.proxy.update(this.dragData.ddel.cloneNode(true));
24262 this.onStartDrag(x, y);
24267 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
24269 afterRepair : function(){
24271 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24273 this.dragging = false;
24277 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24278 * the XY of this.dragData.ddel
24279 * @param {EventObject} e The mouse up event
24280 * @return {Array} The xy location (e.g. [100, 200])
24282 getRepairXY : function(e){
24283 return Roo.Element.fly(this.dragData.ddel).getXY();
24287 * Ext JS Library 1.1.1
24288 * Copyright(c) 2006-2007, Ext JS, LLC.
24290 * Originally Released Under LGPL - original licence link has changed is not relivant.
24293 * <script type="text/javascript">
24296 * @class Roo.dd.DropZone
24297 * @extends Roo.dd.DropTarget
24298 * This class provides a container DD instance that proxies for multiple child node targets.<br />
24299 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24301 * @param {String/HTMLElement/Element} el The container element
24302 * @param {Object} config
24304 Roo.dd.DropZone = function(el, config){
24305 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24308 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24310 * Returns a custom data object associated with the DOM node that is the target of the event. By default
24311 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24312 * provide your own custom lookup.
24313 * @param {Event} e The event
24314 * @return {Object} data The custom data
24316 getTargetFromEvent : function(e){
24317 return Roo.dd.Registry.getTargetFromEvent(e);
24321 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24322 * that it has registered. This method has no default implementation and should be overridden to provide
24323 * node-specific processing if necessary.
24324 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24325 * {@link #getTargetFromEvent} for this node)
24326 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24327 * @param {Event} e The event
24328 * @param {Object} data An object containing arbitrary data supplied by the drag source
24330 onNodeEnter : function(n, dd, e, data){
24335 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24336 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
24337 * overridden to provide the proper feedback.
24338 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24339 * {@link #getTargetFromEvent} for this node)
24340 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24341 * @param {Event} e The event
24342 * @param {Object} data An object containing arbitrary data supplied by the drag source
24343 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24344 * underlying {@link Roo.dd.StatusProxy} can be updated
24346 onNodeOver : function(n, dd, e, data){
24347 return this.dropAllowed;
24351 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24352 * the drop node without dropping. This method has no default implementation and should be overridden to provide
24353 * node-specific processing if necessary.
24354 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24355 * {@link #getTargetFromEvent} for this node)
24356 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24357 * @param {Event} e The event
24358 * @param {Object} data An object containing arbitrary data supplied by the drag source
24360 onNodeOut : function(n, dd, e, data){
24365 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24366 * the drop node. The default implementation returns false, so it should be overridden to provide the
24367 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24368 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24369 * {@link #getTargetFromEvent} for this node)
24370 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24371 * @param {Event} e The event
24372 * @param {Object} data An object containing arbitrary data supplied by the drag source
24373 * @return {Boolean} True if the drop was valid, else false
24375 onNodeDrop : function(n, dd, e, data){
24380 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24381 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
24382 * it should be overridden to provide the proper feedback if necessary.
24383 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24384 * @param {Event} e The event
24385 * @param {Object} data An object containing arbitrary data supplied by the drag source
24386 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24387 * underlying {@link Roo.dd.StatusProxy} can be updated
24389 onContainerOver : function(dd, e, data){
24390 return this.dropNotAllowed;
24394 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24395 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
24396 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24397 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
24398 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24399 * @param {Event} e The event
24400 * @param {Object} data An object containing arbitrary data supplied by the drag source
24401 * @return {Boolean} True if the drop was valid, else false
24403 onContainerDrop : function(dd, e, data){
24408 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24409 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
24410 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24411 * you should override this method and provide a custom implementation.
24412 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24413 * @param {Event} e The event
24414 * @param {Object} data An object containing arbitrary data supplied by the drag source
24415 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24416 * underlying {@link Roo.dd.StatusProxy} can be updated
24418 notifyEnter : function(dd, e, data){
24419 return this.dropNotAllowed;
24423 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24424 * This method will be called on every mouse movement while the drag source is over the drop zone.
24425 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24426 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24427 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24428 * registered node, it will call {@link #onContainerOver}.
24429 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24430 * @param {Event} e The event
24431 * @param {Object} data An object containing arbitrary data supplied by the drag source
24432 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24433 * underlying {@link Roo.dd.StatusProxy} can be updated
24435 notifyOver : function(dd, e, data){
24436 var n = this.getTargetFromEvent(e);
24437 if(!n){ // not over valid drop target
24438 if(this.lastOverNode){
24439 this.onNodeOut(this.lastOverNode, dd, e, data);
24440 this.lastOverNode = null;
24442 return this.onContainerOver(dd, e, data);
24444 if(this.lastOverNode != n){
24445 if(this.lastOverNode){
24446 this.onNodeOut(this.lastOverNode, dd, e, data);
24448 this.onNodeEnter(n, dd, e, data);
24449 this.lastOverNode = n;
24451 return this.onNodeOver(n, dd, e, data);
24455 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24456 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
24457 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24458 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24459 * @param {Event} e The event
24460 * @param {Object} data An object containing arbitrary data supplied by the drag zone
24462 notifyOut : function(dd, e, data){
24463 if(this.lastOverNode){
24464 this.onNodeOut(this.lastOverNode, dd, e, data);
24465 this.lastOverNode = null;
24470 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24471 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
24472 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24473 * otherwise it will call {@link #onContainerDrop}.
24474 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24475 * @param {Event} e The event
24476 * @param {Object} data An object containing arbitrary data supplied by the drag source
24477 * @return {Boolean} True if the drop was valid, else false
24479 notifyDrop : function(dd, e, data){
24480 if(this.lastOverNode){
24481 this.onNodeOut(this.lastOverNode, dd, e, data);
24482 this.lastOverNode = null;
24484 var n = this.getTargetFromEvent(e);
24486 this.onNodeDrop(n, dd, e, data) :
24487 this.onContainerDrop(dd, e, data);
24491 triggerCacheRefresh : function(){
24492 Roo.dd.DDM.refreshCache(this.groups);
24496 * Ext JS Library 1.1.1
24497 * Copyright(c) 2006-2007, Ext JS, LLC.
24499 * Originally Released Under LGPL - original licence link has changed is not relivant.
24502 * <script type="text/javascript">
24507 * @class Roo.data.SortTypes
24509 * Defines the default sorting (casting?) comparison functions used when sorting data.
24511 Roo.data.SortTypes = {
24513 * Default sort that does nothing
24514 * @param {Mixed} s The value being converted
24515 * @return {Mixed} The comparison value
24517 none : function(s){
24522 * The regular expression used to strip tags
24526 stripTagsRE : /<\/?[^>]+>/gi,
24529 * Strips all HTML tags to sort on text only
24530 * @param {Mixed} s The value being converted
24531 * @return {String} The comparison value
24533 asText : function(s){
24534 return String(s).replace(this.stripTagsRE, "");
24538 * Strips all HTML tags to sort on text only - Case insensitive
24539 * @param {Mixed} s The value being converted
24540 * @return {String} The comparison value
24542 asUCText : function(s){
24543 return String(s).toUpperCase().replace(this.stripTagsRE, "");
24547 * Case insensitive string
24548 * @param {Mixed} s The value being converted
24549 * @return {String} The comparison value
24551 asUCString : function(s) {
24552 return String(s).toUpperCase();
24557 * @param {Mixed} s The value being converted
24558 * @return {Number} The comparison value
24560 asDate : function(s) {
24564 if(s instanceof Date){
24565 return s.getTime();
24567 return Date.parse(String(s));
24572 * @param {Mixed} s The value being converted
24573 * @return {Float} The comparison value
24575 asFloat : function(s) {
24576 var val = parseFloat(String(s).replace(/,/g, ""));
24585 * @param {Mixed} s The value being converted
24586 * @return {Number} The comparison value
24588 asInt : function(s) {
24589 var val = parseInt(String(s).replace(/,/g, ""));
24597 * Ext JS Library 1.1.1
24598 * Copyright(c) 2006-2007, Ext JS, LLC.
24600 * Originally Released Under LGPL - original licence link has changed is not relivant.
24603 * <script type="text/javascript">
24607 * @class Roo.data.Record
24608 * Instances of this class encapsulate both record <em>definition</em> information, and record
24609 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24610 * to access Records cached in an {@link Roo.data.Store} object.<br>
24612 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24613 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24616 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24618 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24619 * {@link #create}. The parameters are the same.
24620 * @param {Array} data An associative Array of data values keyed by the field name.
24621 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24622 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24623 * not specified an integer id is generated.
24625 Roo.data.Record = function(data, id){
24626 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24631 * Generate a constructor for a specific record layout.
24632 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24633 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24634 * Each field definition object may contain the following properties: <ul>
24635 * <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,
24636 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24637 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24638 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24639 * is being used, then this is a string containing the javascript expression to reference the data relative to
24640 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24641 * to the data item relative to the record element. If the mapping expression is the same as the field name,
24642 * this may be omitted.</p></li>
24643 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24644 * <ul><li>auto (Default, implies no conversion)</li>
24649 * <li>date</li></ul></p></li>
24650 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24651 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24652 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24653 * by the Reader into an object that will be stored in the Record. It is passed the
24654 * following parameters:<ul>
24655 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24657 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24659 * <br>usage:<br><pre><code>
24660 var TopicRecord = Roo.data.Record.create(
24661 {name: 'title', mapping: 'topic_title'},
24662 {name: 'author', mapping: 'username'},
24663 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24664 {name: 'lastPost', mapping: 'post_time', type: 'date'},
24665 {name: 'lastPoster', mapping: 'user2'},
24666 {name: 'excerpt', mapping: 'post_text'}
24669 var myNewRecord = new TopicRecord({
24670 title: 'Do my job please',
24673 lastPost: new Date(),
24674 lastPoster: 'Animal',
24675 excerpt: 'No way dude!'
24677 myStore.add(myNewRecord);
24682 Roo.data.Record.create = function(o){
24683 var f = function(){
24684 f.superclass.constructor.apply(this, arguments);
24686 Roo.extend(f, Roo.data.Record);
24687 var p = f.prototype;
24688 p.fields = new Roo.util.MixedCollection(false, function(field){
24691 for(var i = 0, len = o.length; i < len; i++){
24692 p.fields.add(new Roo.data.Field(o[i]));
24694 f.getField = function(name){
24695 return p.fields.get(name);
24700 Roo.data.Record.AUTO_ID = 1000;
24701 Roo.data.Record.EDIT = 'edit';
24702 Roo.data.Record.REJECT = 'reject';
24703 Roo.data.Record.COMMIT = 'commit';
24705 Roo.data.Record.prototype = {
24707 * Readonly flag - true if this record has been modified.
24716 join : function(store){
24717 this.store = store;
24721 * Set the named field to the specified value.
24722 * @param {String} name The name of the field to set.
24723 * @param {Object} value The value to set the field to.
24725 set : function(name, value){
24726 if(this.data[name] == value){
24730 if(!this.modified){
24731 this.modified = {};
24733 if(typeof this.modified[name] == 'undefined'){
24734 this.modified[name] = this.data[name];
24736 this.data[name] = value;
24737 if(!this.editing && this.store){
24738 this.store.afterEdit(this);
24743 * Get the value of the named field.
24744 * @param {String} name The name of the field to get the value of.
24745 * @return {Object} The value of the field.
24747 get : function(name){
24748 return this.data[name];
24752 beginEdit : function(){
24753 this.editing = true;
24754 this.modified = {};
24758 cancelEdit : function(){
24759 this.editing = false;
24760 delete this.modified;
24764 endEdit : function(){
24765 this.editing = false;
24766 if(this.dirty && this.store){
24767 this.store.afterEdit(this);
24772 * Usually called by the {@link Roo.data.Store} which owns the Record.
24773 * Rejects all changes made to the Record since either creation, or the last commit operation.
24774 * Modified fields are reverted to their original values.
24776 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24777 * of reject operations.
24779 reject : function(){
24780 var m = this.modified;
24782 if(typeof m[n] != "function"){
24783 this.data[n] = m[n];
24786 this.dirty = false;
24787 delete this.modified;
24788 this.editing = false;
24790 this.store.afterReject(this);
24795 * Usually called by the {@link Roo.data.Store} which owns the Record.
24796 * Commits all changes made to the Record since either creation, or the last commit operation.
24798 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24799 * of commit operations.
24801 commit : function(){
24802 this.dirty = false;
24803 delete this.modified;
24804 this.editing = false;
24806 this.store.afterCommit(this);
24811 hasError : function(){
24812 return this.error != null;
24816 clearError : function(){
24821 * Creates a copy of this record.
24822 * @param {String} id (optional) A new record id if you don't want to use this record's id
24825 copy : function(newId) {
24826 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24830 * Ext JS Library 1.1.1
24831 * Copyright(c) 2006-2007, Ext JS, LLC.
24833 * Originally Released Under LGPL - original licence link has changed is not relivant.
24836 * <script type="text/javascript">
24842 * @class Roo.data.Store
24843 * @extends Roo.util.Observable
24844 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24845 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24847 * 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
24848 * has no knowledge of the format of the data returned by the Proxy.<br>
24850 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24851 * instances from the data object. These records are cached and made available through accessor functions.
24853 * Creates a new Store.
24854 * @param {Object} config A config object containing the objects needed for the Store to access data,
24855 * and read the data into Records.
24857 Roo.data.Store = function(config){
24858 this.data = new Roo.util.MixedCollection(false);
24859 this.data.getKey = function(o){
24862 this.baseParams = {};
24864 this.paramNames = {
24869 "multisort" : "_multisort"
24872 if(config && config.data){
24873 this.inlineData = config.data;
24874 delete config.data;
24877 Roo.apply(this, config);
24879 if(this.reader){ // reader passed
24880 this.reader = Roo.factory(this.reader, Roo.data);
24881 this.reader.xmodule = this.xmodule || false;
24882 if(!this.recordType){
24883 this.recordType = this.reader.recordType;
24885 if(this.reader.onMetaChange){
24886 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24890 if(this.recordType){
24891 this.fields = this.recordType.prototype.fields;
24893 this.modified = [];
24897 * @event datachanged
24898 * Fires when the data cache has changed, and a widget which is using this Store
24899 * as a Record cache should refresh its view.
24900 * @param {Store} this
24902 datachanged : true,
24904 * @event metachange
24905 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24906 * @param {Store} this
24907 * @param {Object} meta The JSON metadata
24912 * Fires when Records have been added to the Store
24913 * @param {Store} this
24914 * @param {Roo.data.Record[]} records The array of Records added
24915 * @param {Number} index The index at which the record(s) were added
24920 * Fires when a Record has been removed from the Store
24921 * @param {Store} this
24922 * @param {Roo.data.Record} record The Record that was removed
24923 * @param {Number} index The index at which the record was removed
24928 * Fires when a Record has been updated
24929 * @param {Store} this
24930 * @param {Roo.data.Record} record The Record that was updated
24931 * @param {String} operation The update operation being performed. Value may be one of:
24933 Roo.data.Record.EDIT
24934 Roo.data.Record.REJECT
24935 Roo.data.Record.COMMIT
24941 * Fires when the data cache has been cleared.
24942 * @param {Store} this
24946 * @event beforeload
24947 * Fires before a request is made for a new data object. If the beforeload handler returns false
24948 * the load action will be canceled.
24949 * @param {Store} this
24950 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24954 * @event beforeloadadd
24955 * Fires after a new set of Records has been loaded.
24956 * @param {Store} this
24957 * @param {Roo.data.Record[]} records The Records that were loaded
24958 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24960 beforeloadadd : true,
24963 * Fires after a new set of Records has been loaded, before they are added to the store.
24964 * @param {Store} this
24965 * @param {Roo.data.Record[]} records The Records that were loaded
24966 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24967 * @params {Object} return from reader
24971 * @event loadexception
24972 * Fires if an exception occurs in the Proxy during loading.
24973 * Called with the signature of the Proxy's "loadexception" event.
24974 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24977 * @param {Object} return from JsonData.reader() - success, totalRecords, records
24978 * @param {Object} load options
24979 * @param {Object} jsonData from your request (normally this contains the Exception)
24981 loadexception : true
24985 this.proxy = Roo.factory(this.proxy, Roo.data);
24986 this.proxy.xmodule = this.xmodule || false;
24987 this.relayEvents(this.proxy, ["loadexception"]);
24989 this.sortToggle = {};
24990 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24992 Roo.data.Store.superclass.constructor.call(this);
24994 if(this.inlineData){
24995 this.loadData(this.inlineData);
24996 delete this.inlineData;
25000 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25002 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
25003 * without a remote query - used by combo/forms at present.
25007 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25010 * @cfg {Array} data Inline data to be loaded when the store is initialized.
25013 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
25014 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25017 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25018 * on any HTTP request
25021 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25024 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25028 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25029 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25031 remoteSort : false,
25034 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25035 * loaded or when a record is removed. (defaults to false).
25037 pruneModifiedRecords : false,
25040 lastOptions : null,
25043 * Add Records to the Store and fires the add event.
25044 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25046 add : function(records){
25047 records = [].concat(records);
25048 for(var i = 0, len = records.length; i < len; i++){
25049 records[i].join(this);
25051 var index = this.data.length;
25052 this.data.addAll(records);
25053 this.fireEvent("add", this, records, index);
25057 * Remove a Record from the Store and fires the remove event.
25058 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25060 remove : function(record){
25061 var index = this.data.indexOf(record);
25062 this.data.removeAt(index);
25064 if(this.pruneModifiedRecords){
25065 this.modified.remove(record);
25067 this.fireEvent("remove", this, record, index);
25071 * Remove all Records from the Store and fires the clear event.
25073 removeAll : function(){
25075 if(this.pruneModifiedRecords){
25076 this.modified = [];
25078 this.fireEvent("clear", this);
25082 * Inserts Records to the Store at the given index and fires the add event.
25083 * @param {Number} index The start index at which to insert the passed Records.
25084 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25086 insert : function(index, records){
25087 records = [].concat(records);
25088 for(var i = 0, len = records.length; i < len; i++){
25089 this.data.insert(index, records[i]);
25090 records[i].join(this);
25092 this.fireEvent("add", this, records, index);
25096 * Get the index within the cache of the passed Record.
25097 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25098 * @return {Number} The index of the passed Record. Returns -1 if not found.
25100 indexOf : function(record){
25101 return this.data.indexOf(record);
25105 * Get the index within the cache of the Record with the passed id.
25106 * @param {String} id The id of the Record to find.
25107 * @return {Number} The index of the Record. Returns -1 if not found.
25109 indexOfId : function(id){
25110 return this.data.indexOfKey(id);
25114 * Get the Record with the specified id.
25115 * @param {String} id The id of the Record to find.
25116 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25118 getById : function(id){
25119 return this.data.key(id);
25123 * Get the Record at the specified index.
25124 * @param {Number} index The index of the Record to find.
25125 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25127 getAt : function(index){
25128 return this.data.itemAt(index);
25132 * Returns a range of Records between specified indices.
25133 * @param {Number} startIndex (optional) The starting index (defaults to 0)
25134 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25135 * @return {Roo.data.Record[]} An array of Records
25137 getRange : function(start, end){
25138 return this.data.getRange(start, end);
25142 storeOptions : function(o){
25143 o = Roo.apply({}, o);
25146 this.lastOptions = o;
25150 * Loads the Record cache from the configured Proxy using the configured Reader.
25152 * If using remote paging, then the first load call must specify the <em>start</em>
25153 * and <em>limit</em> properties in the options.params property to establish the initial
25154 * position within the dataset, and the number of Records to cache on each read from the Proxy.
25156 * <strong>It is important to note that for remote data sources, loading is asynchronous,
25157 * and this call will return before the new data has been loaded. Perform any post-processing
25158 * in a callback function, or in a "load" event handler.</strong>
25160 * @param {Object} options An object containing properties which control loading options:<ul>
25161 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25162 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25165 data : data, // array of key=>value data like JsonReader
25166 total : data.length,
25172 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25173 * passed the following arguments:<ul>
25174 * <li>r : Roo.data.Record[]</li>
25175 * <li>options: Options object from the load call</li>
25176 * <li>success: Boolean success indicator</li></ul></li>
25177 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25178 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25181 load : function(options){
25182 options = options || {};
25183 if(this.fireEvent("beforeload", this, options) !== false){
25184 this.storeOptions(options);
25185 var p = Roo.apply(options.params || {}, this.baseParams);
25186 // if meta was not loaded from remote source.. try requesting it.
25187 if (!this.reader.metaFromRemote) {
25188 p._requestMeta = 1;
25190 if(this.sortInfo && this.remoteSort){
25191 var pn = this.paramNames;
25192 p[pn["sort"]] = this.sortInfo.field;
25193 p[pn["dir"]] = this.sortInfo.direction;
25195 if (this.multiSort) {
25196 var pn = this.paramNames;
25197 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25200 this.proxy.load(p, this.reader, this.loadRecords, this, options);
25205 * Reloads the Record cache from the configured Proxy using the configured Reader and
25206 * the options from the last load operation performed.
25207 * @param {Object} options (optional) An object containing properties which may override the options
25208 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25209 * the most recently used options are reused).
25211 reload : function(options){
25212 this.load(Roo.applyIf(options||{}, this.lastOptions));
25216 // Called as a callback by the Reader during a load operation.
25217 loadRecords : function(o, options, success){
25220 if(success !== false){
25221 this.fireEvent("load", this, [], options, o);
25223 if(options.callback){
25224 options.callback.call(options.scope || this, [], options, false);
25228 // if data returned failure - throw an exception.
25229 if (o.success === false) {
25230 // show a message if no listener is registered.
25231 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25232 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25234 // loadmask wil be hooked into this..
25235 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25238 var r = o.records, t = o.totalRecords || r.length;
25240 this.fireEvent("beforeloadadd", this, r, options, o);
25242 if(!options || options.add !== true){
25243 if(this.pruneModifiedRecords){
25244 this.modified = [];
25246 for(var i = 0, len = r.length; i < len; i++){
25250 this.data = this.snapshot;
25251 delete this.snapshot;
25254 this.data.addAll(r);
25255 this.totalLength = t;
25257 this.fireEvent("datachanged", this);
25259 this.totalLength = Math.max(t, this.data.length+r.length);
25263 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25265 var e = new Roo.data.Record({});
25267 e.set(this.parent.displayField, this.parent.emptyTitle);
25268 e.set(this.parent.valueField, '');
25273 this.fireEvent("load", this, r, options, o);
25274 if(options.callback){
25275 options.callback.call(options.scope || this, r, options, true);
25281 * Loads data from a passed data block. A Reader which understands the format of the data
25282 * must have been configured in the constructor.
25283 * @param {Object} data The data block from which to read the Records. The format of the data expected
25284 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25285 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25287 loadData : function(o, append){
25288 var r = this.reader.readRecords(o);
25289 this.loadRecords(r, {add: append}, true);
25293 * using 'cn' the nested child reader read the child array into it's child stores.
25294 * @param {Object} rec The record with a 'children array
25296 loadDataFromChildren : function(rec)
25298 this.loadData(this.reader.toLoadData(rec));
25303 * Gets the number of cached records.
25305 * <em>If using paging, this may not be the total size of the dataset. If the data object
25306 * used by the Reader contains the dataset size, then the getTotalCount() function returns
25307 * the data set size</em>
25309 getCount : function(){
25310 return this.data.length || 0;
25314 * Gets the total number of records in the dataset as returned by the server.
25316 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25317 * the dataset size</em>
25319 getTotalCount : function(){
25320 return this.totalLength || 0;
25324 * Returns the sort state of the Store as an object with two properties:
25326 field {String} The name of the field by which the Records are sorted
25327 direction {String} The sort order, "ASC" or "DESC"
25330 getSortState : function(){
25331 return this.sortInfo;
25335 applySort : function(){
25336 if(this.sortInfo && !this.remoteSort){
25337 var s = this.sortInfo, f = s.field;
25338 var st = this.fields.get(f).sortType;
25339 var fn = function(r1, r2){
25340 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25341 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25343 this.data.sort(s.direction, fn);
25344 if(this.snapshot && this.snapshot != this.data){
25345 this.snapshot.sort(s.direction, fn);
25351 * Sets the default sort column and order to be used by the next load operation.
25352 * @param {String} fieldName The name of the field to sort by.
25353 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25355 setDefaultSort : function(field, dir){
25356 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25360 * Sort the Records.
25361 * If remote sorting is used, the sort is performed on the server, and the cache is
25362 * reloaded. If local sorting is used, the cache is sorted internally.
25363 * @param {String} fieldName The name of the field to sort by.
25364 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25366 sort : function(fieldName, dir){
25367 var f = this.fields.get(fieldName);
25369 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25371 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25372 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25377 this.sortToggle[f.name] = dir;
25378 this.sortInfo = {field: f.name, direction: dir};
25379 if(!this.remoteSort){
25381 this.fireEvent("datachanged", this);
25383 this.load(this.lastOptions);
25388 * Calls the specified function for each of the Records in the cache.
25389 * @param {Function} fn The function to call. The Record is passed as the first parameter.
25390 * Returning <em>false</em> aborts and exits the iteration.
25391 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25393 each : function(fn, scope){
25394 this.data.each(fn, scope);
25398 * Gets all records modified since the last commit. Modified records are persisted across load operations
25399 * (e.g., during paging).
25400 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25402 getModifiedRecords : function(){
25403 return this.modified;
25407 createFilterFn : function(property, value, anyMatch){
25408 if(!value.exec){ // not a regex
25409 value = String(value);
25410 if(value.length == 0){
25413 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25415 return function(r){
25416 return value.test(r.data[property]);
25421 * Sums the value of <i>property</i> for each record between start and end and returns the result.
25422 * @param {String} property A field on your records
25423 * @param {Number} start The record index to start at (defaults to 0)
25424 * @param {Number} end The last record index to include (defaults to length - 1)
25425 * @return {Number} The sum
25427 sum : function(property, start, end){
25428 var rs = this.data.items, v = 0;
25429 start = start || 0;
25430 end = (end || end === 0) ? end : rs.length-1;
25432 for(var i = start; i <= end; i++){
25433 v += (rs[i].data[property] || 0);
25439 * Filter the records by a specified property.
25440 * @param {String} field A field on your records
25441 * @param {String/RegExp} value Either a string that the field
25442 * should start with or a RegExp to test against the field
25443 * @param {Boolean} anyMatch True to match any part not just the beginning
25445 filter : function(property, value, anyMatch){
25446 var fn = this.createFilterFn(property, value, anyMatch);
25447 return fn ? this.filterBy(fn) : this.clearFilter();
25451 * Filter by a function. The specified function will be called with each
25452 * record in this data source. If the function returns true the record is included,
25453 * otherwise it is filtered.
25454 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25455 * @param {Object} scope (optional) The scope of the function (defaults to this)
25457 filterBy : function(fn, scope){
25458 this.snapshot = this.snapshot || this.data;
25459 this.data = this.queryBy(fn, scope||this);
25460 this.fireEvent("datachanged", this);
25464 * Query the records by a specified property.
25465 * @param {String} field A field on your records
25466 * @param {String/RegExp} value Either a string that the field
25467 * should start with or a RegExp to test against the field
25468 * @param {Boolean} anyMatch True to match any part not just the beginning
25469 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25471 query : function(property, value, anyMatch){
25472 var fn = this.createFilterFn(property, value, anyMatch);
25473 return fn ? this.queryBy(fn) : this.data.clone();
25477 * Query by a function. The specified function will be called with each
25478 * record in this data source. If the function returns true the record is included
25480 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25481 * @param {Object} scope (optional) The scope of the function (defaults to this)
25482 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25484 queryBy : function(fn, scope){
25485 var data = this.snapshot || this.data;
25486 return data.filterBy(fn, scope||this);
25490 * Collects unique values for a particular dataIndex from this store.
25491 * @param {String} dataIndex The property to collect
25492 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25493 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25494 * @return {Array} An array of the unique values
25496 collect : function(dataIndex, allowNull, bypassFilter){
25497 var d = (bypassFilter === true && this.snapshot) ?
25498 this.snapshot.items : this.data.items;
25499 var v, sv, r = [], l = {};
25500 for(var i = 0, len = d.length; i < len; i++){
25501 v = d[i].data[dataIndex];
25503 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25512 * Revert to a view of the Record cache with no filtering applied.
25513 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25515 clearFilter : function(suppressEvent){
25516 if(this.snapshot && this.snapshot != this.data){
25517 this.data = this.snapshot;
25518 delete this.snapshot;
25519 if(suppressEvent !== true){
25520 this.fireEvent("datachanged", this);
25526 afterEdit : function(record){
25527 if(this.modified.indexOf(record) == -1){
25528 this.modified.push(record);
25530 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25534 afterReject : function(record){
25535 this.modified.remove(record);
25536 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25540 afterCommit : function(record){
25541 this.modified.remove(record);
25542 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25546 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25547 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25549 commitChanges : function(){
25550 var m = this.modified.slice(0);
25551 this.modified = [];
25552 for(var i = 0, len = m.length; i < len; i++){
25558 * Cancel outstanding changes on all changed records.
25560 rejectChanges : function(){
25561 var m = this.modified.slice(0);
25562 this.modified = [];
25563 for(var i = 0, len = m.length; i < len; i++){
25568 onMetaChange : function(meta, rtype, o){
25569 this.recordType = rtype;
25570 this.fields = rtype.prototype.fields;
25571 delete this.snapshot;
25572 this.sortInfo = meta.sortInfo || this.sortInfo;
25573 this.modified = [];
25574 this.fireEvent('metachange', this, this.reader.meta);
25577 moveIndex : function(data, type)
25579 var index = this.indexOf(data);
25581 var newIndex = index + type;
25585 this.insert(newIndex, data);
25590 * Ext JS Library 1.1.1
25591 * Copyright(c) 2006-2007, Ext JS, LLC.
25593 * Originally Released Under LGPL - original licence link has changed is not relivant.
25596 * <script type="text/javascript">
25600 * @class Roo.data.SimpleStore
25601 * @extends Roo.data.Store
25602 * Small helper class to make creating Stores from Array data easier.
25603 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25604 * @cfg {Array} fields An array of field definition objects, or field name strings.
25605 * @cfg {Object} an existing reader (eg. copied from another store)
25606 * @cfg {Array} data The multi-dimensional array of data
25607 * @cfg {Roo.data.DataProxy} proxy [not-required]
25608 * @cfg {Roo.data.Reader} reader [not-required]
25610 * @param {Object} config
25612 Roo.data.SimpleStore = function(config)
25614 Roo.data.SimpleStore.superclass.constructor.call(this, {
25616 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25619 Roo.data.Record.create(config.fields)
25621 proxy : new Roo.data.MemoryProxy(config.data)
25625 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25627 * Ext JS Library 1.1.1
25628 * Copyright(c) 2006-2007, Ext JS, LLC.
25630 * Originally Released Under LGPL - original licence link has changed is not relivant.
25633 * <script type="text/javascript">
25638 * @extends Roo.data.Store
25639 * @class Roo.data.JsonStore
25640 * Small helper class to make creating Stores for JSON data easier. <br/>
25642 var store = new Roo.data.JsonStore({
25643 url: 'get-images.php',
25645 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25648 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25649 * JsonReader and HttpProxy (unless inline data is provided).</b>
25650 * @cfg {Array} fields An array of field definition objects, or field name strings.
25652 * @param {Object} config
25654 Roo.data.JsonStore = function(c){
25655 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25656 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25657 reader: new Roo.data.JsonReader(c, c.fields)
25660 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25662 * Ext JS Library 1.1.1
25663 * Copyright(c) 2006-2007, Ext JS, LLC.
25665 * Originally Released Under LGPL - original licence link has changed is not relivant.
25668 * <script type="text/javascript">
25672 Roo.data.Field = function(config){
25673 if(typeof config == "string"){
25674 config = {name: config};
25676 Roo.apply(this, config);
25679 this.type = "auto";
25682 var st = Roo.data.SortTypes;
25683 // named sortTypes are supported, here we look them up
25684 if(typeof this.sortType == "string"){
25685 this.sortType = st[this.sortType];
25688 // set default sortType for strings and dates
25689 if(!this.sortType){
25692 this.sortType = st.asUCString;
25695 this.sortType = st.asDate;
25698 this.sortType = st.none;
25703 var stripRe = /[\$,%]/g;
25705 // prebuilt conversion function for this field, instead of
25706 // switching every time we're reading a value
25708 var cv, dateFormat = this.dateFormat;
25713 cv = function(v){ return v; };
25716 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25720 return v !== undefined && v !== null && v !== '' ?
25721 parseInt(String(v).replace(stripRe, ""), 10) : '';
25726 return v !== undefined && v !== null && v !== '' ?
25727 parseFloat(String(v).replace(stripRe, ""), 10) : '';
25732 cv = function(v){ return v === true || v === "true" || v == 1; };
25739 if(v instanceof Date){
25743 if(dateFormat == "timestamp"){
25744 return new Date(v*1000);
25746 return Date.parseDate(v, dateFormat);
25748 var parsed = Date.parse(v);
25749 return parsed ? new Date(parsed) : null;
25758 Roo.data.Field.prototype = {
25766 * Ext JS Library 1.1.1
25767 * Copyright(c) 2006-2007, Ext JS, LLC.
25769 * Originally Released Under LGPL - original licence link has changed is not relivant.
25772 * <script type="text/javascript">
25775 // Base class for reading structured data from a data source. This class is intended to be
25776 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25779 * @class Roo.data.DataReader
25781 * Base class for reading structured data from a data source. This class is intended to be
25782 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25785 Roo.data.DataReader = function(meta, recordType){
25789 this.recordType = recordType instanceof Array ?
25790 Roo.data.Record.create(recordType) : recordType;
25793 Roo.data.DataReader.prototype = {
25796 readerType : 'Data',
25798 * Create an empty record
25799 * @param {Object} data (optional) - overlay some values
25800 * @return {Roo.data.Record} record created.
25802 newRow : function(d) {
25804 this.recordType.prototype.fields.each(function(c) {
25806 case 'int' : da[c.name] = 0; break;
25807 case 'date' : da[c.name] = new Date(); break;
25808 case 'float' : da[c.name] = 0.0; break;
25809 case 'boolean' : da[c.name] = false; break;
25810 default : da[c.name] = ""; break;
25814 return new this.recordType(Roo.apply(da, d));
25820 * Ext JS Library 1.1.1
25821 * Copyright(c) 2006-2007, Ext JS, LLC.
25823 * Originally Released Under LGPL - original licence link has changed is not relivant.
25826 * <script type="text/javascript">
25830 * @class Roo.data.DataProxy
25831 * @extends Roo.util.Observable
25833 * This class is an abstract base class for implementations which provide retrieval of
25834 * unformatted data objects.<br>
25836 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25837 * (of the appropriate type which knows how to parse the data object) to provide a block of
25838 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25840 * Custom implementations must implement the load method as described in
25841 * {@link Roo.data.HttpProxy#load}.
25843 Roo.data.DataProxy = function(){
25846 * @event beforeload
25847 * Fires before a network request is made to retrieve a data object.
25848 * @param {Object} This DataProxy object.
25849 * @param {Object} params The params parameter to the load function.
25854 * Fires before the load method's callback is called.
25855 * @param {Object} This DataProxy object.
25856 * @param {Object} o The data object.
25857 * @param {Object} arg The callback argument object passed to the load function.
25861 * @event loadexception
25862 * Fires if an Exception occurs during data retrieval.
25863 * @param {Object} This DataProxy object.
25864 * @param {Object} o The data object.
25865 * @param {Object} arg The callback argument object passed to the load function.
25866 * @param {Object} e The Exception.
25868 loadexception : true
25870 Roo.data.DataProxy.superclass.constructor.call(this);
25873 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25876 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25880 * Ext JS Library 1.1.1
25881 * Copyright(c) 2006-2007, Ext JS, LLC.
25883 * Originally Released Under LGPL - original licence link has changed is not relivant.
25886 * <script type="text/javascript">
25889 * @class Roo.data.MemoryProxy
25890 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25891 * to the Reader when its load method is called.
25893 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25895 Roo.data.MemoryProxy = function(data){
25899 Roo.data.MemoryProxy.superclass.constructor.call(this);
25903 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25906 * Load data from the requested source (in this case an in-memory
25907 * data object passed to the constructor), read the data object into
25908 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25909 * process that block using the passed callback.
25910 * @param {Object} params This parameter is not used by the MemoryProxy class.
25911 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25912 * object into a block of Roo.data.Records.
25913 * @param {Function} callback The function into which to pass the block of Roo.data.records.
25914 * The function must be passed <ul>
25915 * <li>The Record block object</li>
25916 * <li>The "arg" argument from the load function</li>
25917 * <li>A boolean success indicator</li>
25919 * @param {Object} scope The scope in which to call the callback
25920 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25922 load : function(params, reader, callback, scope, arg){
25923 params = params || {};
25926 result = reader.readRecords(params.data ? params.data :this.data);
25928 this.fireEvent("loadexception", this, arg, null, e);
25929 callback.call(scope, null, arg, false);
25932 callback.call(scope, result, arg, true);
25936 update : function(params, records){
25941 * Ext JS Library 1.1.1
25942 * Copyright(c) 2006-2007, Ext JS, LLC.
25944 * Originally Released Under LGPL - original licence link has changed is not relivant.
25947 * <script type="text/javascript">
25950 * @class Roo.data.HttpProxy
25951 * @extends Roo.data.DataProxy
25952 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25953 * configured to reference a certain URL.<br><br>
25955 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25956 * from which the running page was served.<br><br>
25958 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25960 * Be aware that to enable the browser to parse an XML document, the server must set
25961 * the Content-Type header in the HTTP response to "text/xml".
25963 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25964 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
25965 * will be used to make the request.
25967 Roo.data.HttpProxy = function(conn){
25968 Roo.data.HttpProxy.superclass.constructor.call(this);
25969 // is conn a conn config or a real conn?
25971 this.useAjax = !conn || !conn.events;
25975 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25976 // thse are take from connection...
25979 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25982 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25983 * extra parameters to each request made by this object. (defaults to undefined)
25986 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25987 * to each request made by this object. (defaults to undefined)
25990 * @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)
25993 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25996 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
26002 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26006 * Return the {@link Roo.data.Connection} object being used by this Proxy.
26007 * @return {Connection} The Connection object. This object may be used to subscribe to events on
26008 * a finer-grained basis than the DataProxy events.
26010 getConnection : function(){
26011 return this.useAjax ? Roo.Ajax : this.conn;
26015 * Load data from the configured {@link Roo.data.Connection}, read the data object into
26016 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26017 * process that block using the passed callback.
26018 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26019 * for the request to the remote server.
26020 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26021 * object into a block of Roo.data.Records.
26022 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26023 * The function must be passed <ul>
26024 * <li>The Record block object</li>
26025 * <li>The "arg" argument from the load function</li>
26026 * <li>A boolean success indicator</li>
26028 * @param {Object} scope The scope in which to call the callback
26029 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26031 load : function(params, reader, callback, scope, arg){
26032 if(this.fireEvent("beforeload", this, params) !== false){
26034 params : params || {},
26036 callback : callback,
26041 callback : this.loadResponse,
26045 Roo.applyIf(o, this.conn);
26046 if(this.activeRequest){
26047 Roo.Ajax.abort(this.activeRequest);
26049 this.activeRequest = Roo.Ajax.request(o);
26051 this.conn.request(o);
26054 callback.call(scope||this, null, arg, false);
26059 loadResponse : function(o, success, response){
26060 delete this.activeRequest;
26062 this.fireEvent("loadexception", this, o, response);
26063 o.request.callback.call(o.request.scope, null, o.request.arg, false);
26068 result = o.reader.read(response);
26071 o.raw = { errorMsg : response.responseText };
26072 this.fireEvent("loadexception", this, o, response, e);
26073 o.request.callback.call(o.request.scope, o, o.request.arg, false);
26077 this.fireEvent("load", this, o, o.request.arg);
26078 o.request.callback.call(o.request.scope, result, o.request.arg, true);
26082 update : function(dataSet){
26087 updateResponse : function(dataSet){
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.ScriptTagProxy
26103 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26104 * other than the originating domain of the running page.<br><br>
26106 * <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
26107 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26109 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26110 * source code that is used as the source inside a <script> tag.<br><br>
26112 * In order for the browser to process the returned data, the server must wrap the data object
26113 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26114 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26115 * depending on whether the callback name was passed:
26118 boolean scriptTag = false;
26119 String cb = request.getParameter("callback");
26122 response.setContentType("text/javascript");
26124 response.setContentType("application/x-json");
26126 Writer out = response.getWriter();
26128 out.write(cb + "(");
26130 out.print(dataBlock.toJsonString());
26137 * @param {Object} config A configuration object.
26139 Roo.data.ScriptTagProxy = function(config){
26140 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26141 Roo.apply(this, config);
26142 this.head = document.getElementsByTagName("head")[0];
26145 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26147 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26149 * @cfg {String} url The URL from which to request the data object.
26152 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26156 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26157 * the server the name of the callback function set up by the load call to process the returned data object.
26158 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26159 * javascript output which calls this named function passing the data object as its only parameter.
26161 callbackParam : "callback",
26163 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26164 * name to the request.
26169 * Load data from the configured URL, read the data object into
26170 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26171 * process that block using the passed callback.
26172 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26173 * for the request to the remote server.
26174 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26175 * object into a block of Roo.data.Records.
26176 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26177 * The function must be passed <ul>
26178 * <li>The Record block object</li>
26179 * <li>The "arg" argument from the load function</li>
26180 * <li>A boolean success indicator</li>
26182 * @param {Object} scope The scope in which to call the callback
26183 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26185 load : function(params, reader, callback, scope, arg){
26186 if(this.fireEvent("beforeload", this, params) !== false){
26188 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26190 var url = this.url;
26191 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26193 url += "&_dc=" + (new Date().getTime());
26195 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26198 cb : "stcCallback"+transId,
26199 scriptId : "stcScript"+transId,
26203 callback : callback,
26209 window[trans.cb] = function(o){
26210 conn.handleResponse(o, trans);
26213 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26215 if(this.autoAbort !== false){
26219 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26221 var script = document.createElement("script");
26222 script.setAttribute("src", url);
26223 script.setAttribute("type", "text/javascript");
26224 script.setAttribute("id", trans.scriptId);
26225 this.head.appendChild(script);
26227 this.trans = trans;
26229 callback.call(scope||this, null, arg, false);
26234 isLoading : function(){
26235 return this.trans ? true : false;
26239 * Abort the current server request.
26241 abort : function(){
26242 if(this.isLoading()){
26243 this.destroyTrans(this.trans);
26248 destroyTrans : function(trans, isLoaded){
26249 this.head.removeChild(document.getElementById(trans.scriptId));
26250 clearTimeout(trans.timeoutId);
26252 window[trans.cb] = undefined;
26254 delete window[trans.cb];
26257 // if hasn't been loaded, wait for load to remove it to prevent script error
26258 window[trans.cb] = function(){
26259 window[trans.cb] = undefined;
26261 delete window[trans.cb];
26268 handleResponse : function(o, trans){
26269 this.trans = false;
26270 this.destroyTrans(trans, true);
26273 result = trans.reader.readRecords(o);
26275 this.fireEvent("loadexception", this, o, trans.arg, e);
26276 trans.callback.call(trans.scope||window, null, trans.arg, false);
26279 this.fireEvent("load", this, o, trans.arg);
26280 trans.callback.call(trans.scope||window, result, trans.arg, true);
26284 handleFailure : function(trans){
26285 this.trans = false;
26286 this.destroyTrans(trans, false);
26287 this.fireEvent("loadexception", this, null, trans.arg);
26288 trans.callback.call(trans.scope||window, null, trans.arg, false);
26292 * Ext JS Library 1.1.1
26293 * Copyright(c) 2006-2007, Ext JS, LLC.
26295 * Originally Released Under LGPL - original licence link has changed is not relivant.
26298 * <script type="text/javascript">
26302 * @class Roo.data.JsonReader
26303 * @extends Roo.data.DataReader
26304 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26305 * based on mappings in a provided Roo.data.Record constructor.
26307 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26308 * in the reply previously.
26313 var RecordDef = Roo.data.Record.create([
26314 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26315 {name: 'occupation'} // This field will use "occupation" as the mapping.
26317 var myReader = new Roo.data.JsonReader({
26318 totalProperty: "results", // The property which contains the total dataset size (optional)
26319 root: "rows", // The property which contains an Array of row objects
26320 id: "id" // The property within each row object that provides an ID for the record (optional)
26324 * This would consume a JSON file like this:
26326 { 'results': 2, 'rows': [
26327 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26328 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26331 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26332 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26333 * paged from the remote server.
26334 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26335 * @cfg {String} root name of the property which contains the Array of row objects.
26336 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26337 * @cfg {Array} fields Array of field definition objects
26339 * Create a new JsonReader
26340 * @param {Object} meta Metadata configuration options
26341 * @param {Object} recordType Either an Array of field definition objects,
26342 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26344 Roo.data.JsonReader = function(meta, recordType){
26347 // set some defaults:
26348 Roo.applyIf(meta, {
26349 totalProperty: 'total',
26350 successProperty : 'success',
26355 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26357 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26359 readerType : 'Json',
26362 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
26363 * Used by Store query builder to append _requestMeta to params.
26366 metaFromRemote : false,
26368 * This method is only used by a DataProxy which has retrieved data from a remote server.
26369 * @param {Object} response The XHR object which contains the JSON data in its responseText.
26370 * @return {Object} data A data block which is used by an Roo.data.Store object as
26371 * a cache of Roo.data.Records.
26373 read : function(response){
26374 var json = response.responseText;
26376 var o = /* eval:var:o */ eval("("+json+")");
26378 throw {message: "JsonReader.read: Json object not found"};
26384 this.metaFromRemote = true;
26385 this.meta = o.metaData;
26386 this.recordType = Roo.data.Record.create(o.metaData.fields);
26387 this.onMetaChange(this.meta, this.recordType, o);
26389 return this.readRecords(o);
26392 // private function a store will implement
26393 onMetaChange : function(meta, recordType, o){
26400 simpleAccess: function(obj, subsc) {
26407 getJsonAccessor: function(){
26409 return function(expr) {
26411 return(re.test(expr))
26412 ? new Function("obj", "return obj." + expr)
26417 return Roo.emptyFn;
26422 * Create a data block containing Roo.data.Records from an XML document.
26423 * @param {Object} o An object which contains an Array of row objects in the property specified
26424 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26425 * which contains the total size of the dataset.
26426 * @return {Object} data A data block which is used by an Roo.data.Store object as
26427 * a cache of Roo.data.Records.
26429 readRecords : function(o){
26431 * After any data loads, the raw JSON data is available for further custom processing.
26435 var s = this.meta, Record = this.recordType,
26436 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26438 // Generate extraction functions for the totalProperty, the root, the id, and for each field
26440 if(s.totalProperty) {
26441 this.getTotal = this.getJsonAccessor(s.totalProperty);
26443 if(s.successProperty) {
26444 this.getSuccess = this.getJsonAccessor(s.successProperty);
26446 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26448 var g = this.getJsonAccessor(s.id);
26449 this.getId = function(rec) {
26451 return (r === undefined || r === "") ? null : r;
26454 this.getId = function(){return null;};
26457 for(var jj = 0; jj < fl; jj++){
26459 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26460 this.ef[jj] = this.getJsonAccessor(map);
26464 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26465 if(s.totalProperty){
26466 var vt = parseInt(this.getTotal(o), 10);
26471 if(s.successProperty){
26472 var vs = this.getSuccess(o);
26473 if(vs === false || vs === 'false'){
26478 for(var i = 0; i < c; i++){
26481 var id = this.getId(n);
26482 for(var j = 0; j < fl; j++){
26484 var v = this.ef[j](n);
26486 Roo.log('missing convert for ' + f.name);
26490 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26494 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26500 var record = new Record(values, id);
26502 records[i] = record;
26508 totalRecords : totalRecords
26511 // used when loading children.. @see loadDataFromChildren
26512 toLoadData: function(rec)
26514 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26515 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26516 return { data : data, total : data.length };
26521 * Ext JS Library 1.1.1
26522 * Copyright(c) 2006-2007, Ext JS, LLC.
26524 * Originally Released Under LGPL - original licence link has changed is not relivant.
26527 * <script type="text/javascript">
26531 * @class Roo.data.XmlReader
26532 * @extends Roo.data.DataReader
26533 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26534 * based on mappings in a provided Roo.data.Record constructor.<br><br>
26536 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26537 * header in the HTTP response must be set to "text/xml".</em>
26541 var RecordDef = Roo.data.Record.create([
26542 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26543 {name: 'occupation'} // This field will use "occupation" as the mapping.
26545 var myReader = new Roo.data.XmlReader({
26546 totalRecords: "results", // The element which contains the total dataset size (optional)
26547 record: "row", // The repeated element which contains row information
26548 id: "id" // The element within the row that provides an ID for the record (optional)
26552 * This would consume an XML file like this:
26556 <results>2</results>
26559 <name>Bill</name>
26560 <occupation>Gardener</occupation>
26564 <name>Ben</name>
26565 <occupation>Horticulturalist</occupation>
26569 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26570 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26571 * paged from the remote server.
26572 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26573 * @cfg {String} success The DomQuery path to the success attribute used by forms.
26574 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26575 * a record identifier value.
26577 * Create a new XmlReader
26578 * @param {Object} meta Metadata configuration options
26579 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
26580 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26581 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
26583 Roo.data.XmlReader = function(meta, recordType){
26585 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26587 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26589 readerType : 'Xml',
26592 * This method is only used by a DataProxy which has retrieved data from a remote server.
26593 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
26594 * to contain a method called 'responseXML' that returns an XML document object.
26595 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26596 * a cache of Roo.data.Records.
26598 read : function(response){
26599 var doc = response.responseXML;
26601 throw {message: "XmlReader.read: XML Document not available"};
26603 return this.readRecords(doc);
26607 * Create a data block containing Roo.data.Records from an XML document.
26608 * @param {Object} doc A parsed XML document.
26609 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26610 * a cache of Roo.data.Records.
26612 readRecords : function(doc){
26614 * After any data loads/reads, the raw XML Document is available for further custom processing.
26615 * @type XMLDocument
26617 this.xmlData = doc;
26618 var root = doc.documentElement || doc;
26619 var q = Roo.DomQuery;
26620 var recordType = this.recordType, fields = recordType.prototype.fields;
26621 var sid = this.meta.id;
26622 var totalRecords = 0, success = true;
26623 if(this.meta.totalRecords){
26624 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26627 if(this.meta.success){
26628 var sv = q.selectValue(this.meta.success, root, true);
26629 success = sv !== false && sv !== 'false';
26632 var ns = q.select(this.meta.record, root);
26633 for(var i = 0, len = ns.length; i < len; i++) {
26636 var id = sid ? q.selectValue(sid, n) : undefined;
26637 for(var j = 0, jlen = fields.length; j < jlen; j++){
26638 var f = fields.items[j];
26639 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26641 values[f.name] = v;
26643 var record = new recordType(values, id);
26645 records[records.length] = record;
26651 totalRecords : totalRecords || records.length
26656 * Ext JS Library 1.1.1
26657 * Copyright(c) 2006-2007, Ext JS, LLC.
26659 * Originally Released Under LGPL - original licence link has changed is not relivant.
26662 * <script type="text/javascript">
26666 * @class Roo.data.ArrayReader
26667 * @extends Roo.data.DataReader
26668 * Data reader class to create an Array of Roo.data.Record objects from an Array.
26669 * Each element of that Array represents a row of data fields. The
26670 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26671 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26675 var RecordDef = Roo.data.Record.create([
26676 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
26677 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
26679 var myReader = new Roo.data.ArrayReader({
26680 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
26684 * This would consume an Array like this:
26686 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26690 * Create a new JsonReader
26691 * @param {Object} meta Metadata configuration options.
26692 * @param {Object|Array} recordType Either an Array of field definition objects
26694 * @cfg {Array} fields Array of field definition objects
26695 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26696 * as specified to {@link Roo.data.Record#create},
26697 * or an {@link Roo.data.Record} object
26700 * created using {@link Roo.data.Record#create}.
26702 Roo.data.ArrayReader = function(meta, recordType)
26704 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26707 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26710 * Create a data block containing Roo.data.Records from an XML document.
26711 * @param {Object} o An Array of row objects which represents the dataset.
26712 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26713 * a cache of Roo.data.Records.
26715 readRecords : function(o)
26717 var sid = this.meta ? this.meta.id : null;
26718 var recordType = this.recordType, fields = recordType.prototype.fields;
26721 for(var i = 0; i < root.length; i++){
26724 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26725 for(var j = 0, jlen = fields.length; j < jlen; j++){
26726 var f = fields.items[j];
26727 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26728 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26730 values[f.name] = v;
26732 var record = new recordType(values, id);
26734 records[records.length] = record;
26738 totalRecords : records.length
26741 // used when loading children.. @see loadDataFromChildren
26742 toLoadData: function(rec)
26744 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26745 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26752 * Ext JS Library 1.1.1
26753 * Copyright(c) 2006-2007, Ext JS, LLC.
26755 * Originally Released Under LGPL - original licence link has changed is not relivant.
26758 * <script type="text/javascript">
26763 * @class Roo.data.Tree
26764 * @extends Roo.util.Observable
26765 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26766 * in the tree have most standard DOM functionality.
26768 * @param {Node} root (optional) The root node
26770 Roo.data.Tree = function(root){
26771 this.nodeHash = {};
26773 * The root node for this tree
26778 this.setRootNode(root);
26783 * Fires when a new child node is appended to a node in this tree.
26784 * @param {Tree} tree The owner tree
26785 * @param {Node} parent The parent node
26786 * @param {Node} node The newly appended node
26787 * @param {Number} index The index of the newly appended node
26792 * Fires when a child node is removed from a node in this tree.
26793 * @param {Tree} tree The owner tree
26794 * @param {Node} parent The parent node
26795 * @param {Node} node The child node removed
26800 * Fires when a node is moved to a new location in the tree
26801 * @param {Tree} tree The owner tree
26802 * @param {Node} node The node moved
26803 * @param {Node} oldParent The old parent of this node
26804 * @param {Node} newParent The new parent of this node
26805 * @param {Number} index The index it was moved to
26810 * Fires when a new child node is inserted in a node in this tree.
26811 * @param {Tree} tree The owner tree
26812 * @param {Node} parent The parent node
26813 * @param {Node} node The child node inserted
26814 * @param {Node} refNode The child node the node was inserted before
26818 * @event beforeappend
26819 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26820 * @param {Tree} tree The owner tree
26821 * @param {Node} parent The parent node
26822 * @param {Node} node The child node to be appended
26824 "beforeappend" : true,
26826 * @event beforeremove
26827 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26828 * @param {Tree} tree The owner tree
26829 * @param {Node} parent The parent node
26830 * @param {Node} node The child node to be removed
26832 "beforeremove" : true,
26834 * @event beforemove
26835 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26836 * @param {Tree} tree The owner tree
26837 * @param {Node} node The node being moved
26838 * @param {Node} oldParent The parent of the node
26839 * @param {Node} newParent The new parent the node is moving to
26840 * @param {Number} index The index it is being moved to
26842 "beforemove" : true,
26844 * @event beforeinsert
26845 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26846 * @param {Tree} tree The owner tree
26847 * @param {Node} parent The parent node
26848 * @param {Node} node The child node to be inserted
26849 * @param {Node} refNode The child node the node is being inserted before
26851 "beforeinsert" : true
26854 Roo.data.Tree.superclass.constructor.call(this);
26857 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26858 pathSeparator: "/",
26860 proxyNodeEvent : function(){
26861 return this.fireEvent.apply(this, arguments);
26865 * Returns the root node for this tree.
26868 getRootNode : function(){
26873 * Sets the root node for this tree.
26874 * @param {Node} node
26877 setRootNode : function(node){
26879 node.ownerTree = this;
26880 node.isRoot = true;
26881 this.registerNode(node);
26886 * Gets a node in this tree by its id.
26887 * @param {String} id
26890 getNodeById : function(id){
26891 return this.nodeHash[id];
26894 registerNode : function(node){
26895 this.nodeHash[node.id] = node;
26898 unregisterNode : function(node){
26899 delete this.nodeHash[node.id];
26902 toString : function(){
26903 return "[Tree"+(this.id?" "+this.id:"")+"]";
26908 * @class Roo.data.Node
26909 * @extends Roo.util.Observable
26910 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26911 * @cfg {String} id The id for this node. If one is not specified, one is generated.
26913 * @param {Object} attributes The attributes/config for the node
26915 Roo.data.Node = function(attributes){
26917 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26920 this.attributes = attributes || {};
26921 this.leaf = this.attributes.leaf;
26923 * The node id. @type String
26925 this.id = this.attributes.id;
26927 this.id = Roo.id(null, "ynode-");
26928 this.attributes.id = this.id;
26933 * All child nodes of this node. @type Array
26935 this.childNodes = [];
26936 if(!this.childNodes.indexOf){ // indexOf is a must
26937 this.childNodes.indexOf = function(o){
26938 for(var i = 0, len = this.length; i < len; i++){
26947 * The parent node for this node. @type Node
26949 this.parentNode = null;
26951 * The first direct child node of this node, or null if this node has no child nodes. @type Node
26953 this.firstChild = null;
26955 * The last direct child node of this node, or null if this node has no child nodes. @type Node
26957 this.lastChild = null;
26959 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26961 this.previousSibling = null;
26963 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26965 this.nextSibling = null;
26970 * Fires when a new child node is appended
26971 * @param {Tree} tree The owner tree
26972 * @param {Node} this This node
26973 * @param {Node} node The newly appended node
26974 * @param {Number} index The index of the newly appended node
26979 * Fires when a child node is removed
26980 * @param {Tree} tree The owner tree
26981 * @param {Node} this This node
26982 * @param {Node} node The removed node
26987 * Fires when this node is moved to a new location in the tree
26988 * @param {Tree} tree The owner tree
26989 * @param {Node} this This node
26990 * @param {Node} oldParent The old parent of this node
26991 * @param {Node} newParent The new parent of this node
26992 * @param {Number} index The index it was moved to
26997 * Fires when a new child node is inserted.
26998 * @param {Tree} tree The owner tree
26999 * @param {Node} this This node
27000 * @param {Node} node The child node inserted
27001 * @param {Node} refNode The child node the node was inserted before
27005 * @event beforeappend
27006 * Fires before a new child is appended, return false to cancel the append.
27007 * @param {Tree} tree The owner tree
27008 * @param {Node} this This node
27009 * @param {Node} node The child node to be appended
27011 "beforeappend" : true,
27013 * @event beforeremove
27014 * Fires before a child is removed, return false to cancel the remove.
27015 * @param {Tree} tree The owner tree
27016 * @param {Node} this This node
27017 * @param {Node} node The child node to be removed
27019 "beforeremove" : true,
27021 * @event beforemove
27022 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27023 * @param {Tree} tree The owner tree
27024 * @param {Node} this This node
27025 * @param {Node} oldParent The parent of this node
27026 * @param {Node} newParent The new parent this node is moving to
27027 * @param {Number} index The index it is being moved to
27029 "beforemove" : true,
27031 * @event beforeinsert
27032 * Fires before a new child is inserted, return false to cancel the insert.
27033 * @param {Tree} tree The owner tree
27034 * @param {Node} this This node
27035 * @param {Node} node The child node to be inserted
27036 * @param {Node} refNode The child node the node is being inserted before
27038 "beforeinsert" : true
27040 this.listeners = this.attributes.listeners;
27041 Roo.data.Node.superclass.constructor.call(this);
27044 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27045 fireEvent : function(evtName){
27046 // first do standard event for this node
27047 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27050 // then bubble it up to the tree if the event wasn't cancelled
27051 var ot = this.getOwnerTree();
27053 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27061 * Returns true if this node is a leaf
27062 * @return {Boolean}
27064 isLeaf : function(){
27065 return this.leaf === true;
27069 setFirstChild : function(node){
27070 this.firstChild = node;
27074 setLastChild : function(node){
27075 this.lastChild = node;
27080 * Returns true if this node is the last child of its parent
27081 * @return {Boolean}
27083 isLast : function(){
27084 return (!this.parentNode ? true : this.parentNode.lastChild == this);
27088 * Returns true if this node is the first child of its parent
27089 * @return {Boolean}
27091 isFirst : function(){
27092 return (!this.parentNode ? true : this.parentNode.firstChild == this);
27095 hasChildNodes : function(){
27096 return !this.isLeaf() && this.childNodes.length > 0;
27100 * Insert node(s) as the last child node of this node.
27101 * @param {Node/Array} node The node or Array of nodes to append
27102 * @return {Node} The appended node if single append, or null if an array was passed
27104 appendChild : function(node){
27106 if(node instanceof Array){
27108 }else if(arguments.length > 1){
27112 // if passed an array or multiple args do them one by one
27114 for(var i = 0, len = multi.length; i < len; i++) {
27115 this.appendChild(multi[i]);
27118 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27121 var index = this.childNodes.length;
27122 var oldParent = node.parentNode;
27123 // it's a move, make sure we move it cleanly
27125 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27128 oldParent.removeChild(node);
27131 index = this.childNodes.length;
27133 this.setFirstChild(node);
27135 this.childNodes.push(node);
27136 node.parentNode = this;
27137 var ps = this.childNodes[index-1];
27139 node.previousSibling = ps;
27140 ps.nextSibling = node;
27142 node.previousSibling = null;
27144 node.nextSibling = null;
27145 this.setLastChild(node);
27146 node.setOwnerTree(this.getOwnerTree());
27147 this.fireEvent("append", this.ownerTree, this, node, index);
27148 if(this.ownerTree) {
27149 this.ownerTree.fireEvent("appendnode", this, node, index);
27152 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27159 * Removes a child node from this node.
27160 * @param {Node} node The node to remove
27161 * @return {Node} The removed node
27163 removeChild : function(node){
27164 var index = this.childNodes.indexOf(node);
27168 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27172 // remove it from childNodes collection
27173 this.childNodes.splice(index, 1);
27176 if(node.previousSibling){
27177 node.previousSibling.nextSibling = node.nextSibling;
27179 if(node.nextSibling){
27180 node.nextSibling.previousSibling = node.previousSibling;
27183 // update child refs
27184 if(this.firstChild == node){
27185 this.setFirstChild(node.nextSibling);
27187 if(this.lastChild == node){
27188 this.setLastChild(node.previousSibling);
27191 node.setOwnerTree(null);
27192 // clear any references from the node
27193 node.parentNode = null;
27194 node.previousSibling = null;
27195 node.nextSibling = null;
27196 this.fireEvent("remove", this.ownerTree, this, node);
27201 * Inserts the first node before the second node in this nodes childNodes collection.
27202 * @param {Node} node The node to insert
27203 * @param {Node} refNode The node to insert before (if null the node is appended)
27204 * @return {Node} The inserted node
27206 insertBefore : function(node, refNode){
27207 if(!refNode){ // like standard Dom, refNode can be null for append
27208 return this.appendChild(node);
27211 if(node == refNode){
27215 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27218 var index = this.childNodes.indexOf(refNode);
27219 var oldParent = node.parentNode;
27220 var refIndex = index;
27222 // when moving internally, indexes will change after remove
27223 if(oldParent == this && this.childNodes.indexOf(node) < index){
27227 // it's a move, make sure we move it cleanly
27229 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27232 oldParent.removeChild(node);
27235 this.setFirstChild(node);
27237 this.childNodes.splice(refIndex, 0, node);
27238 node.parentNode = this;
27239 var ps = this.childNodes[refIndex-1];
27241 node.previousSibling = ps;
27242 ps.nextSibling = node;
27244 node.previousSibling = null;
27246 node.nextSibling = refNode;
27247 refNode.previousSibling = node;
27248 node.setOwnerTree(this.getOwnerTree());
27249 this.fireEvent("insert", this.ownerTree, this, node, refNode);
27251 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27257 * Returns the child node at the specified index.
27258 * @param {Number} index
27261 item : function(index){
27262 return this.childNodes[index];
27266 * Replaces one child node in this node with another.
27267 * @param {Node} newChild The replacement node
27268 * @param {Node} oldChild The node to replace
27269 * @return {Node} The replaced node
27271 replaceChild : function(newChild, oldChild){
27272 this.insertBefore(newChild, oldChild);
27273 this.removeChild(oldChild);
27278 * Returns the index of a child node
27279 * @param {Node} node
27280 * @return {Number} The index of the node or -1 if it was not found
27282 indexOf : function(child){
27283 return this.childNodes.indexOf(child);
27287 * Returns the tree this node is in.
27290 getOwnerTree : function(){
27291 // if it doesn't have one, look for one
27292 if(!this.ownerTree){
27296 this.ownerTree = p.ownerTree;
27302 return this.ownerTree;
27306 * Returns depth of this node (the root node has a depth of 0)
27309 getDepth : function(){
27312 while(p.parentNode){
27320 setOwnerTree : function(tree){
27321 // if it's move, we need to update everyone
27322 if(tree != this.ownerTree){
27323 if(this.ownerTree){
27324 this.ownerTree.unregisterNode(this);
27326 this.ownerTree = tree;
27327 var cs = this.childNodes;
27328 for(var i = 0, len = cs.length; i < len; i++) {
27329 cs[i].setOwnerTree(tree);
27332 tree.registerNode(this);
27338 * Returns the path for this node. The path can be used to expand or select this node programmatically.
27339 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27340 * @return {String} The path
27342 getPath : function(attr){
27343 attr = attr || "id";
27344 var p = this.parentNode;
27345 var b = [this.attributes[attr]];
27347 b.unshift(p.attributes[attr]);
27350 var sep = this.getOwnerTree().pathSeparator;
27351 return sep + b.join(sep);
27355 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27356 * function call will be the scope provided or the current node. The arguments to the function
27357 * will be the args provided or the current node. If the function returns false at any point,
27358 * the bubble is stopped.
27359 * @param {Function} fn The function to call
27360 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27361 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27363 bubble : function(fn, scope, args){
27366 if(fn.call(scope || p, args || p) === false){
27374 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27375 * function call will be the scope provided or the current node. The arguments to the function
27376 * will be the args provided or the current node. If the function returns false at any point,
27377 * the cascade is stopped on that branch.
27378 * @param {Function} fn The function to call
27379 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27380 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27382 cascade : function(fn, scope, args){
27383 if(fn.call(scope || this, args || this) !== false){
27384 var cs = this.childNodes;
27385 for(var i = 0, len = cs.length; i < len; i++) {
27386 cs[i].cascade(fn, scope, args);
27392 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27393 * function call will be the scope provided or the current node. The arguments to the function
27394 * will be the args provided or the current node. If the function returns false at any point,
27395 * the iteration stops.
27396 * @param {Function} fn The function to call
27397 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27398 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27400 eachChild : function(fn, scope, args){
27401 var cs = this.childNodes;
27402 for(var i = 0, len = cs.length; i < len; i++) {
27403 if(fn.call(scope || this, args || cs[i]) === false){
27410 * Finds the first child that has the attribute with the specified value.
27411 * @param {String} attribute The attribute name
27412 * @param {Mixed} value The value to search for
27413 * @return {Node} The found child or null if none was found
27415 findChild : function(attribute, value){
27416 var cs = this.childNodes;
27417 for(var i = 0, len = cs.length; i < len; i++) {
27418 if(cs[i].attributes[attribute] == value){
27426 * Finds the first child by a custom function. The child matches if the function passed
27428 * @param {Function} fn
27429 * @param {Object} scope (optional)
27430 * @return {Node} The found child or null if none was found
27432 findChildBy : function(fn, scope){
27433 var cs = this.childNodes;
27434 for(var i = 0, len = cs.length; i < len; i++) {
27435 if(fn.call(scope||cs[i], cs[i]) === true){
27443 * Sorts this nodes children using the supplied sort function
27444 * @param {Function} fn
27445 * @param {Object} scope (optional)
27447 sort : function(fn, scope){
27448 var cs = this.childNodes;
27449 var len = cs.length;
27451 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27453 for(var i = 0; i < len; i++){
27455 n.previousSibling = cs[i-1];
27456 n.nextSibling = cs[i+1];
27458 this.setFirstChild(n);
27461 this.setLastChild(n);
27468 * Returns true if this node is an ancestor (at any point) of the passed node.
27469 * @param {Node} node
27470 * @return {Boolean}
27472 contains : function(node){
27473 return node.isAncestor(this);
27477 * Returns true if the passed node is an ancestor (at any point) of this node.
27478 * @param {Node} node
27479 * @return {Boolean}
27481 isAncestor : function(node){
27482 var p = this.parentNode;
27492 toString : function(){
27493 return "[Node"+(this.id?" "+this.id:"")+"]";
27497 * Ext JS Library 1.1.1
27498 * Copyright(c) 2006-2007, Ext JS, LLC.
27500 * Originally Released Under LGPL - original licence link has changed is not relivant.
27503 * <script type="text/javascript">
27508 * @class Roo.Shadow
27509 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
27510 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
27511 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27513 * Create a new Shadow
27514 * @param {Object} config The config object
27516 Roo.Shadow = function(config){
27517 Roo.apply(this, config);
27518 if(typeof this.mode != "string"){
27519 this.mode = this.defaultMode;
27521 var o = this.offset, a = {h: 0};
27522 var rad = Math.floor(this.offset/2);
27523 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27529 a.l -= this.offset + rad;
27530 a.t -= this.offset + rad;
27541 a.l -= (this.offset - rad);
27542 a.t -= this.offset + rad;
27544 a.w -= (this.offset - rad)*2;
27555 a.l -= (this.offset - rad);
27556 a.t -= (this.offset - rad);
27558 a.w -= (this.offset + rad + 1);
27559 a.h -= (this.offset + rad);
27568 Roo.Shadow.prototype = {
27570 * @cfg {String} mode
27571 * The shadow display mode. Supports the following options:<br />
27572 * sides: Shadow displays on both sides and bottom only<br />
27573 * frame: Shadow displays equally on all four sides<br />
27574 * drop: Traditional bottom-right drop shadow (default)
27578 * @cfg {String} offset
27579 * The number of pixels to offset the shadow from the element (defaults to 4)
27584 defaultMode: "drop",
27587 * Displays the shadow under the target element
27588 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27590 show : function(target){
27591 target = Roo.get(target);
27593 this.el = Roo.Shadow.Pool.pull();
27594 if(this.el.dom.nextSibling != target.dom){
27595 this.el.insertBefore(target);
27598 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27600 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27603 target.getLeft(true),
27604 target.getTop(true),
27608 this.el.dom.style.display = "block";
27612 * Returns true if the shadow is visible, else false
27614 isVisible : function(){
27615 return this.el ? true : false;
27619 * Direct alignment when values are already available. Show must be called at least once before
27620 * calling this method to ensure it is initialized.
27621 * @param {Number} left The target element left position
27622 * @param {Number} top The target element top position
27623 * @param {Number} width The target element width
27624 * @param {Number} height The target element height
27626 realign : function(l, t, w, h){
27630 var a = this.adjusts, d = this.el.dom, s = d.style;
27632 s.left = (l+a.l)+"px";
27633 s.top = (t+a.t)+"px";
27634 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27636 if(s.width != sws || s.height != shs){
27640 var cn = d.childNodes;
27641 var sww = Math.max(0, (sw-12))+"px";
27642 cn[0].childNodes[1].style.width = sww;
27643 cn[1].childNodes[1].style.width = sww;
27644 cn[2].childNodes[1].style.width = sww;
27645 cn[1].style.height = Math.max(0, (sh-12))+"px";
27651 * Hides this shadow
27655 this.el.dom.style.display = "none";
27656 Roo.Shadow.Pool.push(this.el);
27662 * Adjust the z-index of this shadow
27663 * @param {Number} zindex The new z-index
27665 setZIndex : function(z){
27668 this.el.setStyle("z-index", z);
27673 // Private utility class that manages the internal Shadow cache
27674 Roo.Shadow.Pool = function(){
27676 var markup = Roo.isIE ?
27677 '<div class="x-ie-shadow"></div>' :
27678 '<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>';
27681 var sh = p.shift();
27683 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27684 sh.autoBoxAdjust = false;
27689 push : function(sh){
27695 * Ext JS Library 1.1.1
27696 * Copyright(c) 2006-2007, Ext JS, LLC.
27698 * Originally Released Under LGPL - original licence link has changed is not relivant.
27701 * <script type="text/javascript">
27706 * @class Roo.SplitBar
27707 * @extends Roo.util.Observable
27708 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27712 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27713 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27714 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27715 split.minSize = 100;
27716 split.maxSize = 600;
27717 split.animate = true;
27718 split.on('moved', splitterMoved);
27721 * Create a new SplitBar
27722 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
27723 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
27724 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27725 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
27726 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27727 position of the SplitBar).
27729 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27732 this.el = Roo.get(dragElement, true);
27733 this.el.dom.unselectable = "on";
27735 this.resizingEl = Roo.get(resizingElement, true);
27739 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27740 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27743 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27746 * The minimum size of the resizing element. (Defaults to 0)
27752 * The maximum size of the resizing element. (Defaults to 2000)
27755 this.maxSize = 2000;
27758 * Whether to animate the transition to the new size
27761 this.animate = false;
27764 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27767 this.useShim = false;
27772 if(!existingProxy){
27774 this.proxy = Roo.SplitBar.createProxy(this.orientation);
27776 this.proxy = Roo.get(existingProxy).dom;
27779 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27782 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27785 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27788 this.dragSpecs = {};
27791 * @private The adapter to use to positon and resize elements
27793 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27794 this.adapter.init(this);
27796 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27798 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27799 this.el.addClass("x-splitbar-h");
27802 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27803 this.el.addClass("x-splitbar-v");
27809 * Fires when the splitter is moved (alias for {@link #event-moved})
27810 * @param {Roo.SplitBar} this
27811 * @param {Number} newSize the new width or height
27816 * Fires when the splitter is moved
27817 * @param {Roo.SplitBar} this
27818 * @param {Number} newSize the new width or height
27822 * @event beforeresize
27823 * Fires before the splitter is dragged
27824 * @param {Roo.SplitBar} this
27826 "beforeresize" : true,
27828 "beforeapply" : true
27831 Roo.util.Observable.call(this);
27834 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27835 onStartProxyDrag : function(x, y){
27836 this.fireEvent("beforeresize", this);
27838 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
27840 o.enableDisplayMode("block");
27841 // all splitbars share the same overlay
27842 Roo.SplitBar.prototype.overlay = o;
27844 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27845 this.overlay.show();
27846 Roo.get(this.proxy).setDisplayed("block");
27847 var size = this.adapter.getElementSize(this);
27848 this.activeMinSize = this.getMinimumSize();;
27849 this.activeMaxSize = this.getMaximumSize();;
27850 var c1 = size - this.activeMinSize;
27851 var c2 = Math.max(this.activeMaxSize - size, 0);
27852 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27853 this.dd.resetConstraints();
27854 this.dd.setXConstraint(
27855 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
27856 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27858 this.dd.setYConstraint(0, 0);
27860 this.dd.resetConstraints();
27861 this.dd.setXConstraint(0, 0);
27862 this.dd.setYConstraint(
27863 this.placement == Roo.SplitBar.TOP ? c1 : c2,
27864 this.placement == Roo.SplitBar.TOP ? c2 : c1
27867 this.dragSpecs.startSize = size;
27868 this.dragSpecs.startPoint = [x, y];
27869 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27873 * @private Called after the drag operation by the DDProxy
27875 onEndProxyDrag : function(e){
27876 Roo.get(this.proxy).setDisplayed(false);
27877 var endPoint = Roo.lib.Event.getXY(e);
27879 this.overlay.hide();
27882 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27883 newSize = this.dragSpecs.startSize +
27884 (this.placement == Roo.SplitBar.LEFT ?
27885 endPoint[0] - this.dragSpecs.startPoint[0] :
27886 this.dragSpecs.startPoint[0] - endPoint[0]
27889 newSize = this.dragSpecs.startSize +
27890 (this.placement == Roo.SplitBar.TOP ?
27891 endPoint[1] - this.dragSpecs.startPoint[1] :
27892 this.dragSpecs.startPoint[1] - endPoint[1]
27895 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27896 if(newSize != this.dragSpecs.startSize){
27897 if(this.fireEvent('beforeapply', this, newSize) !== false){
27898 this.adapter.setElementSize(this, newSize);
27899 this.fireEvent("moved", this, newSize);
27900 this.fireEvent("resize", this, newSize);
27906 * Get the adapter this SplitBar uses
27907 * @return The adapter object
27909 getAdapter : function(){
27910 return this.adapter;
27914 * Set the adapter this SplitBar uses
27915 * @param {Object} adapter A SplitBar adapter object
27917 setAdapter : function(adapter){
27918 this.adapter = adapter;
27919 this.adapter.init(this);
27923 * Gets the minimum size for the resizing element
27924 * @return {Number} The minimum size
27926 getMinimumSize : function(){
27927 return this.minSize;
27931 * Sets the minimum size for the resizing element
27932 * @param {Number} minSize The minimum size
27934 setMinimumSize : function(minSize){
27935 this.minSize = minSize;
27939 * Gets the maximum size for the resizing element
27940 * @return {Number} The maximum size
27942 getMaximumSize : function(){
27943 return this.maxSize;
27947 * Sets the maximum size for the resizing element
27948 * @param {Number} maxSize The maximum size
27950 setMaximumSize : function(maxSize){
27951 this.maxSize = maxSize;
27955 * Sets the initialize size for the resizing element
27956 * @param {Number} size The initial size
27958 setCurrentSize : function(size){
27959 var oldAnimate = this.animate;
27960 this.animate = false;
27961 this.adapter.setElementSize(this, size);
27962 this.animate = oldAnimate;
27966 * Destroy this splitbar.
27967 * @param {Boolean} removeEl True to remove the element
27969 destroy : function(removeEl){
27971 this.shim.remove();
27974 this.proxy.parentNode.removeChild(this.proxy);
27982 * @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.
27984 Roo.SplitBar.createProxy = function(dir){
27985 var proxy = new Roo.Element(document.createElement("div"));
27986 proxy.unselectable();
27987 var cls = 'x-splitbar-proxy';
27988 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27989 document.body.appendChild(proxy.dom);
27994 * @class Roo.SplitBar.BasicLayoutAdapter
27995 * Default Adapter. It assumes the splitter and resizing element are not positioned
27996 * elements and only gets/sets the width of the element. Generally used for table based layouts.
27998 Roo.SplitBar.BasicLayoutAdapter = function(){
28001 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28002 // do nothing for now
28003 init : function(s){
28007 * Called before drag operations to get the current size of the resizing element.
28008 * @param {Roo.SplitBar} s The SplitBar using this adapter
28010 getElementSize : function(s){
28011 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28012 return s.resizingEl.getWidth();
28014 return s.resizingEl.getHeight();
28019 * Called after drag operations to set the size of the resizing element.
28020 * @param {Roo.SplitBar} s The SplitBar using this adapter
28021 * @param {Number} newSize The new size to set
28022 * @param {Function} onComplete A function to be invoked when resizing is complete
28024 setElementSize : function(s, newSize, onComplete){
28025 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28027 s.resizingEl.setWidth(newSize);
28029 onComplete(s, newSize);
28032 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28037 s.resizingEl.setHeight(newSize);
28039 onComplete(s, newSize);
28042 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28049 *@class Roo.SplitBar.AbsoluteLayoutAdapter
28050 * @extends Roo.SplitBar.BasicLayoutAdapter
28051 * Adapter that moves the splitter element to align with the resized sizing element.
28052 * Used with an absolute positioned SplitBar.
28053 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28054 * document.body, make sure you assign an id to the body element.
28056 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28057 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28058 this.container = Roo.get(container);
28061 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28062 init : function(s){
28063 this.basic.init(s);
28066 getElementSize : function(s){
28067 return this.basic.getElementSize(s);
28070 setElementSize : function(s, newSize, onComplete){
28071 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28074 moveSplitter : function(s){
28075 var yes = Roo.SplitBar;
28076 switch(s.placement){
28078 s.el.setX(s.resizingEl.getRight());
28081 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28084 s.el.setY(s.resizingEl.getBottom());
28087 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28094 * Orientation constant - Create a vertical SplitBar
28098 Roo.SplitBar.VERTICAL = 1;
28101 * Orientation constant - Create a horizontal SplitBar
28105 Roo.SplitBar.HORIZONTAL = 2;
28108 * Placement constant - The resizing element is to the left of the splitter element
28112 Roo.SplitBar.LEFT = 1;
28115 * Placement constant - The resizing element is to the right of the splitter element
28119 Roo.SplitBar.RIGHT = 2;
28122 * Placement constant - The resizing element is positioned above the splitter element
28126 Roo.SplitBar.TOP = 3;
28129 * Placement constant - The resizing element is positioned under splitter element
28133 Roo.SplitBar.BOTTOM = 4;
28136 * Ext JS Library 1.1.1
28137 * Copyright(c) 2006-2007, Ext JS, LLC.
28139 * Originally Released Under LGPL - original licence link has changed is not relivant.
28142 * <script type="text/javascript">
28147 * @extends Roo.util.Observable
28148 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
28149 * This class also supports single and multi selection modes. <br>
28150 * Create a data model bound view:
28152 var store = new Roo.data.Store(...);
28154 var view = new Roo.View({
28156 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
28158 singleSelect: true,
28159 selectedClass: "ydataview-selected",
28163 // listen for node click?
28164 view.on("click", function(vw, index, node, e){
28165 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28169 dataModel.load("foobar.xml");
28171 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28173 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28174 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28176 * Note: old style constructor is still suported (container, template, config)
28179 * Create a new View
28180 * @param {Object} config The config object
28183 Roo.View = function(config, depreciated_tpl, depreciated_config){
28185 this.parent = false;
28187 if (typeof(depreciated_tpl) == 'undefined') {
28188 // new way.. - universal constructor.
28189 Roo.apply(this, config);
28190 this.el = Roo.get(this.el);
28193 this.el = Roo.get(config);
28194 this.tpl = depreciated_tpl;
28195 Roo.apply(this, depreciated_config);
28197 this.wrapEl = this.el.wrap().wrap();
28198 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28201 if(typeof(this.tpl) == "string"){
28202 this.tpl = new Roo.Template(this.tpl);
28204 // support xtype ctors..
28205 this.tpl = new Roo.factory(this.tpl, Roo);
28209 this.tpl.compile();
28214 * @event beforeclick
28215 * Fires before a click is processed. Returns false to cancel the default action.
28216 * @param {Roo.View} this
28217 * @param {Number} index The index of the target node
28218 * @param {HTMLElement} node The target node
28219 * @param {Roo.EventObject} e The raw event object
28221 "beforeclick" : true,
28224 * Fires when a template node is clicked.
28225 * @param {Roo.View} this
28226 * @param {Number} index The index of the target node
28227 * @param {HTMLElement} node The target node
28228 * @param {Roo.EventObject} e The raw event object
28233 * Fires when a template node is double clicked.
28234 * @param {Roo.View} this
28235 * @param {Number} index The index of the target node
28236 * @param {HTMLElement} node The target node
28237 * @param {Roo.EventObject} e The raw event object
28241 * @event contextmenu
28242 * Fires when a template node is right clicked.
28243 * @param {Roo.View} this
28244 * @param {Number} index The index of the target node
28245 * @param {HTMLElement} node The target node
28246 * @param {Roo.EventObject} e The raw event object
28248 "contextmenu" : true,
28250 * @event selectionchange
28251 * Fires when the selected nodes change.
28252 * @param {Roo.View} this
28253 * @param {Array} selections Array of the selected nodes
28255 "selectionchange" : true,
28258 * @event beforeselect
28259 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28260 * @param {Roo.View} this
28261 * @param {HTMLElement} node The node to be selected
28262 * @param {Array} selections Array of currently selected nodes
28264 "beforeselect" : true,
28266 * @event preparedata
28267 * Fires on every row to render, to allow you to change the data.
28268 * @param {Roo.View} this
28269 * @param {Object} data to be rendered (change this)
28271 "preparedata" : true
28279 "click": this.onClick,
28280 "dblclick": this.onDblClick,
28281 "contextmenu": this.onContextMenu,
28285 this.selections = [];
28287 this.cmp = new Roo.CompositeElementLite([]);
28289 this.store = Roo.factory(this.store, Roo.data);
28290 this.setStore(this.store, true);
28293 if ( this.footer && this.footer.xtype) {
28295 var fctr = this.wrapEl.appendChild(document.createElement("div"));
28297 this.footer.dataSource = this.store;
28298 this.footer.container = fctr;
28299 this.footer = Roo.factory(this.footer, Roo);
28300 fctr.insertFirst(this.el);
28302 // this is a bit insane - as the paging toolbar seems to detach the el..
28303 // dom.parentNode.parentNode.parentNode
28304 // they get detached?
28308 Roo.View.superclass.constructor.call(this);
28313 Roo.extend(Roo.View, Roo.util.Observable, {
28316 * @cfg {Roo.data.Store} store Data store to load data from.
28321 * @cfg {String|Roo.Element} el The container element.
28326 * @cfg {String|Roo.Template} tpl The template used by this View
28330 * @cfg {String} dataName the named area of the template to use as the data area
28331 * Works with domtemplates roo-name="name"
28335 * @cfg {String} selectedClass The css class to add to selected nodes
28337 selectedClass : "x-view-selected",
28339 * @cfg {String} emptyText The empty text to show when nothing is loaded.
28344 * @cfg {String} text to display on mask (default Loading)
28348 * @cfg {Boolean} multiSelect Allow multiple selection
28350 multiSelect : false,
28352 * @cfg {Boolean} singleSelect Allow single selection
28354 singleSelect: false,
28357 * @cfg {Boolean} toggleSelect - selecting
28359 toggleSelect : false,
28362 * @cfg {Boolean} tickable - selecting
28367 * Returns the element this view is bound to.
28368 * @return {Roo.Element}
28370 getEl : function(){
28371 return this.wrapEl;
28377 * Refreshes the view. - called by datachanged on the store. - do not call directly.
28379 refresh : function(){
28380 //Roo.log('refresh');
28383 // if we are using something like 'domtemplate', then
28384 // the what gets used is:
28385 // t.applySubtemplate(NAME, data, wrapping data..)
28386 // the outer template then get' applied with
28387 // the store 'extra data'
28388 // and the body get's added to the
28389 // roo-name="data" node?
28390 // <span class='roo-tpl-{name}'></span> ?????
28394 this.clearSelections();
28395 this.el.update("");
28397 var records = this.store.getRange();
28398 if(records.length < 1) {
28400 // is this valid?? = should it render a template??
28402 this.el.update(this.emptyText);
28406 if (this.dataName) {
28407 this.el.update(t.apply(this.store.meta)); //????
28408 el = this.el.child('.roo-tpl-' + this.dataName);
28411 for(var i = 0, len = records.length; i < len; i++){
28412 var data = this.prepareData(records[i].data, i, records[i]);
28413 this.fireEvent("preparedata", this, data, i, records[i]);
28415 var d = Roo.apply({}, data);
28418 Roo.apply(d, {'roo-id' : Roo.id()});
28422 Roo.each(this.parent.item, function(item){
28423 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28426 Roo.apply(d, {'roo-data-checked' : 'checked'});
28430 html[html.length] = Roo.util.Format.trim(
28432 t.applySubtemplate(this.dataName, d, this.store.meta) :
28439 el.update(html.join(""));
28440 this.nodes = el.dom.childNodes;
28441 this.updateIndexes(0);
28446 * Function to override to reformat the data that is sent to
28447 * the template for each node.
28448 * DEPRICATED - use the preparedata event handler.
28449 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28450 * a JSON object for an UpdateManager bound view).
28452 prepareData : function(data, index, record)
28454 this.fireEvent("preparedata", this, data, index, record);
28458 onUpdate : function(ds, record){
28459 // Roo.log('on update');
28460 this.clearSelections();
28461 var index = this.store.indexOf(record);
28462 var n = this.nodes[index];
28463 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28464 n.parentNode.removeChild(n);
28465 this.updateIndexes(index, index);
28471 onAdd : function(ds, records, index)
28473 //Roo.log(['on Add', ds, records, index] );
28474 this.clearSelections();
28475 if(this.nodes.length == 0){
28479 var n = this.nodes[index];
28480 for(var i = 0, len = records.length; i < len; i++){
28481 var d = this.prepareData(records[i].data, i, records[i]);
28483 this.tpl.insertBefore(n, d);
28486 this.tpl.append(this.el, d);
28489 this.updateIndexes(index);
28492 onRemove : function(ds, record, index){
28493 // Roo.log('onRemove');
28494 this.clearSelections();
28495 var el = this.dataName ?
28496 this.el.child('.roo-tpl-' + this.dataName) :
28499 el.dom.removeChild(this.nodes[index]);
28500 this.updateIndexes(index);
28504 * Refresh an individual node.
28505 * @param {Number} index
28507 refreshNode : function(index){
28508 this.onUpdate(this.store, this.store.getAt(index));
28511 updateIndexes : function(startIndex, endIndex){
28512 var ns = this.nodes;
28513 startIndex = startIndex || 0;
28514 endIndex = endIndex || ns.length - 1;
28515 for(var i = startIndex; i <= endIndex; i++){
28516 ns[i].nodeIndex = i;
28521 * Changes the data store this view uses and refresh the view.
28522 * @param {Store} store
28524 setStore : function(store, initial){
28525 if(!initial && this.store){
28526 this.store.un("datachanged", this.refresh);
28527 this.store.un("add", this.onAdd);
28528 this.store.un("remove", this.onRemove);
28529 this.store.un("update", this.onUpdate);
28530 this.store.un("clear", this.refresh);
28531 this.store.un("beforeload", this.onBeforeLoad);
28532 this.store.un("load", this.onLoad);
28533 this.store.un("loadexception", this.onLoad);
28537 store.on("datachanged", this.refresh, this);
28538 store.on("add", this.onAdd, this);
28539 store.on("remove", this.onRemove, this);
28540 store.on("update", this.onUpdate, this);
28541 store.on("clear", this.refresh, this);
28542 store.on("beforeload", this.onBeforeLoad, this);
28543 store.on("load", this.onLoad, this);
28544 store.on("loadexception", this.onLoad, this);
28552 * onbeforeLoad - masks the loading area.
28555 onBeforeLoad : function(store,opts)
28557 //Roo.log('onBeforeLoad');
28559 this.el.update("");
28561 this.el.mask(this.mask ? this.mask : "Loading" );
28563 onLoad : function ()
28570 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28571 * @param {HTMLElement} node
28572 * @return {HTMLElement} The template node
28574 findItemFromChild : function(node){
28575 var el = this.dataName ?
28576 this.el.child('.roo-tpl-' + this.dataName,true) :
28579 if(!node || node.parentNode == el){
28582 var p = node.parentNode;
28583 while(p && p != el){
28584 if(p.parentNode == el){
28593 onClick : function(e){
28594 var item = this.findItemFromChild(e.getTarget());
28596 var index = this.indexOf(item);
28597 if(this.onItemClick(item, index, e) !== false){
28598 this.fireEvent("click", this, index, item, e);
28601 this.clearSelections();
28606 onContextMenu : function(e){
28607 var item = this.findItemFromChild(e.getTarget());
28609 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28614 onDblClick : function(e){
28615 var item = this.findItemFromChild(e.getTarget());
28617 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28621 onItemClick : function(item, index, e)
28623 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28626 if (this.toggleSelect) {
28627 var m = this.isSelected(item) ? 'unselect' : 'select';
28630 _t[m](item, true, false);
28633 if(this.multiSelect || this.singleSelect){
28634 if(this.multiSelect && e.shiftKey && this.lastSelection){
28635 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28637 this.select(item, this.multiSelect && e.ctrlKey);
28638 this.lastSelection = item;
28641 if(!this.tickable){
28642 e.preventDefault();
28650 * Get the number of selected nodes.
28653 getSelectionCount : function(){
28654 return this.selections.length;
28658 * Get the currently selected nodes.
28659 * @return {Array} An array of HTMLElements
28661 getSelectedNodes : function(){
28662 return this.selections;
28666 * Get the indexes of the selected nodes.
28669 getSelectedIndexes : function(){
28670 var indexes = [], s = this.selections;
28671 for(var i = 0, len = s.length; i < len; i++){
28672 indexes.push(s[i].nodeIndex);
28678 * Clear all selections
28679 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28681 clearSelections : function(suppressEvent){
28682 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28683 this.cmp.elements = this.selections;
28684 this.cmp.removeClass(this.selectedClass);
28685 this.selections = [];
28686 if(!suppressEvent){
28687 this.fireEvent("selectionchange", this, this.selections);
28693 * Returns true if the passed node is selected
28694 * @param {HTMLElement/Number} node The node or node index
28695 * @return {Boolean}
28697 isSelected : function(node){
28698 var s = this.selections;
28702 node = this.getNode(node);
28703 return s.indexOf(node) !== -1;
28708 * @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
28709 * @param {Boolean} keepExisting (optional) true to keep existing selections
28710 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28712 select : function(nodeInfo, keepExisting, suppressEvent){
28713 if(nodeInfo instanceof Array){
28715 this.clearSelections(true);
28717 for(var i = 0, len = nodeInfo.length; i < len; i++){
28718 this.select(nodeInfo[i], true, true);
28722 var node = this.getNode(nodeInfo);
28723 if(!node || this.isSelected(node)){
28724 return; // already selected.
28727 this.clearSelections(true);
28730 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28731 Roo.fly(node).addClass(this.selectedClass);
28732 this.selections.push(node);
28733 if(!suppressEvent){
28734 this.fireEvent("selectionchange", this, this.selections);
28742 * @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
28743 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28744 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28746 unselect : function(nodeInfo, keepExisting, suppressEvent)
28748 if(nodeInfo instanceof Array){
28749 Roo.each(this.selections, function(s) {
28750 this.unselect(s, nodeInfo);
28754 var node = this.getNode(nodeInfo);
28755 if(!node || !this.isSelected(node)){
28756 //Roo.log("not selected");
28757 return; // not selected.
28761 Roo.each(this.selections, function(s) {
28763 Roo.fly(node).removeClass(this.selectedClass);
28770 this.selections= ns;
28771 this.fireEvent("selectionchange", this, this.selections);
28775 * Gets a template node.
28776 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28777 * @return {HTMLElement} The node or null if it wasn't found
28779 getNode : function(nodeInfo){
28780 if(typeof nodeInfo == "string"){
28781 return document.getElementById(nodeInfo);
28782 }else if(typeof nodeInfo == "number"){
28783 return this.nodes[nodeInfo];
28789 * Gets a range template nodes.
28790 * @param {Number} startIndex
28791 * @param {Number} endIndex
28792 * @return {Array} An array of nodes
28794 getNodes : function(start, end){
28795 var ns = this.nodes;
28796 start = start || 0;
28797 end = typeof end == "undefined" ? ns.length - 1 : end;
28800 for(var i = start; i <= end; i++){
28804 for(var i = start; i >= end; i--){
28812 * Finds the index of the passed node
28813 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28814 * @return {Number} The index of the node or -1
28816 indexOf : function(node){
28817 node = this.getNode(node);
28818 if(typeof node.nodeIndex == "number"){
28819 return node.nodeIndex;
28821 var ns = this.nodes;
28822 for(var i = 0, len = ns.length; i < len; i++){
28832 * Ext JS Library 1.1.1
28833 * Copyright(c) 2006-2007, Ext JS, LLC.
28835 * Originally Released Under LGPL - original licence link has changed is not relivant.
28838 * <script type="text/javascript">
28842 * @class Roo.JsonView
28843 * @extends Roo.View
28844 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28846 var view = new Roo.JsonView({
28847 container: "my-element",
28848 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
28853 // listen for node click?
28854 view.on("click", function(vw, index, node, e){
28855 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28858 // direct load of JSON data
28859 view.load("foobar.php");
28861 // Example from my blog list
28862 var tpl = new Roo.Template(
28863 '<div class="entry">' +
28864 '<a class="entry-title" href="{link}">{title}</a>' +
28865 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
28866 "</div><hr />"
28869 var moreView = new Roo.JsonView({
28870 container : "entry-list",
28874 moreView.on("beforerender", this.sortEntries, this);
28876 url: "/blog/get-posts.php",
28877 params: "allposts=true",
28878 text: "Loading Blog Entries..."
28882 * Note: old code is supported with arguments : (container, template, config)
28886 * Create a new JsonView
28888 * @param {Object} config The config object
28891 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28894 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28896 var um = this.el.getUpdateManager();
28897 um.setRenderer(this);
28898 um.on("update", this.onLoad, this);
28899 um.on("failure", this.onLoadException, this);
28902 * @event beforerender
28903 * Fires before rendering of the downloaded JSON data.
28904 * @param {Roo.JsonView} this
28905 * @param {Object} data The JSON data loaded
28909 * Fires when data is loaded.
28910 * @param {Roo.JsonView} this
28911 * @param {Object} data The JSON data loaded
28912 * @param {Object} response The raw Connect response object
28915 * @event loadexception
28916 * Fires when loading fails.
28917 * @param {Roo.JsonView} this
28918 * @param {Object} response The raw Connect response object
28921 'beforerender' : true,
28923 'loadexception' : true
28926 Roo.extend(Roo.JsonView, Roo.View, {
28928 * @type {String} The root property in the loaded JSON object that contains the data
28933 * Refreshes the view.
28935 refresh : function(){
28936 this.clearSelections();
28937 this.el.update("");
28939 var o = this.jsonData;
28940 if(o && o.length > 0){
28941 for(var i = 0, len = o.length; i < len; i++){
28942 var data = this.prepareData(o[i], i, o);
28943 html[html.length] = this.tpl.apply(data);
28946 html.push(this.emptyText);
28948 this.el.update(html.join(""));
28949 this.nodes = this.el.dom.childNodes;
28950 this.updateIndexes(0);
28954 * 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.
28955 * @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:
28958 url: "your-url.php",
28959 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28960 callback: yourFunction,
28961 scope: yourObject, //(optional scope)
28964 text: "Loading...",
28969 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28970 * 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.
28971 * @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}
28972 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28973 * @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.
28976 var um = this.el.getUpdateManager();
28977 um.update.apply(um, arguments);
28980 // note - render is a standard framework call...
28981 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28982 render : function(el, response){
28984 this.clearSelections();
28985 this.el.update("");
28988 if (response != '') {
28989 o = Roo.util.JSON.decode(response.responseText);
28992 o = o[this.jsonRoot];
28998 * The current JSON data or null
29001 this.beforeRender();
29006 * Get the number of records in the current JSON dataset
29009 getCount : function(){
29010 return this.jsonData ? this.jsonData.length : 0;
29014 * Returns the JSON object for the specified node(s)
29015 * @param {HTMLElement/Array} node The node or an array of nodes
29016 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29017 * you get the JSON object for the node
29019 getNodeData : function(node){
29020 if(node instanceof Array){
29022 for(var i = 0, len = node.length; i < len; i++){
29023 data.push(this.getNodeData(node[i]));
29027 return this.jsonData[this.indexOf(node)] || null;
29030 beforeRender : function(){
29031 this.snapshot = this.jsonData;
29033 this.sort.apply(this, this.sortInfo);
29035 this.fireEvent("beforerender", this, this.jsonData);
29038 onLoad : function(el, o){
29039 this.fireEvent("load", this, this.jsonData, o);
29042 onLoadException : function(el, o){
29043 this.fireEvent("loadexception", this, o);
29047 * Filter the data by a specific property.
29048 * @param {String} property A property on your JSON objects
29049 * @param {String/RegExp} value Either string that the property values
29050 * should start with, or a RegExp to test against the property
29052 filter : function(property, value){
29055 var ss = this.snapshot;
29056 if(typeof value == "string"){
29057 var vlen = value.length;
29059 this.clearFilter();
29062 value = value.toLowerCase();
29063 for(var i = 0, len = ss.length; i < len; i++){
29065 if(o[property].substr(0, vlen).toLowerCase() == value){
29069 } else if(value.exec){ // regex?
29070 for(var i = 0, len = ss.length; i < len; i++){
29072 if(value.test(o[property])){
29079 this.jsonData = data;
29085 * Filter by a function. The passed function will be called with each
29086 * object in the current dataset. If the function returns true the value is kept,
29087 * otherwise it is filtered.
29088 * @param {Function} fn
29089 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29091 filterBy : function(fn, scope){
29094 var ss = this.snapshot;
29095 for(var i = 0, len = ss.length; i < len; i++){
29097 if(fn.call(scope || this, o)){
29101 this.jsonData = data;
29107 * Clears the current filter.
29109 clearFilter : function(){
29110 if(this.snapshot && this.jsonData != this.snapshot){
29111 this.jsonData = this.snapshot;
29118 * Sorts the data for this view and refreshes it.
29119 * @param {String} property A property on your JSON objects to sort on
29120 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29121 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29123 sort : function(property, dir, sortType){
29124 this.sortInfo = Array.prototype.slice.call(arguments, 0);
29127 var dsc = dir && dir.toLowerCase() == "desc";
29128 var f = function(o1, o2){
29129 var v1 = sortType ? sortType(o1[p]) : o1[p];
29130 var v2 = sortType ? sortType(o2[p]) : o2[p];
29133 return dsc ? +1 : -1;
29134 } else if(v1 > v2){
29135 return dsc ? -1 : +1;
29140 this.jsonData.sort(f);
29142 if(this.jsonData != this.snapshot){
29143 this.snapshot.sort(f);
29149 * Ext JS Library 1.1.1
29150 * Copyright(c) 2006-2007, Ext JS, LLC.
29152 * Originally Released Under LGPL - original licence link has changed is not relivant.
29155 * <script type="text/javascript">
29160 * @class Roo.ColorPalette
29161 * @extends Roo.Component
29162 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
29163 * Here's an example of typical usage:
29165 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
29166 cp.render('my-div');
29168 cp.on('select', function(palette, selColor){
29169 // do something with selColor
29173 * Create a new ColorPalette
29174 * @param {Object} config The config object
29176 Roo.ColorPalette = function(config){
29177 Roo.ColorPalette.superclass.constructor.call(this, config);
29181 * Fires when a color is selected
29182 * @param {ColorPalette} this
29183 * @param {String} color The 6-digit color hex code (without the # symbol)
29189 this.on("select", this.handler, this.scope, true);
29192 Roo.extend(Roo.ColorPalette, Roo.Component, {
29194 * @cfg {String} itemCls
29195 * The CSS class to apply to the containing element (defaults to "x-color-palette")
29197 itemCls : "x-color-palette",
29199 * @cfg {String} value
29200 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
29201 * the hex codes are case-sensitive.
29204 clickEvent:'click',
29206 ctype: "Roo.ColorPalette",
29209 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29211 allowReselect : false,
29214 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
29215 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
29216 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29217 * of colors with the width setting until the box is symmetrical.</p>
29218 * <p>You can override individual colors if needed:</p>
29220 var cp = new Roo.ColorPalette();
29221 cp.colors[0] = "FF0000"; // change the first box to red
29224 Or you can provide a custom array of your own for complete control:
29226 var cp = new Roo.ColorPalette();
29227 cp.colors = ["000000", "993300", "333300"];
29232 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29233 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29234 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29235 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29236 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29240 onRender : function(container, position){
29241 var t = new Roo.MasterTemplate(
29242 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
29244 var c = this.colors;
29245 for(var i = 0, len = c.length; i < len; i++){
29248 var el = document.createElement("div");
29249 el.className = this.itemCls;
29251 container.dom.insertBefore(el, position);
29252 this.el = Roo.get(el);
29253 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
29254 if(this.clickEvent != 'click'){
29255 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
29260 afterRender : function(){
29261 Roo.ColorPalette.superclass.afterRender.call(this);
29263 var s = this.value;
29270 handleClick : function(e, t){
29271 e.preventDefault();
29272 if(!this.disabled){
29273 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29274 this.select(c.toUpperCase());
29279 * Selects the specified color in the palette (fires the select event)
29280 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29282 select : function(color){
29283 color = color.replace("#", "");
29284 if(color != this.value || this.allowReselect){
29287 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29289 el.child("a.color-"+color).addClass("x-color-palette-sel");
29290 this.value = color;
29291 this.fireEvent("select", this, color);
29296 * Ext JS Library 1.1.1
29297 * Copyright(c) 2006-2007, Ext JS, LLC.
29299 * Originally Released Under LGPL - original licence link has changed is not relivant.
29302 * <script type="text/javascript">
29306 * @class Roo.DatePicker
29307 * @extends Roo.Component
29308 * Simple date picker class.
29310 * Create a new DatePicker
29311 * @param {Object} config The config object
29313 Roo.DatePicker = function(config){
29314 Roo.DatePicker.superclass.constructor.call(this, config);
29316 this.value = config && config.value ?
29317 config.value.clearTime() : new Date().clearTime();
29322 * Fires when a date is selected
29323 * @param {DatePicker} this
29324 * @param {Date} date The selected date
29328 * @event monthchange
29329 * Fires when the displayed month changes
29330 * @param {DatePicker} this
29331 * @param {Date} date The selected month
29333 'monthchange': true
29337 this.on("select", this.handler, this.scope || this);
29339 // build the disabledDatesRE
29340 if(!this.disabledDatesRE && this.disabledDates){
29341 var dd = this.disabledDates;
29343 for(var i = 0; i < dd.length; i++){
29345 if(i != dd.length-1) {
29349 this.disabledDatesRE = new RegExp(re + ")");
29353 Roo.extend(Roo.DatePicker, Roo.Component, {
29355 * @cfg {String} todayText
29356 * The text to display on the button that selects the current date (defaults to "Today")
29358 todayText : "Today",
29360 * @cfg {String} okText
29361 * The text to display on the ok button
29363 okText : " OK ", //   to give the user extra clicking room
29365 * @cfg {String} cancelText
29366 * The text to display on the cancel button
29368 cancelText : "Cancel",
29370 * @cfg {String} todayTip
29371 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29373 todayTip : "{0} (Spacebar)",
29375 * @cfg {Date} minDate
29376 * Minimum allowable date (JavaScript date object, defaults to null)
29380 * @cfg {Date} maxDate
29381 * Maximum allowable date (JavaScript date object, defaults to null)
29385 * @cfg {String} minText
29386 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29388 minText : "This date is before the minimum date",
29390 * @cfg {String} maxText
29391 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29393 maxText : "This date is after the maximum date",
29395 * @cfg {String} format
29396 * The default date format string which can be overriden for localization support. The format must be
29397 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29401 * @cfg {Array} disabledDays
29402 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29404 disabledDays : null,
29406 * @cfg {String} disabledDaysText
29407 * The tooltip to display when the date falls on a disabled day (defaults to "")
29409 disabledDaysText : "",
29411 * @cfg {RegExp} disabledDatesRE
29412 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29414 disabledDatesRE : null,
29416 * @cfg {String} disabledDatesText
29417 * The tooltip text to display when the date falls on a disabled date (defaults to "")
29419 disabledDatesText : "",
29421 * @cfg {Boolean} constrainToViewport
29422 * True to constrain the date picker to the viewport (defaults to true)
29424 constrainToViewport : true,
29426 * @cfg {Array} monthNames
29427 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29429 monthNames : Date.monthNames,
29431 * @cfg {Array} dayNames
29432 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29434 dayNames : Date.dayNames,
29436 * @cfg {String} nextText
29437 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29439 nextText: 'Next Month (Control+Right)',
29441 * @cfg {String} prevText
29442 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29444 prevText: 'Previous Month (Control+Left)',
29446 * @cfg {String} monthYearText
29447 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29449 monthYearText: 'Choose a month (Control+Up/Down to move years)',
29451 * @cfg {Number} startDay
29452 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29456 * @cfg {Bool} showClear
29457 * Show a clear button (usefull for date form elements that can be blank.)
29463 * Sets the value of the date field
29464 * @param {Date} value The date to set
29466 setValue : function(value){
29467 var old = this.value;
29469 if (typeof(value) == 'string') {
29471 value = Date.parseDate(value, this.format);
29474 value = new Date();
29477 this.value = value.clearTime(true);
29479 this.update(this.value);
29484 * Gets the current selected value of the date field
29485 * @return {Date} The selected date
29487 getValue : function(){
29492 focus : function(){
29494 this.update(this.activeDate);
29499 onRender : function(container, position){
29502 '<table cellspacing="0">',
29503 '<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>',
29504 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29505 var dn = this.dayNames;
29506 for(var i = 0; i < 7; i++){
29507 var d = this.startDay+i;
29511 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29513 m[m.length] = "</tr></thead><tbody><tr>";
29514 for(var i = 0; i < 42; i++) {
29515 if(i % 7 == 0 && i != 0){
29516 m[m.length] = "</tr><tr>";
29518 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29520 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29521 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29523 var el = document.createElement("div");
29524 el.className = "x-date-picker";
29525 el.innerHTML = m.join("");
29527 container.dom.insertBefore(el, position);
29529 this.el = Roo.get(el);
29530 this.eventEl = Roo.get(el.firstChild);
29532 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29533 handler: this.showPrevMonth,
29535 preventDefault:true,
29539 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29540 handler: this.showNextMonth,
29542 preventDefault:true,
29546 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
29548 this.monthPicker = this.el.down('div.x-date-mp');
29549 this.monthPicker.enableDisplayMode('block');
29551 var kn = new Roo.KeyNav(this.eventEl, {
29552 "left" : function(e){
29554 this.showPrevMonth() :
29555 this.update(this.activeDate.add("d", -1));
29558 "right" : function(e){
29560 this.showNextMonth() :
29561 this.update(this.activeDate.add("d", 1));
29564 "up" : function(e){
29566 this.showNextYear() :
29567 this.update(this.activeDate.add("d", -7));
29570 "down" : function(e){
29572 this.showPrevYear() :
29573 this.update(this.activeDate.add("d", 7));
29576 "pageUp" : function(e){
29577 this.showNextMonth();
29580 "pageDown" : function(e){
29581 this.showPrevMonth();
29584 "enter" : function(e){
29585 e.stopPropagation();
29592 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
29594 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
29596 this.el.unselectable();
29598 this.cells = this.el.select("table.x-date-inner tbody td");
29599 this.textNodes = this.el.query("table.x-date-inner tbody span");
29601 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29603 tooltip: this.monthYearText
29606 this.mbtn.on('click', this.showMonthPicker, this);
29607 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29610 var today = (new Date()).dateFormat(this.format);
29612 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29613 if (this.showClear) {
29614 baseTb.add( new Roo.Toolbar.Fill());
29617 text: String.format(this.todayText, today),
29618 tooltip: String.format(this.todayTip, today),
29619 handler: this.selectToday,
29623 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29626 if (this.showClear) {
29628 baseTb.add( new Roo.Toolbar.Fill());
29631 cls: 'x-btn-icon x-btn-clear',
29632 handler: function() {
29634 this.fireEvent("select", this, '');
29644 this.update(this.value);
29647 createMonthPicker : function(){
29648 if(!this.monthPicker.dom.firstChild){
29649 var buf = ['<table border="0" cellspacing="0">'];
29650 for(var i = 0; i < 6; i++){
29652 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29653 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29655 '<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>' :
29656 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29660 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29662 '</button><button type="button" class="x-date-mp-cancel">',
29664 '</button></td></tr>',
29667 this.monthPicker.update(buf.join(''));
29668 this.monthPicker.on('click', this.onMonthClick, this);
29669 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29671 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29672 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29674 this.mpMonths.each(function(m, a, i){
29677 m.dom.xmonth = 5 + Math.round(i * .5);
29679 m.dom.xmonth = Math.round((i-1) * .5);
29685 showMonthPicker : function(){
29686 this.createMonthPicker();
29687 var size = this.el.getSize();
29688 this.monthPicker.setSize(size);
29689 this.monthPicker.child('table').setSize(size);
29691 this.mpSelMonth = (this.activeDate || this.value).getMonth();
29692 this.updateMPMonth(this.mpSelMonth);
29693 this.mpSelYear = (this.activeDate || this.value).getFullYear();
29694 this.updateMPYear(this.mpSelYear);
29696 this.monthPicker.slideIn('t', {duration:.2});
29699 updateMPYear : function(y){
29701 var ys = this.mpYears.elements;
29702 for(var i = 1; i <= 10; i++){
29703 var td = ys[i-1], y2;
29705 y2 = y + Math.round(i * .5);
29706 td.firstChild.innerHTML = y2;
29709 y2 = y - (5-Math.round(i * .5));
29710 td.firstChild.innerHTML = y2;
29713 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29717 updateMPMonth : function(sm){
29718 this.mpMonths.each(function(m, a, i){
29719 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29723 selectMPMonth: function(m){
29727 onMonthClick : function(e, t){
29729 var el = new Roo.Element(t), pn;
29730 if(el.is('button.x-date-mp-cancel')){
29731 this.hideMonthPicker();
29733 else if(el.is('button.x-date-mp-ok')){
29734 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29735 this.hideMonthPicker();
29737 else if(pn = el.up('td.x-date-mp-month', 2)){
29738 this.mpMonths.removeClass('x-date-mp-sel');
29739 pn.addClass('x-date-mp-sel');
29740 this.mpSelMonth = pn.dom.xmonth;
29742 else if(pn = el.up('td.x-date-mp-year', 2)){
29743 this.mpYears.removeClass('x-date-mp-sel');
29744 pn.addClass('x-date-mp-sel');
29745 this.mpSelYear = pn.dom.xyear;
29747 else if(el.is('a.x-date-mp-prev')){
29748 this.updateMPYear(this.mpyear-10);
29750 else if(el.is('a.x-date-mp-next')){
29751 this.updateMPYear(this.mpyear+10);
29755 onMonthDblClick : function(e, t){
29757 var el = new Roo.Element(t), pn;
29758 if(pn = el.up('td.x-date-mp-month', 2)){
29759 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29760 this.hideMonthPicker();
29762 else if(pn = el.up('td.x-date-mp-year', 2)){
29763 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29764 this.hideMonthPicker();
29768 hideMonthPicker : function(disableAnim){
29769 if(this.monthPicker){
29770 if(disableAnim === true){
29771 this.monthPicker.hide();
29773 this.monthPicker.slideOut('t', {duration:.2});
29779 showPrevMonth : function(e){
29780 this.update(this.activeDate.add("mo", -1));
29784 showNextMonth : function(e){
29785 this.update(this.activeDate.add("mo", 1));
29789 showPrevYear : function(){
29790 this.update(this.activeDate.add("y", -1));
29794 showNextYear : function(){
29795 this.update(this.activeDate.add("y", 1));
29799 handleMouseWheel : function(e){
29800 var delta = e.getWheelDelta();
29802 this.showPrevMonth();
29804 } else if(delta < 0){
29805 this.showNextMonth();
29811 handleDateClick : function(e, t){
29813 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29814 this.setValue(new Date(t.dateValue));
29815 this.fireEvent("select", this, this.value);
29820 selectToday : function(){
29821 this.setValue(new Date().clearTime());
29822 this.fireEvent("select", this, this.value);
29826 update : function(date)
29828 var vd = this.activeDate;
29829 this.activeDate = date;
29831 var t = date.getTime();
29832 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29833 this.cells.removeClass("x-date-selected");
29834 this.cells.each(function(c){
29835 if(c.dom.firstChild.dateValue == t){
29836 c.addClass("x-date-selected");
29837 setTimeout(function(){
29838 try{c.dom.firstChild.focus();}catch(e){}
29847 var days = date.getDaysInMonth();
29848 var firstOfMonth = date.getFirstDateOfMonth();
29849 var startingPos = firstOfMonth.getDay()-this.startDay;
29851 if(startingPos <= this.startDay){
29855 var pm = date.add("mo", -1);
29856 var prevStart = pm.getDaysInMonth()-startingPos;
29858 var cells = this.cells.elements;
29859 var textEls = this.textNodes;
29860 days += startingPos;
29862 // convert everything to numbers so it's fast
29863 var day = 86400000;
29864 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29865 var today = new Date().clearTime().getTime();
29866 var sel = date.clearTime().getTime();
29867 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29868 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29869 var ddMatch = this.disabledDatesRE;
29870 var ddText = this.disabledDatesText;
29871 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29872 var ddaysText = this.disabledDaysText;
29873 var format = this.format;
29875 var setCellClass = function(cal, cell){
29877 var t = d.getTime();
29878 cell.firstChild.dateValue = t;
29880 cell.className += " x-date-today";
29881 cell.title = cal.todayText;
29884 cell.className += " x-date-selected";
29885 setTimeout(function(){
29886 try{cell.firstChild.focus();}catch(e){}
29891 cell.className = " x-date-disabled";
29892 cell.title = cal.minText;
29896 cell.className = " x-date-disabled";
29897 cell.title = cal.maxText;
29901 if(ddays.indexOf(d.getDay()) != -1){
29902 cell.title = ddaysText;
29903 cell.className = " x-date-disabled";
29906 if(ddMatch && format){
29907 var fvalue = d.dateFormat(format);
29908 if(ddMatch.test(fvalue)){
29909 cell.title = ddText.replace("%0", fvalue);
29910 cell.className = " x-date-disabled";
29916 for(; i < startingPos; i++) {
29917 textEls[i].innerHTML = (++prevStart);
29918 d.setDate(d.getDate()+1);
29919 cells[i].className = "x-date-prevday";
29920 setCellClass(this, cells[i]);
29922 for(; i < days; i++){
29923 intDay = i - startingPos + 1;
29924 textEls[i].innerHTML = (intDay);
29925 d.setDate(d.getDate()+1);
29926 cells[i].className = "x-date-active";
29927 setCellClass(this, cells[i]);
29930 for(; i < 42; i++) {
29931 textEls[i].innerHTML = (++extraDays);
29932 d.setDate(d.getDate()+1);
29933 cells[i].className = "x-date-nextday";
29934 setCellClass(this, cells[i]);
29937 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29938 this.fireEvent('monthchange', this, date);
29940 if(!this.internalRender){
29941 var main = this.el.dom.firstChild;
29942 var w = main.offsetWidth;
29943 this.el.setWidth(w + this.el.getBorderWidth("lr"));
29944 Roo.fly(main).setWidth(w);
29945 this.internalRender = true;
29946 // opera does not respect the auto grow header center column
29947 // then, after it gets a width opera refuses to recalculate
29948 // without a second pass
29949 if(Roo.isOpera && !this.secondPass){
29950 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29951 this.secondPass = true;
29952 this.update.defer(10, this, [date]);
29960 * Ext JS Library 1.1.1
29961 * Copyright(c) 2006-2007, Ext JS, LLC.
29963 * Originally Released Under LGPL - original licence link has changed is not relivant.
29966 * <script type="text/javascript">
29969 * @class Roo.TabPanel
29970 * @extends Roo.util.Observable
29971 * A lightweight tab container.
29975 // basic tabs 1, built from existing content
29976 var tabs = new Roo.TabPanel("tabs1");
29977 tabs.addTab("script", "View Script");
29978 tabs.addTab("markup", "View Markup");
29979 tabs.activate("script");
29981 // more advanced tabs, built from javascript
29982 var jtabs = new Roo.TabPanel("jtabs");
29983 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29985 // set up the UpdateManager
29986 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29987 var updater = tab2.getUpdateManager();
29988 updater.setDefaultUrl("ajax1.htm");
29989 tab2.on('activate', updater.refresh, updater, true);
29991 // Use setUrl for Ajax loading
29992 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29993 tab3.setUrl("ajax2.htm", null, true);
29996 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29999 jtabs.activate("jtabs-1");
30002 * Create a new TabPanel.
30003 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30004 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30006 Roo.TabPanel = function(container, config){
30008 * The container element for this TabPanel.
30009 * @type Roo.Element
30011 this.el = Roo.get(container, true);
30013 if(typeof config == "boolean"){
30014 this.tabPosition = config ? "bottom" : "top";
30016 Roo.apply(this, config);
30019 if(this.tabPosition == "bottom"){
30020 this.bodyEl = Roo.get(this.createBody(this.el.dom));
30021 this.el.addClass("x-tabs-bottom");
30023 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30024 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30025 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30027 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30029 if(this.tabPosition != "bottom"){
30030 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30031 * @type Roo.Element
30033 this.bodyEl = Roo.get(this.createBody(this.el.dom));
30034 this.el.addClass("x-tabs-top");
30038 this.bodyEl.setStyle("position", "relative");
30040 this.active = null;
30041 this.activateDelegate = this.activate.createDelegate(this);
30046 * Fires when the active tab changes
30047 * @param {Roo.TabPanel} this
30048 * @param {Roo.TabPanelItem} activePanel The new active tab
30052 * @event beforetabchange
30053 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30054 * @param {Roo.TabPanel} this
30055 * @param {Object} e Set cancel to true on this object to cancel the tab change
30056 * @param {Roo.TabPanelItem} tab The tab being changed to
30058 "beforetabchange" : true
30061 Roo.EventManager.onWindowResize(this.onResize, this);
30062 this.cpad = this.el.getPadding("lr");
30063 this.hiddenCount = 0;
30066 // toolbar on the tabbar support...
30067 if (this.toolbar) {
30068 var tcfg = this.toolbar;
30069 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
30070 this.toolbar = new Roo.Toolbar(tcfg);
30071 if (Roo.isSafari) {
30072 var tbl = tcfg.container.child('table', true);
30073 tbl.setAttribute('width', '100%');
30080 Roo.TabPanel.superclass.constructor.call(this);
30083 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30085 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30087 tabPosition : "top",
30089 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30091 currentTabWidth : 0,
30093 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30097 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30101 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30103 preferredTabWidth : 175,
30105 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30107 resizeTabs : false,
30109 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30111 monitorResize : true,
30113 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
30118 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30119 * @param {String} id The id of the div to use <b>or create</b>
30120 * @param {String} text The text for the tab
30121 * @param {String} content (optional) Content to put in the TabPanelItem body
30122 * @param {Boolean} closable (optional) True to create a close icon on the tab
30123 * @return {Roo.TabPanelItem} The created TabPanelItem
30125 addTab : function(id, text, content, closable){
30126 var item = new Roo.TabPanelItem(this, id, text, closable);
30127 this.addTabItem(item);
30129 item.setContent(content);
30135 * Returns the {@link Roo.TabPanelItem} with the specified id/index
30136 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30137 * @return {Roo.TabPanelItem}
30139 getTab : function(id){
30140 return this.items[id];
30144 * Hides the {@link Roo.TabPanelItem} with the specified id/index
30145 * @param {String/Number} id The id or index of the TabPanelItem to hide.
30147 hideTab : function(id){
30148 var t = this.items[id];
30151 this.hiddenCount++;
30152 this.autoSizeTabs();
30157 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30158 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30160 unhideTab : function(id){
30161 var t = this.items[id];
30163 t.setHidden(false);
30164 this.hiddenCount--;
30165 this.autoSizeTabs();
30170 * Adds an existing {@link Roo.TabPanelItem}.
30171 * @param {Roo.TabPanelItem} item The TabPanelItem to add
30173 addTabItem : function(item){
30174 this.items[item.id] = item;
30175 this.items.push(item);
30176 if(this.resizeTabs){
30177 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30178 this.autoSizeTabs();
30185 * Removes a {@link Roo.TabPanelItem}.
30186 * @param {String/Number} id The id or index of the TabPanelItem to remove.
30188 removeTab : function(id){
30189 var items = this.items;
30190 var tab = items[id];
30191 if(!tab) { return; }
30192 var index = items.indexOf(tab);
30193 if(this.active == tab && items.length > 1){
30194 var newTab = this.getNextAvailable(index);
30199 this.stripEl.dom.removeChild(tab.pnode.dom);
30200 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30201 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30203 items.splice(index, 1);
30204 delete this.items[tab.id];
30205 tab.fireEvent("close", tab);
30206 tab.purgeListeners();
30207 this.autoSizeTabs();
30210 getNextAvailable : function(start){
30211 var items = this.items;
30213 // look for a next tab that will slide over to
30214 // replace the one being removed
30215 while(index < items.length){
30216 var item = items[++index];
30217 if(item && !item.isHidden()){
30221 // if one isn't found select the previous tab (on the left)
30224 var item = items[--index];
30225 if(item && !item.isHidden()){
30233 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30234 * @param {String/Number} id The id or index of the TabPanelItem to disable.
30236 disableTab : function(id){
30237 var tab = this.items[id];
30238 if(tab && this.active != tab){
30244 * Enables a {@link Roo.TabPanelItem} that is disabled.
30245 * @param {String/Number} id The id or index of the TabPanelItem to enable.
30247 enableTab : function(id){
30248 var tab = this.items[id];
30253 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30254 * @param {String/Number} id The id or index of the TabPanelItem to activate.
30255 * @return {Roo.TabPanelItem} The TabPanelItem.
30257 activate : function(id){
30258 var tab = this.items[id];
30262 if(tab == this.active || tab.disabled){
30266 this.fireEvent("beforetabchange", this, e, tab);
30267 if(e.cancel !== true && !tab.disabled){
30269 this.active.hide();
30271 this.active = this.items[id];
30272 this.active.show();
30273 this.fireEvent("tabchange", this, this.active);
30279 * Gets the active {@link Roo.TabPanelItem}.
30280 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30282 getActiveTab : function(){
30283 return this.active;
30287 * Updates the tab body element to fit the height of the container element
30288 * for overflow scrolling
30289 * @param {Number} targetHeight (optional) Override the starting height from the elements height
30291 syncHeight : function(targetHeight){
30292 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30293 var bm = this.bodyEl.getMargins();
30294 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30295 this.bodyEl.setHeight(newHeight);
30299 onResize : function(){
30300 if(this.monitorResize){
30301 this.autoSizeTabs();
30306 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30308 beginUpdate : function(){
30309 this.updating = true;
30313 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30315 endUpdate : function(){
30316 this.updating = false;
30317 this.autoSizeTabs();
30321 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30323 autoSizeTabs : function(){
30324 var count = this.items.length;
30325 var vcount = count - this.hiddenCount;
30326 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30329 var w = Math.max(this.el.getWidth() - this.cpad, 10);
30330 var availWidth = Math.floor(w / vcount);
30331 var b = this.stripBody;
30332 if(b.getWidth() > w){
30333 var tabs = this.items;
30334 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30335 if(availWidth < this.minTabWidth){
30336 /*if(!this.sleft){ // incomplete scrolling code
30337 this.createScrollButtons();
30340 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30343 if(this.currentTabWidth < this.preferredTabWidth){
30344 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30350 * Returns the number of tabs in this TabPanel.
30353 getCount : function(){
30354 return this.items.length;
30358 * Resizes all the tabs to the passed width
30359 * @param {Number} The new width
30361 setTabWidth : function(width){
30362 this.currentTabWidth = width;
30363 for(var i = 0, len = this.items.length; i < len; i++) {
30364 if(!this.items[i].isHidden()) {
30365 this.items[i].setWidth(width);
30371 * Destroys this TabPanel
30372 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30374 destroy : function(removeEl){
30375 Roo.EventManager.removeResizeListener(this.onResize, this);
30376 for(var i = 0, len = this.items.length; i < len; i++){
30377 this.items[i].purgeListeners();
30379 if(removeEl === true){
30380 this.el.update("");
30387 * @class Roo.TabPanelItem
30388 * @extends Roo.util.Observable
30389 * Represents an individual item (tab plus body) in a TabPanel.
30390 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30391 * @param {String} id The id of this TabPanelItem
30392 * @param {String} text The text for the tab of this TabPanelItem
30393 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30395 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30397 * The {@link Roo.TabPanel} this TabPanelItem belongs to
30398 * @type Roo.TabPanel
30400 this.tabPanel = tabPanel;
30402 * The id for this TabPanelItem
30407 this.disabled = false;
30411 this.loaded = false;
30412 this.closable = closable;
30415 * The body element for this TabPanelItem.
30416 * @type Roo.Element
30418 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30419 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30420 this.bodyEl.setStyle("display", "block");
30421 this.bodyEl.setStyle("zoom", "1");
30424 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30426 this.el = Roo.get(els.el, true);
30427 this.inner = Roo.get(els.inner, true);
30428 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30429 this.pnode = Roo.get(els.el.parentNode, true);
30430 this.el.on("mousedown", this.onTabMouseDown, this);
30431 this.el.on("click", this.onTabClick, this);
30434 var c = Roo.get(els.close, true);
30435 c.dom.title = this.closeText;
30436 c.addClassOnOver("close-over");
30437 c.on("click", this.closeClick, this);
30443 * Fires when this tab becomes the active tab.
30444 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30445 * @param {Roo.TabPanelItem} this
30449 * @event beforeclose
30450 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30451 * @param {Roo.TabPanelItem} this
30452 * @param {Object} e Set cancel to true on this object to cancel the close.
30454 "beforeclose": true,
30457 * Fires when this tab is closed.
30458 * @param {Roo.TabPanelItem} this
30462 * @event deactivate
30463 * Fires when this tab is no longer the active tab.
30464 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30465 * @param {Roo.TabPanelItem} this
30467 "deactivate" : true
30469 this.hidden = false;
30471 Roo.TabPanelItem.superclass.constructor.call(this);
30474 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30475 purgeListeners : function(){
30476 Roo.util.Observable.prototype.purgeListeners.call(this);
30477 this.el.removeAllListeners();
30480 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30483 this.pnode.addClass("on");
30486 this.tabPanel.stripWrap.repaint();
30488 this.fireEvent("activate", this.tabPanel, this);
30492 * Returns true if this tab is the active tab.
30493 * @return {Boolean}
30495 isActive : function(){
30496 return this.tabPanel.getActiveTab() == this;
30500 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30503 this.pnode.removeClass("on");
30505 this.fireEvent("deactivate", this.tabPanel, this);
30508 hideAction : function(){
30509 this.bodyEl.hide();
30510 this.bodyEl.setStyle("position", "absolute");
30511 this.bodyEl.setLeft("-20000px");
30512 this.bodyEl.setTop("-20000px");
30515 showAction : function(){
30516 this.bodyEl.setStyle("position", "relative");
30517 this.bodyEl.setTop("");
30518 this.bodyEl.setLeft("");
30519 this.bodyEl.show();
30523 * Set the tooltip for the tab.
30524 * @param {String} tooltip The tab's tooltip
30526 setTooltip : function(text){
30527 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30528 this.textEl.dom.qtip = text;
30529 this.textEl.dom.removeAttribute('title');
30531 this.textEl.dom.title = text;
30535 onTabClick : function(e){
30536 e.preventDefault();
30537 this.tabPanel.activate(this.id);
30540 onTabMouseDown : function(e){
30541 e.preventDefault();
30542 this.tabPanel.activate(this.id);
30545 getWidth : function(){
30546 return this.inner.getWidth();
30549 setWidth : function(width){
30550 var iwidth = width - this.pnode.getPadding("lr");
30551 this.inner.setWidth(iwidth);
30552 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30553 this.pnode.setWidth(width);
30557 * Show or hide the tab
30558 * @param {Boolean} hidden True to hide or false to show.
30560 setHidden : function(hidden){
30561 this.hidden = hidden;
30562 this.pnode.setStyle("display", hidden ? "none" : "");
30566 * Returns true if this tab is "hidden"
30567 * @return {Boolean}
30569 isHidden : function(){
30570 return this.hidden;
30574 * Returns the text for this tab
30577 getText : function(){
30581 autoSize : function(){
30582 //this.el.beginMeasure();
30583 this.textEl.setWidth(1);
30585 * #2804 [new] Tabs in Roojs
30586 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30588 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30589 //this.el.endMeasure();
30593 * Sets the text for the tab (Note: this also sets the tooltip text)
30594 * @param {String} text The tab's text and tooltip
30596 setText : function(text){
30598 this.textEl.update(text);
30599 this.setTooltip(text);
30600 if(!this.tabPanel.resizeTabs){
30605 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30607 activate : function(){
30608 this.tabPanel.activate(this.id);
30612 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30614 disable : function(){
30615 if(this.tabPanel.active != this){
30616 this.disabled = true;
30617 this.pnode.addClass("disabled");
30622 * Enables this TabPanelItem if it was previously disabled.
30624 enable : function(){
30625 this.disabled = false;
30626 this.pnode.removeClass("disabled");
30630 * Sets the content for this TabPanelItem.
30631 * @param {String} content The content
30632 * @param {Boolean} loadScripts true to look for and load scripts
30634 setContent : function(content, loadScripts){
30635 this.bodyEl.update(content, loadScripts);
30639 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30640 * @return {Roo.UpdateManager} The UpdateManager
30642 getUpdateManager : function(){
30643 return this.bodyEl.getUpdateManager();
30647 * Set a URL to be used to load the content for this TabPanelItem.
30648 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30649 * @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)
30650 * @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)
30651 * @return {Roo.UpdateManager} The UpdateManager
30653 setUrl : function(url, params, loadOnce){
30654 if(this.refreshDelegate){
30655 this.un('activate', this.refreshDelegate);
30657 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30658 this.on("activate", this.refreshDelegate);
30659 return this.bodyEl.getUpdateManager();
30663 _handleRefresh : function(url, params, loadOnce){
30664 if(!loadOnce || !this.loaded){
30665 var updater = this.bodyEl.getUpdateManager();
30666 updater.update(url, params, this._setLoaded.createDelegate(this));
30671 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
30672 * Will fail silently if the setUrl method has not been called.
30673 * This does not activate the panel, just updates its content.
30675 refresh : function(){
30676 if(this.refreshDelegate){
30677 this.loaded = false;
30678 this.refreshDelegate();
30683 _setLoaded : function(){
30684 this.loaded = true;
30688 closeClick : function(e){
30691 this.fireEvent("beforeclose", this, o);
30692 if(o.cancel !== true){
30693 this.tabPanel.removeTab(this.id);
30697 * The text displayed in the tooltip for the close icon.
30700 closeText : "Close this tab"
30704 Roo.TabPanel.prototype.createStrip = function(container){
30705 var strip = document.createElement("div");
30706 strip.className = "x-tabs-wrap";
30707 container.appendChild(strip);
30711 Roo.TabPanel.prototype.createStripList = function(strip){
30712 // div wrapper for retard IE
30713 // returns the "tr" element.
30714 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30715 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30716 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30717 return strip.firstChild.firstChild.firstChild.firstChild;
30720 Roo.TabPanel.prototype.createBody = function(container){
30721 var body = document.createElement("div");
30722 Roo.id(body, "tab-body");
30723 Roo.fly(body).addClass("x-tabs-body");
30724 container.appendChild(body);
30728 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30729 var body = Roo.getDom(id);
30731 body = document.createElement("div");
30734 Roo.fly(body).addClass("x-tabs-item-body");
30735 bodyEl.insertBefore(body, bodyEl.firstChild);
30739 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30740 var td = document.createElement("td");
30741 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30742 //stripEl.appendChild(td);
30744 td.className = "x-tabs-closable";
30745 if(!this.closeTpl){
30746 this.closeTpl = new Roo.Template(
30747 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30748 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30749 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
30752 var el = this.closeTpl.overwrite(td, {"text": text});
30753 var close = el.getElementsByTagName("div")[0];
30754 var inner = el.getElementsByTagName("em")[0];
30755 return {"el": el, "close": close, "inner": inner};
30758 this.tabTpl = new Roo.Template(
30759 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30760 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30763 var el = this.tabTpl.overwrite(td, {"text": text});
30764 var inner = el.getElementsByTagName("em")[0];
30765 return {"el": el, "inner": inner};
30769 * Ext JS Library 1.1.1
30770 * Copyright(c) 2006-2007, Ext JS, LLC.
30772 * Originally Released Under LGPL - original licence link has changed is not relivant.
30775 * <script type="text/javascript">
30779 * @class Roo.Button
30780 * @extends Roo.util.Observable
30781 * Simple Button class
30782 * @cfg {String} text The button text
30783 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30784 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30785 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30786 * @cfg {Object} scope The scope of the handler
30787 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30788 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30789 * @cfg {Boolean} hidden True to start hidden (defaults to false)
30790 * @cfg {Boolean} disabled True to start disabled (defaults to false)
30791 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30792 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30793 applies if enableToggle = true)
30794 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30795 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30796 an {@link Roo.util.ClickRepeater} config object (defaults to false).
30798 * Create a new button
30799 * @param {Object} config The config object
30801 Roo.Button = function(renderTo, config)
30805 renderTo = config.renderTo || false;
30808 Roo.apply(this, config);
30812 * Fires when this button is clicked
30813 * @param {Button} this
30814 * @param {EventObject} e The click event
30819 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30820 * @param {Button} this
30821 * @param {Boolean} pressed
30826 * Fires when the mouse hovers over the button
30827 * @param {Button} this
30828 * @param {Event} e The event object
30830 'mouseover' : true,
30833 * Fires when the mouse exits the button
30834 * @param {Button} this
30835 * @param {Event} e The event object
30840 * Fires when the button is rendered
30841 * @param {Button} this
30846 this.menu = Roo.menu.MenuMgr.get(this.menu);
30848 // register listeners first!! - so render can be captured..
30849 Roo.util.Observable.call(this);
30851 this.render(renderTo);
30857 Roo.extend(Roo.Button, Roo.util.Observable, {
30863 * Read-only. True if this button is hidden
30868 * Read-only. True if this button is disabled
30873 * Read-only. True if this button is pressed (only if enableToggle = true)
30879 * @cfg {Number} tabIndex
30880 * The DOM tabIndex for this button (defaults to undefined)
30882 tabIndex : undefined,
30885 * @cfg {Boolean} enableToggle
30886 * True to enable pressed/not pressed toggling (defaults to false)
30888 enableToggle: false,
30890 * @cfg {Roo.menu.Menu} menu
30891 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30895 * @cfg {String} menuAlign
30896 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30898 menuAlign : "tl-bl?",
30901 * @cfg {String} iconCls
30902 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30904 iconCls : undefined,
30906 * @cfg {String} type
30907 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
30912 menuClassTarget: 'tr',
30915 * @cfg {String} clickEvent
30916 * The type of event to map to the button's event handler (defaults to 'click')
30918 clickEvent : 'click',
30921 * @cfg {Boolean} handleMouseEvents
30922 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30924 handleMouseEvents : true,
30927 * @cfg {String} tooltipType
30928 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30930 tooltipType : 'qtip',
30933 * @cfg {String} cls
30934 * A CSS class to apply to the button's main element.
30938 * @cfg {Roo.Template} template (Optional)
30939 * An {@link Roo.Template} with which to create the Button's main element. This Template must
30940 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30941 * require code modifications if required elements (e.g. a button) aren't present.
30945 render : function(renderTo){
30947 if(this.hideParent){
30948 this.parentEl = Roo.get(renderTo);
30950 if(!this.dhconfig){
30951 if(!this.template){
30952 if(!Roo.Button.buttonTemplate){
30953 // hideous table template
30954 Roo.Button.buttonTemplate = new Roo.Template(
30955 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30956 '<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>',
30957 "</tr></tbody></table>");
30959 this.template = Roo.Button.buttonTemplate;
30961 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
30962 var btnEl = btn.child("button:first");
30963 btnEl.on('focus', this.onFocus, this);
30964 btnEl.on('blur', this.onBlur, this);
30966 btn.addClass(this.cls);
30969 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30972 btnEl.addClass(this.iconCls);
30974 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30977 if(this.tabIndex !== undefined){
30978 btnEl.dom.tabIndex = this.tabIndex;
30981 if(typeof this.tooltip == 'object'){
30982 Roo.QuickTips.tips(Roo.apply({
30986 btnEl.dom[this.tooltipType] = this.tooltip;
30990 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30994 this.el.dom.id = this.el.id = this.id;
30997 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30998 this.menu.on("show", this.onMenuShow, this);
30999 this.menu.on("hide", this.onMenuHide, this);
31001 btn.addClass("x-btn");
31002 if(Roo.isIE && !Roo.isIE7){
31003 this.autoWidth.defer(1, this);
31007 if(this.handleMouseEvents){
31008 btn.on("mouseover", this.onMouseOver, this);
31009 btn.on("mouseout", this.onMouseOut, this);
31010 btn.on("mousedown", this.onMouseDown, this);
31012 btn.on(this.clickEvent, this.onClick, this);
31013 //btn.on("mouseup", this.onMouseUp, this);
31020 Roo.ButtonToggleMgr.register(this);
31022 this.el.addClass("x-btn-pressed");
31025 var repeater = new Roo.util.ClickRepeater(btn,
31026 typeof this.repeat == "object" ? this.repeat : {}
31028 repeater.on("click", this.onClick, this);
31031 this.fireEvent('render', this);
31035 * Returns the button's underlying element
31036 * @return {Roo.Element} The element
31038 getEl : function(){
31043 * Destroys this Button and removes any listeners.
31045 destroy : function(){
31046 Roo.ButtonToggleMgr.unregister(this);
31047 this.el.removeAllListeners();
31048 this.purgeListeners();
31053 autoWidth : function(){
31055 this.el.setWidth("auto");
31056 if(Roo.isIE7 && Roo.isStrict){
31057 var ib = this.el.child('button');
31058 if(ib && ib.getWidth() > 20){
31060 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31065 this.el.beginMeasure();
31067 if(this.el.getWidth() < this.minWidth){
31068 this.el.setWidth(this.minWidth);
31071 this.el.endMeasure();
31078 * Assigns this button's click handler
31079 * @param {Function} handler The function to call when the button is clicked
31080 * @param {Object} scope (optional) Scope for the function passed in
31082 setHandler : function(handler, scope){
31083 this.handler = handler;
31084 this.scope = scope;
31088 * Sets this button's text
31089 * @param {String} text The button text
31091 setText : function(text){
31094 this.el.child("td.x-btn-center button.x-btn-text").update(text);
31100 * Gets the text for this button
31101 * @return {String} The button text
31103 getText : function(){
31111 this.hidden = false;
31113 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31121 this.hidden = true;
31123 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31128 * Convenience function for boolean show/hide
31129 * @param {Boolean} visible True to show, false to hide
31131 setVisible: function(visible){
31140 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31141 * @param {Boolean} state (optional) Force a particular state
31143 toggle : function(state){
31144 state = state === undefined ? !this.pressed : state;
31145 if(state != this.pressed){
31147 this.el.addClass("x-btn-pressed");
31148 this.pressed = true;
31149 this.fireEvent("toggle", this, true);
31151 this.el.removeClass("x-btn-pressed");
31152 this.pressed = false;
31153 this.fireEvent("toggle", this, false);
31155 if(this.toggleHandler){
31156 this.toggleHandler.call(this.scope || this, this, state);
31164 focus : function(){
31165 this.el.child('button:first').focus();
31169 * Disable this button
31171 disable : function(){
31173 this.el.addClass("x-btn-disabled");
31175 this.disabled = true;
31179 * Enable this button
31181 enable : function(){
31183 this.el.removeClass("x-btn-disabled");
31185 this.disabled = false;
31189 * Convenience function for boolean enable/disable
31190 * @param {Boolean} enabled True to enable, false to disable
31192 setDisabled : function(v){
31193 this[v !== true ? "enable" : "disable"]();
31197 onClick : function(e)
31200 e.preventDefault();
31205 if(!this.disabled){
31206 if(this.enableToggle){
31209 if(this.menu && !this.menu.isVisible()){
31210 this.menu.show(this.el, this.menuAlign);
31212 this.fireEvent("click", this, e);
31214 this.el.removeClass("x-btn-over");
31215 this.handler.call(this.scope || this, this, e);
31220 onMouseOver : function(e){
31221 if(!this.disabled){
31222 this.el.addClass("x-btn-over");
31223 this.fireEvent('mouseover', this, e);
31227 onMouseOut : function(e){
31228 if(!e.within(this.el, true)){
31229 this.el.removeClass("x-btn-over");
31230 this.fireEvent('mouseout', this, e);
31234 onFocus : function(e){
31235 if(!this.disabled){
31236 this.el.addClass("x-btn-focus");
31240 onBlur : function(e){
31241 this.el.removeClass("x-btn-focus");
31244 onMouseDown : function(e){
31245 if(!this.disabled && e.button == 0){
31246 this.el.addClass("x-btn-click");
31247 Roo.get(document).on('mouseup', this.onMouseUp, this);
31251 onMouseUp : function(e){
31253 this.el.removeClass("x-btn-click");
31254 Roo.get(document).un('mouseup', this.onMouseUp, this);
31258 onMenuShow : function(e){
31259 this.el.addClass("x-btn-menu-active");
31262 onMenuHide : function(e){
31263 this.el.removeClass("x-btn-menu-active");
31267 // Private utility class used by Button
31268 Roo.ButtonToggleMgr = function(){
31271 function toggleGroup(btn, state){
31273 var g = groups[btn.toggleGroup];
31274 for(var i = 0, l = g.length; i < l; i++){
31276 g[i].toggle(false);
31283 register : function(btn){
31284 if(!btn.toggleGroup){
31287 var g = groups[btn.toggleGroup];
31289 g = groups[btn.toggleGroup] = [];
31292 btn.on("toggle", toggleGroup);
31295 unregister : function(btn){
31296 if(!btn.toggleGroup){
31299 var g = groups[btn.toggleGroup];
31302 btn.un("toggle", toggleGroup);
31308 * Ext JS Library 1.1.1
31309 * Copyright(c) 2006-2007, Ext JS, LLC.
31311 * Originally Released Under LGPL - original licence link has changed is not relivant.
31314 * <script type="text/javascript">
31318 * @class Roo.SplitButton
31319 * @extends Roo.Button
31320 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31321 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
31322 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31323 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31324 * @cfg {String} arrowTooltip The title attribute of the arrow
31326 * Create a new menu button
31327 * @param {String/HTMLElement/Element} renderTo The element to append the button to
31328 * @param {Object} config The config object
31330 Roo.SplitButton = function(renderTo, config){
31331 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31333 * @event arrowclick
31334 * Fires when this button's arrow is clicked
31335 * @param {SplitButton} this
31336 * @param {EventObject} e The click event
31338 this.addEvents({"arrowclick":true});
31341 Roo.extend(Roo.SplitButton, Roo.Button, {
31342 render : function(renderTo){
31343 // this is one sweet looking template!
31344 var tpl = new Roo.Template(
31345 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31346 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31347 '<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>',
31348 "</tbody></table></td><td>",
31349 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31350 '<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>',
31351 "</tbody></table></td></tr></table>"
31353 var btn = tpl.append(renderTo, [this.text, this.type], true);
31354 var btnEl = btn.child("button");
31356 btn.addClass(this.cls);
31359 btnEl.setStyle('background-image', 'url(' +this.icon +')');
31362 btnEl.addClass(this.iconCls);
31364 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31368 if(this.handleMouseEvents){
31369 btn.on("mouseover", this.onMouseOver, this);
31370 btn.on("mouseout", this.onMouseOut, this);
31371 btn.on("mousedown", this.onMouseDown, this);
31372 btn.on("mouseup", this.onMouseUp, this);
31374 btn.on(this.clickEvent, this.onClick, this);
31376 if(typeof this.tooltip == 'object'){
31377 Roo.QuickTips.tips(Roo.apply({
31381 btnEl.dom[this.tooltipType] = this.tooltip;
31384 if(this.arrowTooltip){
31385 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31394 this.el.addClass("x-btn-pressed");
31396 if(Roo.isIE && !Roo.isIE7){
31397 this.autoWidth.defer(1, this);
31402 this.menu.on("show", this.onMenuShow, this);
31403 this.menu.on("hide", this.onMenuHide, this);
31405 this.fireEvent('render', this);
31409 autoWidth : function(){
31411 var tbl = this.el.child("table:first");
31412 var tbl2 = this.el.child("table:last");
31413 this.el.setWidth("auto");
31414 tbl.setWidth("auto");
31415 if(Roo.isIE7 && Roo.isStrict){
31416 var ib = this.el.child('button:first');
31417 if(ib && ib.getWidth() > 20){
31419 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31424 this.el.beginMeasure();
31426 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31427 tbl.setWidth(this.minWidth-tbl2.getWidth());
31430 this.el.endMeasure();
31433 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31437 * Sets this button's click handler
31438 * @param {Function} handler The function to call when the button is clicked
31439 * @param {Object} scope (optional) Scope for the function passed above
31441 setHandler : function(handler, scope){
31442 this.handler = handler;
31443 this.scope = scope;
31447 * Sets this button's arrow click handler
31448 * @param {Function} handler The function to call when the arrow is clicked
31449 * @param {Object} scope (optional) Scope for the function passed above
31451 setArrowHandler : function(handler, scope){
31452 this.arrowHandler = handler;
31453 this.scope = scope;
31459 focus : function(){
31461 this.el.child("button:first").focus();
31466 onClick : function(e){
31467 e.preventDefault();
31468 if(!this.disabled){
31469 if(e.getTarget(".x-btn-menu-arrow-wrap")){
31470 if(this.menu && !this.menu.isVisible()){
31471 this.menu.show(this.el, this.menuAlign);
31473 this.fireEvent("arrowclick", this, e);
31474 if(this.arrowHandler){
31475 this.arrowHandler.call(this.scope || this, this, e);
31478 this.fireEvent("click", this, e);
31480 this.handler.call(this.scope || this, this, e);
31486 onMouseDown : function(e){
31487 if(!this.disabled){
31488 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31492 onMouseUp : function(e){
31493 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31498 // backwards compat
31499 Roo.MenuButton = Roo.SplitButton;/*
31501 * Ext JS Library 1.1.1
31502 * Copyright(c) 2006-2007, Ext JS, LLC.
31504 * Originally Released Under LGPL - original licence link has changed is not relivant.
31507 * <script type="text/javascript">
31511 * @class Roo.Toolbar
31512 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31513 * Basic Toolbar class.
31515 * Creates a new Toolbar
31516 * @param {Object} container The config object
31518 Roo.Toolbar = function(container, buttons, config)
31520 /// old consturctor format still supported..
31521 if(container instanceof Array){ // omit the container for later rendering
31522 buttons = container;
31526 if (typeof(container) == 'object' && container.xtype) {
31527 config = container;
31528 container = config.container;
31529 buttons = config.buttons || []; // not really - use items!!
31532 if (config && config.items) {
31533 xitems = config.items;
31534 delete config.items;
31536 Roo.apply(this, config);
31537 this.buttons = buttons;
31540 this.render(container);
31542 this.xitems = xitems;
31543 Roo.each(xitems, function(b) {
31549 Roo.Toolbar.prototype = {
31551 * @cfg {Array} items
31552 * array of button configs or elements to add (will be converted to a MixedCollection)
31556 * @cfg {String/HTMLElement/Element} container
31557 * The id or element that will contain the toolbar
31560 render : function(ct){
31561 this.el = Roo.get(ct);
31563 this.el.addClass(this.cls);
31565 // using a table allows for vertical alignment
31566 // 100% width is needed by Safari...
31567 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31568 this.tr = this.el.child("tr", true);
31570 this.items = new Roo.util.MixedCollection(false, function(o){
31571 return o.id || ("item" + (++autoId));
31574 this.add.apply(this, this.buttons);
31575 delete this.buttons;
31580 * Adds element(s) to the toolbar -- this function takes a variable number of
31581 * arguments of mixed type and adds them to the toolbar.
31582 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31584 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31585 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31586 * <li>Field: Any form field (equivalent to {@link #addField})</li>
31587 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31588 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31589 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31590 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31591 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31592 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31594 * @param {Mixed} arg2
31595 * @param {Mixed} etc.
31598 var a = arguments, l = a.length;
31599 for(var i = 0; i < l; i++){
31604 _add : function(el) {
31607 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31610 if (el.applyTo){ // some kind of form field
31611 return this.addField(el);
31613 if (el.render){ // some kind of Toolbar.Item
31614 return this.addItem(el);
31616 if (typeof el == "string"){ // string
31617 if(el == "separator" || el == "-"){
31618 return this.addSeparator();
31621 return this.addSpacer();
31624 return this.addFill();
31626 return this.addText(el);
31629 if(el.tagName){ // element
31630 return this.addElement(el);
31632 if(typeof el == "object"){ // must be button config?
31633 return this.addButton(el);
31635 // and now what?!?!
31641 * Add an Xtype element
31642 * @param {Object} xtype Xtype Object
31643 * @return {Object} created Object
31645 addxtype : function(e){
31646 return this.add(e);
31650 * Returns the Element for this toolbar.
31651 * @return {Roo.Element}
31653 getEl : function(){
31659 * @return {Roo.Toolbar.Item} The separator item
31661 addSeparator : function(){
31662 return this.addItem(new Roo.Toolbar.Separator());
31666 * Adds a spacer element
31667 * @return {Roo.Toolbar.Spacer} The spacer item
31669 addSpacer : function(){
31670 return this.addItem(new Roo.Toolbar.Spacer());
31674 * Adds a fill element that forces subsequent additions to the right side of the toolbar
31675 * @return {Roo.Toolbar.Fill} The fill item
31677 addFill : function(){
31678 return this.addItem(new Roo.Toolbar.Fill());
31682 * Adds any standard HTML element to the toolbar
31683 * @param {String/HTMLElement/Element} el The element or id of the element to add
31684 * @return {Roo.Toolbar.Item} The element's item
31686 addElement : function(el){
31687 return this.addItem(new Roo.Toolbar.Item(el));
31690 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31691 * @type Roo.util.MixedCollection
31696 * Adds any Toolbar.Item or subclass
31697 * @param {Roo.Toolbar.Item} item
31698 * @return {Roo.Toolbar.Item} The item
31700 addItem : function(item){
31701 var td = this.nextBlock();
31703 this.items.add(item);
31708 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31709 * @param {Object/Array} config A button config or array of configs
31710 * @return {Roo.Toolbar.Button/Array}
31712 addButton : function(config){
31713 if(config instanceof Array){
31715 for(var i = 0, len = config.length; i < len; i++) {
31716 buttons.push(this.addButton(config[i]));
31721 if(!(config instanceof Roo.Toolbar.Button)){
31723 new Roo.Toolbar.SplitButton(config) :
31724 new Roo.Toolbar.Button(config);
31726 var td = this.nextBlock();
31733 * Adds text to the toolbar
31734 * @param {String} text The text to add
31735 * @return {Roo.Toolbar.Item} The element's item
31737 addText : function(text){
31738 return this.addItem(new Roo.Toolbar.TextItem(text));
31742 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31743 * @param {Number} index The index where the item is to be inserted
31744 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31745 * @return {Roo.Toolbar.Button/Item}
31747 insertButton : function(index, item){
31748 if(item instanceof Array){
31750 for(var i = 0, len = item.length; i < len; i++) {
31751 buttons.push(this.insertButton(index + i, item[i]));
31755 if (!(item instanceof Roo.Toolbar.Button)){
31756 item = new Roo.Toolbar.Button(item);
31758 var td = document.createElement("td");
31759 this.tr.insertBefore(td, this.tr.childNodes[index]);
31761 this.items.insert(index, item);
31766 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31767 * @param {Object} config
31768 * @return {Roo.Toolbar.Item} The element's item
31770 addDom : function(config, returnEl){
31771 var td = this.nextBlock();
31772 Roo.DomHelper.overwrite(td, config);
31773 var ti = new Roo.Toolbar.Item(td.firstChild);
31775 this.items.add(ti);
31780 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31781 * @type Roo.util.MixedCollection
31786 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31787 * Note: the field should not have been rendered yet. For a field that has already been
31788 * rendered, use {@link #addElement}.
31789 * @param {Roo.form.Field} field
31790 * @return {Roo.ToolbarItem}
31794 addField : function(field) {
31795 if (!this.fields) {
31797 this.fields = new Roo.util.MixedCollection(false, function(o){
31798 return o.id || ("item" + (++autoId));
31803 var td = this.nextBlock();
31805 var ti = new Roo.Toolbar.Item(td.firstChild);
31807 this.items.add(ti);
31808 this.fields.add(field);
31819 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31820 this.el.child('div').hide();
31828 this.el.child('div').show();
31832 nextBlock : function(){
31833 var td = document.createElement("td");
31834 this.tr.appendChild(td);
31839 destroy : function(){
31840 if(this.items){ // rendered?
31841 Roo.destroy.apply(Roo, this.items.items);
31843 if(this.fields){ // rendered?
31844 Roo.destroy.apply(Roo, this.fields.items);
31846 Roo.Element.uncache(this.el, this.tr);
31851 * @class Roo.Toolbar.Item
31852 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31854 * Creates a new Item
31855 * @param {HTMLElement} el
31857 Roo.Toolbar.Item = function(el){
31859 if (typeof (el.xtype) != 'undefined') {
31864 this.el = Roo.getDom(el);
31865 this.id = Roo.id(this.el);
31866 this.hidden = false;
31871 * Fires when the button is rendered
31872 * @param {Button} this
31876 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31878 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31879 //Roo.Toolbar.Item.prototype = {
31882 * Get this item's HTML Element
31883 * @return {HTMLElement}
31885 getEl : function(){
31890 render : function(td){
31893 td.appendChild(this.el);
31895 this.fireEvent('render', this);
31899 * Removes and destroys this item.
31901 destroy : function(){
31902 this.td.parentNode.removeChild(this.td);
31909 this.hidden = false;
31910 this.td.style.display = "";
31917 this.hidden = true;
31918 this.td.style.display = "none";
31922 * Convenience function for boolean show/hide.
31923 * @param {Boolean} visible true to show/false to hide
31925 setVisible: function(visible){
31934 * Try to focus this item.
31936 focus : function(){
31937 Roo.fly(this.el).focus();
31941 * Disables this item.
31943 disable : function(){
31944 Roo.fly(this.td).addClass("x-item-disabled");
31945 this.disabled = true;
31946 this.el.disabled = true;
31950 * Enables this item.
31952 enable : function(){
31953 Roo.fly(this.td).removeClass("x-item-disabled");
31954 this.disabled = false;
31955 this.el.disabled = false;
31961 * @class Roo.Toolbar.Separator
31962 * @extends Roo.Toolbar.Item
31963 * A simple toolbar separator class
31965 * Creates a new Separator
31967 Roo.Toolbar.Separator = function(cfg){
31969 var s = document.createElement("span");
31970 s.className = "ytb-sep";
31975 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31977 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31978 enable:Roo.emptyFn,
31979 disable:Roo.emptyFn,
31984 * @class Roo.Toolbar.Spacer
31985 * @extends Roo.Toolbar.Item
31986 * A simple element that adds extra horizontal space to a toolbar.
31988 * Creates a new Spacer
31990 Roo.Toolbar.Spacer = function(cfg){
31991 var s = document.createElement("div");
31992 s.className = "ytb-spacer";
31996 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31998 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31999 enable:Roo.emptyFn,
32000 disable:Roo.emptyFn,
32005 * @class Roo.Toolbar.Fill
32006 * @extends Roo.Toolbar.Spacer
32007 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32009 * Creates a new Spacer
32011 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32013 render : function(td){
32014 td.style.width = '100%';
32015 Roo.Toolbar.Fill.superclass.render.call(this, td);
32020 * @class Roo.Toolbar.TextItem
32021 * @extends Roo.Toolbar.Item
32022 * A simple class that renders text directly into a toolbar.
32024 * Creates a new TextItem
32025 * @cfg {string} text
32027 Roo.Toolbar.TextItem = function(cfg){
32028 var text = cfg || "";
32029 if (typeof(cfg) == 'object') {
32030 text = cfg.text || "";
32034 var s = document.createElement("span");
32035 s.className = "ytb-text";
32036 s.innerHTML = text;
32041 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
32043 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32046 enable:Roo.emptyFn,
32047 disable:Roo.emptyFn,
32050 * Shows this button
32053 this.hidden = false;
32054 this.el.style.display = "";
32058 * Hides this button
32061 this.hidden = true;
32062 this.el.style.display = "none";
32068 * @class Roo.Toolbar.Button
32069 * @extends Roo.Button
32070 * A button that renders into a toolbar.
32072 * Creates a new Button
32073 * @param {Object} config A standard {@link Roo.Button} config object
32075 Roo.Toolbar.Button = function(config){
32076 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32078 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32082 render : function(td){
32084 Roo.Toolbar.Button.superclass.render.call(this, td);
32088 * Removes and destroys this button
32090 destroy : function(){
32091 Roo.Toolbar.Button.superclass.destroy.call(this);
32092 this.td.parentNode.removeChild(this.td);
32096 * Shows this button
32099 this.hidden = false;
32100 this.td.style.display = "";
32104 * Hides this button
32107 this.hidden = true;
32108 this.td.style.display = "none";
32112 * Disables this item
32114 disable : function(){
32115 Roo.fly(this.td).addClass("x-item-disabled");
32116 this.disabled = true;
32120 * Enables this item
32122 enable : function(){
32123 Roo.fly(this.td).removeClass("x-item-disabled");
32124 this.disabled = false;
32127 // backwards compat
32128 Roo.ToolbarButton = Roo.Toolbar.Button;
32131 * @class Roo.Toolbar.SplitButton
32132 * @extends Roo.SplitButton
32133 * A menu button that renders into a toolbar.
32135 * Creates a new SplitButton
32136 * @param {Object} config A standard {@link Roo.SplitButton} config object
32138 Roo.Toolbar.SplitButton = function(config){
32139 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32141 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32142 render : function(td){
32144 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32148 * Removes and destroys this button
32150 destroy : function(){
32151 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32152 this.td.parentNode.removeChild(this.td);
32156 * Shows this button
32159 this.hidden = false;
32160 this.td.style.display = "";
32164 * Hides this button
32167 this.hidden = true;
32168 this.td.style.display = "none";
32172 // backwards compat
32173 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32175 * Ext JS Library 1.1.1
32176 * Copyright(c) 2006-2007, Ext JS, LLC.
32178 * Originally Released Under LGPL - original licence link has changed is not relivant.
32181 * <script type="text/javascript">
32185 * @class Roo.PagingToolbar
32186 * @extends Roo.Toolbar
32187 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32188 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32190 * Create a new PagingToolbar
32191 * @param {Object} config The config object
32193 Roo.PagingToolbar = function(el, ds, config)
32195 // old args format still supported... - xtype is prefered..
32196 if (typeof(el) == 'object' && el.xtype) {
32197 // created from xtype...
32199 ds = el.dataSource;
32200 el = config.container;
32203 if (config.items) {
32204 items = config.items;
32208 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32211 this.renderButtons(this.el);
32214 // supprot items array.
32216 Roo.each(items, function(e) {
32217 this.add(Roo.factory(e));
32222 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32225 * @cfg {String/HTMLElement/Element} container
32226 * container The id or element that will contain the toolbar
32229 * @cfg {Boolean} displayInfo
32230 * True to display the displayMsg (defaults to false)
32235 * @cfg {Number} pageSize
32236 * The number of records to display per page (defaults to 20)
32240 * @cfg {String} displayMsg
32241 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32243 displayMsg : 'Displaying {0} - {1} of {2}',
32245 * @cfg {String} emptyMsg
32246 * The message to display when no records are found (defaults to "No data to display")
32248 emptyMsg : 'No data to display',
32250 * Customizable piece of the default paging text (defaults to "Page")
32253 beforePageText : "Page",
32255 * Customizable piece of the default paging text (defaults to "of %0")
32258 afterPageText : "of {0}",
32260 * Customizable piece of the default paging text (defaults to "First Page")
32263 firstText : "First Page",
32265 * Customizable piece of the default paging text (defaults to "Previous Page")
32268 prevText : "Previous Page",
32270 * Customizable piece of the default paging text (defaults to "Next Page")
32273 nextText : "Next Page",
32275 * Customizable piece of the default paging text (defaults to "Last Page")
32278 lastText : "Last Page",
32280 * Customizable piece of the default paging text (defaults to "Refresh")
32283 refreshText : "Refresh",
32286 renderButtons : function(el){
32287 Roo.PagingToolbar.superclass.render.call(this, el);
32288 this.first = this.addButton({
32289 tooltip: this.firstText,
32290 cls: "x-btn-icon x-grid-page-first",
32292 handler: this.onClick.createDelegate(this, ["first"])
32294 this.prev = this.addButton({
32295 tooltip: this.prevText,
32296 cls: "x-btn-icon x-grid-page-prev",
32298 handler: this.onClick.createDelegate(this, ["prev"])
32300 //this.addSeparator();
32301 this.add(this.beforePageText);
32302 this.field = Roo.get(this.addDom({
32307 cls: "x-grid-page-number"
32309 this.field.on("keydown", this.onPagingKeydown, this);
32310 this.field.on("focus", function(){this.dom.select();});
32311 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32312 this.field.setHeight(18);
32313 //this.addSeparator();
32314 this.next = this.addButton({
32315 tooltip: this.nextText,
32316 cls: "x-btn-icon x-grid-page-next",
32318 handler: this.onClick.createDelegate(this, ["next"])
32320 this.last = this.addButton({
32321 tooltip: this.lastText,
32322 cls: "x-btn-icon x-grid-page-last",
32324 handler: this.onClick.createDelegate(this, ["last"])
32326 //this.addSeparator();
32327 this.loading = this.addButton({
32328 tooltip: this.refreshText,
32329 cls: "x-btn-icon x-grid-loading",
32330 handler: this.onClick.createDelegate(this, ["refresh"])
32333 if(this.displayInfo){
32334 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32339 updateInfo : function(){
32340 if(this.displayEl){
32341 var count = this.ds.getCount();
32342 var msg = count == 0 ?
32346 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32348 this.displayEl.update(msg);
32353 onLoad : function(ds, r, o){
32354 this.cursor = o.params ? o.params.start : 0;
32355 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32357 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32358 this.field.dom.value = ap;
32359 this.first.setDisabled(ap == 1);
32360 this.prev.setDisabled(ap == 1);
32361 this.next.setDisabled(ap == ps);
32362 this.last.setDisabled(ap == ps);
32363 this.loading.enable();
32368 getPageData : function(){
32369 var total = this.ds.getTotalCount();
32372 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32373 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32378 onLoadError : function(){
32379 this.loading.enable();
32383 onPagingKeydown : function(e){
32384 var k = e.getKey();
32385 var d = this.getPageData();
32387 var v = this.field.dom.value, pageNum;
32388 if(!v || isNaN(pageNum = parseInt(v, 10))){
32389 this.field.dom.value = d.activePage;
32392 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32393 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32396 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))
32398 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32399 this.field.dom.value = pageNum;
32400 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32403 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32405 var v = this.field.dom.value, pageNum;
32406 var increment = (e.shiftKey) ? 10 : 1;
32407 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32410 if(!v || isNaN(pageNum = parseInt(v, 10))) {
32411 this.field.dom.value = d.activePage;
32414 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32416 this.field.dom.value = parseInt(v, 10) + increment;
32417 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32418 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32425 beforeLoad : function(){
32427 this.loading.disable();
32431 * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32432 * @param {String} which (first|prev|next|last|refresh) which button to press.
32436 onClick : function(which){
32440 ds.load({params:{start: 0, limit: this.pageSize}});
32443 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32446 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32449 var total = ds.getTotalCount();
32450 var extra = total % this.pageSize;
32451 var lastStart = extra ? (total - extra) : total-this.pageSize;
32452 ds.load({params:{start: lastStart, limit: this.pageSize}});
32455 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32461 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32462 * @param {Roo.data.Store} store The data store to unbind
32464 unbind : function(ds){
32465 ds.un("beforeload", this.beforeLoad, this);
32466 ds.un("load", this.onLoad, this);
32467 ds.un("loadexception", this.onLoadError, this);
32468 ds.un("remove", this.updateInfo, this);
32469 ds.un("add", this.updateInfo, this);
32470 this.ds = undefined;
32474 * Binds the paging toolbar to the specified {@link Roo.data.Store}
32475 * @param {Roo.data.Store} store The data store to bind
32477 bind : function(ds){
32478 ds.on("beforeload", this.beforeLoad, this);
32479 ds.on("load", this.onLoad, this);
32480 ds.on("loadexception", this.onLoadError, this);
32481 ds.on("remove", this.updateInfo, this);
32482 ds.on("add", this.updateInfo, this);
32487 * Ext JS Library 1.1.1
32488 * Copyright(c) 2006-2007, Ext JS, LLC.
32490 * Originally Released Under LGPL - original licence link has changed is not relivant.
32493 * <script type="text/javascript">
32497 * @class Roo.Resizable
32498 * @extends Roo.util.Observable
32499 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32500 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32501 * 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
32502 * the element will be wrapped for you automatically.</p>
32503 * <p>Here is the list of valid resize handles:</p>
32506 ------ -------------------
32515 'hd' horizontal drag
32518 * <p>Here's an example showing the creation of a typical Resizable:</p>
32520 var resizer = new Roo.Resizable("element-id", {
32528 resizer.on("resize", myHandler);
32530 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32531 * resizer.east.setDisplayed(false);</p>
32532 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32533 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32534 * resize operation's new size (defaults to [0, 0])
32535 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32536 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32537 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32538 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32539 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32540 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32541 * @cfg {Number} width The width of the element in pixels (defaults to null)
32542 * @cfg {Number} height The height of the element in pixels (defaults to null)
32543 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32544 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32545 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32546 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32547 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
32548 * in favor of the handles config option (defaults to false)
32549 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32550 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32551 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32552 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32553 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32554 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32555 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32556 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32557 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32558 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32559 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32561 * Create a new resizable component
32562 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32563 * @param {Object} config configuration options
32565 Roo.Resizable = function(el, config)
32567 this.el = Roo.get(el);
32569 if(config && config.wrap){
32570 config.resizeChild = this.el;
32571 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32572 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32573 this.el.setStyle("overflow", "hidden");
32574 this.el.setPositioning(config.resizeChild.getPositioning());
32575 config.resizeChild.clearPositioning();
32576 if(!config.width || !config.height){
32577 var csize = config.resizeChild.getSize();
32578 this.el.setSize(csize.width, csize.height);
32580 if(config.pinned && !config.adjustments){
32581 config.adjustments = "auto";
32585 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32586 this.proxy.unselectable();
32587 this.proxy.enableDisplayMode('block');
32589 Roo.apply(this, config);
32592 this.disableTrackOver = true;
32593 this.el.addClass("x-resizable-pinned");
32595 // if the element isn't positioned, make it relative
32596 var position = this.el.getStyle("position");
32597 if(position != "absolute" && position != "fixed"){
32598 this.el.setStyle("position", "relative");
32600 if(!this.handles){ // no handles passed, must be legacy style
32601 this.handles = 's,e,se';
32602 if(this.multiDirectional){
32603 this.handles += ',n,w';
32606 if(this.handles == "all"){
32607 this.handles = "n s e w ne nw se sw";
32609 var hs = this.handles.split(/\s*?[,;]\s*?| /);
32610 var ps = Roo.Resizable.positions;
32611 for(var i = 0, len = hs.length; i < len; i++){
32612 if(hs[i] && ps[hs[i]]){
32613 var pos = ps[hs[i]];
32614 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32618 this.corner = this.southeast;
32620 // updateBox = the box can move..
32621 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32622 this.updateBox = true;
32625 this.activeHandle = null;
32627 if(this.resizeChild){
32628 if(typeof this.resizeChild == "boolean"){
32629 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32631 this.resizeChild = Roo.get(this.resizeChild, true);
32635 if(this.adjustments == "auto"){
32636 var rc = this.resizeChild;
32637 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32638 if(rc && (hw || hn)){
32639 rc.position("relative");
32640 rc.setLeft(hw ? hw.el.getWidth() : 0);
32641 rc.setTop(hn ? hn.el.getHeight() : 0);
32643 this.adjustments = [
32644 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32645 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32649 if(this.draggable){
32650 this.dd = this.dynamic ?
32651 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32652 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32658 * @event beforeresize
32659 * Fired before resize is allowed. Set enabled to false to cancel resize.
32660 * @param {Roo.Resizable} this
32661 * @param {Roo.EventObject} e The mousedown event
32663 "beforeresize" : true,
32666 * Fired a resizing.
32667 * @param {Roo.Resizable} this
32668 * @param {Number} x The new x position
32669 * @param {Number} y The new y position
32670 * @param {Number} w The new w width
32671 * @param {Number} h The new h hight
32672 * @param {Roo.EventObject} e The mouseup event
32677 * Fired after a resize.
32678 * @param {Roo.Resizable} this
32679 * @param {Number} width The new width
32680 * @param {Number} height The new height
32681 * @param {Roo.EventObject} e The mouseup event
32686 if(this.width !== null && this.height !== null){
32687 this.resizeTo(this.width, this.height);
32689 this.updateChildSize();
32692 this.el.dom.style.zoom = 1;
32694 Roo.Resizable.superclass.constructor.call(this);
32697 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32698 resizeChild : false,
32699 adjustments : [0, 0],
32709 multiDirectional : false,
32710 disableTrackOver : false,
32711 easing : 'easeOutStrong',
32712 widthIncrement : 0,
32713 heightIncrement : 0,
32717 preserveRatio : false,
32718 transparent: false,
32724 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32726 constrainTo: undefined,
32728 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32730 resizeRegion: undefined,
32734 * Perform a manual resize
32735 * @param {Number} width
32736 * @param {Number} height
32738 resizeTo : function(width, height){
32739 this.el.setSize(width, height);
32740 this.updateChildSize();
32741 this.fireEvent("resize", this, width, height, null);
32745 startSizing : function(e, handle){
32746 this.fireEvent("beforeresize", this, e);
32747 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32750 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
32751 this.overlay.unselectable();
32752 this.overlay.enableDisplayMode("block");
32753 this.overlay.on("mousemove", this.onMouseMove, this);
32754 this.overlay.on("mouseup", this.onMouseUp, this);
32756 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32758 this.resizing = true;
32759 this.startBox = this.el.getBox();
32760 this.startPoint = e.getXY();
32761 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32762 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32764 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32765 this.overlay.show();
32767 if(this.constrainTo) {
32768 var ct = Roo.get(this.constrainTo);
32769 this.resizeRegion = ct.getRegion().adjust(
32770 ct.getFrameWidth('t'),
32771 ct.getFrameWidth('l'),
32772 -ct.getFrameWidth('b'),
32773 -ct.getFrameWidth('r')
32777 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32779 this.proxy.setBox(this.startBox);
32781 this.proxy.setStyle('visibility', 'visible');
32787 onMouseDown : function(handle, e){
32790 this.activeHandle = handle;
32791 this.startSizing(e, handle);
32796 onMouseUp : function(e){
32797 var size = this.resizeElement();
32798 this.resizing = false;
32800 this.overlay.hide();
32802 this.fireEvent("resize", this, size.width, size.height, e);
32806 updateChildSize : function(){
32808 if(this.resizeChild){
32810 var child = this.resizeChild;
32811 var adj = this.adjustments;
32812 if(el.dom.offsetWidth){
32813 var b = el.getSize(true);
32814 child.setSize(b.width+adj[0], b.height+adj[1]);
32816 // Second call here for IE
32817 // The first call enables instant resizing and
32818 // the second call corrects scroll bars if they
32821 setTimeout(function(){
32822 if(el.dom.offsetWidth){
32823 var b = el.getSize(true);
32824 child.setSize(b.width+adj[0], b.height+adj[1]);
32832 snap : function(value, inc, min){
32833 if(!inc || !value) {
32836 var newValue = value;
32837 var m = value % inc;
32840 newValue = value + (inc-m);
32842 newValue = value - m;
32845 return Math.max(min, newValue);
32849 resizeElement : function(){
32850 var box = this.proxy.getBox();
32851 if(this.updateBox){
32852 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32854 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32856 this.updateChildSize();
32864 constrain : function(v, diff, m, mx){
32867 }else if(v - diff > mx){
32874 onMouseMove : function(e){
32877 try{// try catch so if something goes wrong the user doesn't get hung
32879 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32883 //var curXY = this.startPoint;
32884 var curSize = this.curSize || this.startBox;
32885 var x = this.startBox.x, y = this.startBox.y;
32886 var ox = x, oy = y;
32887 var w = curSize.width, h = curSize.height;
32888 var ow = w, oh = h;
32889 var mw = this.minWidth, mh = this.minHeight;
32890 var mxw = this.maxWidth, mxh = this.maxHeight;
32891 var wi = this.widthIncrement;
32892 var hi = this.heightIncrement;
32894 var eventXY = e.getXY();
32895 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32896 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32898 var pos = this.activeHandle.position;
32903 w = Math.min(Math.max(mw, w), mxw);
32908 h = Math.min(Math.max(mh, h), mxh);
32913 w = Math.min(Math.max(mw, w), mxw);
32914 h = Math.min(Math.max(mh, h), mxh);
32917 diffY = this.constrain(h, diffY, mh, mxh);
32924 var adiffX = Math.abs(diffX);
32925 var sub = (adiffX % wi); // how much
32926 if (sub > (wi/2)) { // far enough to snap
32927 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32929 // remove difference..
32930 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32934 x = Math.max(this.minX, x);
32937 diffX = this.constrain(w, diffX, mw, mxw);
32943 w = Math.min(Math.max(mw, w), mxw);
32944 diffY = this.constrain(h, diffY, mh, mxh);
32949 diffX = this.constrain(w, diffX, mw, mxw);
32950 diffY = this.constrain(h, diffY, mh, mxh);
32957 diffX = this.constrain(w, diffX, mw, mxw);
32959 h = Math.min(Math.max(mh, h), mxh);
32965 var sw = this.snap(w, wi, mw);
32966 var sh = this.snap(h, hi, mh);
32967 if(sw != w || sh != h){
32990 if(this.preserveRatio){
32995 h = Math.min(Math.max(mh, h), mxh);
33000 w = Math.min(Math.max(mw, w), mxw);
33005 w = Math.min(Math.max(mw, w), mxw);
33011 w = Math.min(Math.max(mw, w), mxw);
33017 h = Math.min(Math.max(mh, h), mxh);
33025 h = Math.min(Math.max(mh, h), mxh);
33035 h = Math.min(Math.max(mh, h), mxh);
33043 if (pos == 'hdrag') {
33046 this.proxy.setBounds(x, y, w, h);
33048 this.resizeElement();
33052 this.fireEvent("resizing", this, x, y, w, h, e);
33056 handleOver : function(){
33058 this.el.addClass("x-resizable-over");
33063 handleOut : function(){
33064 if(!this.resizing){
33065 this.el.removeClass("x-resizable-over");
33070 * Returns the element this component is bound to.
33071 * @return {Roo.Element}
33073 getEl : function(){
33078 * Returns the resizeChild element (or null).
33079 * @return {Roo.Element}
33081 getResizeChild : function(){
33082 return this.resizeChild;
33084 groupHandler : function()
33089 * Destroys this resizable. If the element was wrapped and
33090 * removeEl is not true then the element remains.
33091 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33093 destroy : function(removeEl){
33094 this.proxy.remove();
33096 this.overlay.removeAllListeners();
33097 this.overlay.remove();
33099 var ps = Roo.Resizable.positions;
33101 if(typeof ps[k] != "function" && this[ps[k]]){
33102 var h = this[ps[k]];
33103 h.el.removeAllListeners();
33108 this.el.update("");
33115 // hash to map config positions to true positions
33116 Roo.Resizable.positions = {
33117 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
33122 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33124 // only initialize the template if resizable is used
33125 var tpl = Roo.DomHelper.createTemplate(
33126 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33129 Roo.Resizable.Handle.prototype.tpl = tpl;
33131 this.position = pos;
33133 // show north drag fro topdra
33134 var handlepos = pos == 'hdrag' ? 'north' : pos;
33136 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33137 if (pos == 'hdrag') {
33138 this.el.setStyle('cursor', 'pointer');
33140 this.el.unselectable();
33142 this.el.setOpacity(0);
33144 this.el.on("mousedown", this.onMouseDown, this);
33145 if(!disableTrackOver){
33146 this.el.on("mouseover", this.onMouseOver, this);
33147 this.el.on("mouseout", this.onMouseOut, this);
33152 Roo.Resizable.Handle.prototype = {
33153 afterResize : function(rz){
33158 onMouseDown : function(e){
33159 this.rz.onMouseDown(this, e);
33162 onMouseOver : function(e){
33163 this.rz.handleOver(this, e);
33166 onMouseOut : function(e){
33167 this.rz.handleOut(this, e);
33171 * Ext JS Library 1.1.1
33172 * Copyright(c) 2006-2007, Ext JS, LLC.
33174 * Originally Released Under LGPL - original licence link has changed is not relivant.
33177 * <script type="text/javascript">
33181 * @class Roo.Editor
33182 * @extends Roo.Component
33183 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33185 * Create a new Editor
33186 * @param {Roo.form.Field} field The Field object (or descendant)
33187 * @param {Object} config The config object
33189 Roo.Editor = function(field, config){
33190 Roo.Editor.superclass.constructor.call(this, config);
33191 this.field = field;
33194 * @event beforestartedit
33195 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
33196 * false from the handler of this event.
33197 * @param {Editor} this
33198 * @param {Roo.Element} boundEl The underlying element bound to this editor
33199 * @param {Mixed} value The field value being set
33201 "beforestartedit" : true,
33204 * Fires when this editor is displayed
33205 * @param {Roo.Element} boundEl The underlying element bound to this editor
33206 * @param {Mixed} value The starting field value
33208 "startedit" : true,
33210 * @event beforecomplete
33211 * Fires after a change has been made to the field, but before the change is reflected in the underlying
33212 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
33213 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33214 * event will not fire since no edit actually occurred.
33215 * @param {Editor} this
33216 * @param {Mixed} value The current field value
33217 * @param {Mixed} startValue The original field value
33219 "beforecomplete" : true,
33222 * Fires after editing is complete and any changed value has been written to the underlying field.
33223 * @param {Editor} this
33224 * @param {Mixed} value The current field value
33225 * @param {Mixed} startValue The original field value
33229 * @event specialkey
33230 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
33231 * {@link Roo.EventObject#getKey} to determine which key was pressed.
33232 * @param {Roo.form.Field} this
33233 * @param {Roo.EventObject} e The event object
33235 "specialkey" : true
33239 Roo.extend(Roo.Editor, Roo.Component, {
33241 * @cfg {Boolean/String} autosize
33242 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33243 * or "height" to adopt the height only (defaults to false)
33246 * @cfg {Boolean} revertInvalid
33247 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33248 * validation fails (defaults to true)
33251 * @cfg {Boolean} ignoreNoChange
33252 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33253 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
33254 * will never be ignored.
33257 * @cfg {Boolean} hideEl
33258 * False to keep the bound element visible while the editor is displayed (defaults to true)
33261 * @cfg {Mixed} value
33262 * The data value of the underlying field (defaults to "")
33266 * @cfg {String} alignment
33267 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33271 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33272 * for bottom-right shadow (defaults to "frame")
33276 * @cfg {Boolean} constrain True to constrain the editor to the viewport
33280 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33282 completeOnEnter : false,
33284 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33286 cancelOnEsc : false,
33288 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33293 onRender : function(ct, position){
33294 this.el = new Roo.Layer({
33295 shadow: this.shadow,
33301 constrain: this.constrain
33303 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33304 if(this.field.msgTarget != 'title'){
33305 this.field.msgTarget = 'qtip';
33307 this.field.render(this.el);
33309 this.field.el.dom.setAttribute('autocomplete', 'off');
33311 this.field.on("specialkey", this.onSpecialKey, this);
33312 if(this.swallowKeys){
33313 this.field.el.swallowEvent(['keydown','keypress']);
33316 this.field.on("blur", this.onBlur, this);
33317 if(this.field.grow){
33318 this.field.on("autosize", this.el.sync, this.el, {delay:1});
33322 onSpecialKey : function(field, e)
33324 //Roo.log('editor onSpecialKey');
33325 if(this.completeOnEnter && e.getKey() == e.ENTER){
33327 this.completeEdit();
33330 // do not fire special key otherwise it might hide close the editor...
33331 if(e.getKey() == e.ENTER){
33334 if(this.cancelOnEsc && e.getKey() == e.ESC){
33338 this.fireEvent('specialkey', field, e);
33343 * Starts the editing process and shows the editor.
33344 * @param {String/HTMLElement/Element} el The element to edit
33345 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33346 * to the innerHTML of el.
33348 startEdit : function(el, value){
33350 this.completeEdit();
33352 this.boundEl = Roo.get(el);
33353 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33354 if(!this.rendered){
33355 this.render(this.parentEl || document.body);
33357 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33360 this.startValue = v;
33361 this.field.setValue(v);
33363 var sz = this.boundEl.getSize();
33364 switch(this.autoSize){
33366 this.setSize(sz.width, "");
33369 this.setSize("", sz.height);
33372 this.setSize(sz.width, sz.height);
33375 this.el.alignTo(this.boundEl, this.alignment);
33376 this.editing = true;
33378 Roo.QuickTips.disable();
33384 * Sets the height and width of this editor.
33385 * @param {Number} width The new width
33386 * @param {Number} height The new height
33388 setSize : function(w, h){
33389 this.field.setSize(w, h);
33396 * Realigns the editor to the bound field based on the current alignment config value.
33398 realign : function(){
33399 this.el.alignTo(this.boundEl, this.alignment);
33403 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33404 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33406 completeEdit : function(remainVisible){
33410 var v = this.getValue();
33411 if(this.revertInvalid !== false && !this.field.isValid()){
33412 v = this.startValue;
33413 this.cancelEdit(true);
33415 if(String(v) === String(this.startValue) && this.ignoreNoChange){
33416 this.editing = false;
33420 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33421 this.editing = false;
33422 if(this.updateEl && this.boundEl){
33423 this.boundEl.update(v);
33425 if(remainVisible !== true){
33428 this.fireEvent("complete", this, v, this.startValue);
33433 onShow : function(){
33435 if(this.hideEl !== false){
33436 this.boundEl.hide();
33439 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33440 this.fixIEFocus = true;
33441 this.deferredFocus.defer(50, this);
33443 this.field.focus();
33445 this.fireEvent("startedit", this.boundEl, this.startValue);
33448 deferredFocus : function(){
33450 this.field.focus();
33455 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
33456 * reverted to the original starting value.
33457 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33458 * cancel (defaults to false)
33460 cancelEdit : function(remainVisible){
33462 this.setValue(this.startValue);
33463 if(remainVisible !== true){
33470 onBlur : function(){
33471 if(this.allowBlur !== true && this.editing){
33472 this.completeEdit();
33477 onHide : function(){
33479 this.completeEdit();
33483 if(this.field.collapse){
33484 this.field.collapse();
33487 if(this.hideEl !== false){
33488 this.boundEl.show();
33491 Roo.QuickTips.enable();
33496 * Sets the data value of the editor
33497 * @param {Mixed} value Any valid value supported by the underlying field
33499 setValue : function(v){
33500 this.field.setValue(v);
33504 * Gets the data value of the editor
33505 * @return {Mixed} The data value
33507 getValue : function(){
33508 return this.field.getValue();
33512 * Ext JS Library 1.1.1
33513 * Copyright(c) 2006-2007, Ext JS, LLC.
33515 * Originally Released Under LGPL - original licence link has changed is not relivant.
33518 * <script type="text/javascript">
33522 * @class Roo.BasicDialog
33523 * @extends Roo.util.Observable
33524 * @parent none builder
33525 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
33527 var dlg = new Roo.BasicDialog("my-dlg", {
33536 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33537 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
33538 dlg.addButton('Cancel', dlg.hide, dlg);
33541 <b>A Dialog should always be a direct child of the body element.</b>
33542 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33543 * @cfg {String} title Default text to display in the title bar (defaults to null)
33544 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33545 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33546 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33547 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33548 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33549 * (defaults to null with no animation)
33550 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33551 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33552 * property for valid values (defaults to 'all')
33553 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33554 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33555 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33556 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33557 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33558 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33559 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33560 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33561 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33562 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33563 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33564 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33565 * draggable = true (defaults to false)
33566 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33567 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33568 * shadow (defaults to false)
33569 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33570 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33571 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33572 * @cfg {Array} buttons Array of buttons
33573 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33575 * Create a new BasicDialog.
33576 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33577 * @param {Object} config Configuration options
33579 Roo.BasicDialog = function(el, config){
33580 this.el = Roo.get(el);
33581 var dh = Roo.DomHelper;
33582 if(!this.el && config && config.autoCreate){
33583 if(typeof config.autoCreate == "object"){
33584 if(!config.autoCreate.id){
33585 config.autoCreate.id = el;
33587 this.el = dh.append(document.body,
33588 config.autoCreate, true);
33590 this.el = dh.append(document.body,
33591 {tag: "div", id: el, style:'visibility:hidden;'}, true);
33595 el.setDisplayed(true);
33596 el.hide = this.hideAction;
33598 el.addClass("x-dlg");
33600 Roo.apply(this, config);
33602 this.proxy = el.createProxy("x-dlg-proxy");
33603 this.proxy.hide = this.hideAction;
33604 this.proxy.setOpacity(.5);
33608 el.setWidth(config.width);
33611 el.setHeight(config.height);
33613 this.size = el.getSize();
33614 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33615 this.xy = [config.x,config.y];
33617 this.xy = el.getCenterXY(true);
33619 /** The header element @type Roo.Element */
33620 this.header = el.child("> .x-dlg-hd");
33621 /** The body element @type Roo.Element */
33622 this.body = el.child("> .x-dlg-bd");
33623 /** The footer element @type Roo.Element */
33624 this.footer = el.child("> .x-dlg-ft");
33627 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
33630 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33633 this.header.unselectable();
33635 this.header.update(this.title);
33637 // this element allows the dialog to be focused for keyboard event
33638 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33639 this.focusEl.swallowEvent("click", true);
33641 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33643 // wrap the body and footer for special rendering
33644 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33646 this.bwrap.dom.appendChild(this.footer.dom);
33649 this.bg = this.el.createChild({
33650 tag: "div", cls:"x-dlg-bg",
33651 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
33653 this.centerBg = this.bg.child("div.x-dlg-bg-center");
33656 if(this.autoScroll !== false && !this.autoTabs){
33657 this.body.setStyle("overflow", "auto");
33660 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33662 if(this.closable !== false){
33663 this.el.addClass("x-dlg-closable");
33664 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33665 this.close.on("click", this.closeClick, this);
33666 this.close.addClassOnOver("x-dlg-close-over");
33668 if(this.collapsible !== false){
33669 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33670 this.collapseBtn.on("click", this.collapseClick, this);
33671 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33672 this.header.on("dblclick", this.collapseClick, this);
33674 if(this.resizable !== false){
33675 this.el.addClass("x-dlg-resizable");
33676 this.resizer = new Roo.Resizable(el, {
33677 minWidth: this.minWidth || 80,
33678 minHeight:this.minHeight || 80,
33679 handles: this.resizeHandles || "all",
33682 this.resizer.on("beforeresize", this.beforeResize, this);
33683 this.resizer.on("resize", this.onResize, this);
33685 if(this.draggable !== false){
33686 el.addClass("x-dlg-draggable");
33687 if (!this.proxyDrag) {
33688 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33691 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33693 dd.setHandleElId(this.header.id);
33694 dd.endDrag = this.endMove.createDelegate(this);
33695 dd.startDrag = this.startMove.createDelegate(this);
33696 dd.onDrag = this.onDrag.createDelegate(this);
33701 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33702 this.mask.enableDisplayMode("block");
33704 this.el.addClass("x-dlg-modal");
33707 this.shadow = new Roo.Shadow({
33708 mode : typeof this.shadow == "string" ? this.shadow : "sides",
33709 offset : this.shadowOffset
33712 this.shadowOffset = 0;
33714 if(Roo.useShims && this.shim !== false){
33715 this.shim = this.el.createShim();
33716 this.shim.hide = this.hideAction;
33724 if (this.buttons) {
33725 var bts= this.buttons;
33727 Roo.each(bts, function(b) {
33736 * Fires when a key is pressed
33737 * @param {Roo.BasicDialog} this
33738 * @param {Roo.EventObject} e
33743 * Fires when this dialog is moved by the user.
33744 * @param {Roo.BasicDialog} this
33745 * @param {Number} x The new page X
33746 * @param {Number} y The new page Y
33751 * Fires when this dialog is resized by the user.
33752 * @param {Roo.BasicDialog} this
33753 * @param {Number} width The new width
33754 * @param {Number} height The new height
33758 * @event beforehide
33759 * Fires before this dialog is hidden.
33760 * @param {Roo.BasicDialog} this
33762 "beforehide" : true,
33765 * Fires when this dialog is hidden.
33766 * @param {Roo.BasicDialog} this
33770 * @event beforeshow
33771 * Fires before this dialog is shown.
33772 * @param {Roo.BasicDialog} this
33774 "beforeshow" : true,
33777 * Fires when this dialog is shown.
33778 * @param {Roo.BasicDialog} this
33782 el.on("keydown", this.onKeyDown, this);
33783 el.on("mousedown", this.toFront, this);
33784 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33786 Roo.DialogManager.register(this);
33787 Roo.BasicDialog.superclass.constructor.call(this);
33790 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33791 shadowOffset: Roo.isIE ? 6 : 5,
33794 minButtonWidth: 75,
33795 defaultButton: null,
33796 buttonAlign: "right",
33801 * Sets the dialog title text
33802 * @param {String} text The title text to display
33803 * @return {Roo.BasicDialog} this
33805 setTitle : function(text){
33806 this.header.update(text);
33811 closeClick : function(){
33816 collapseClick : function(){
33817 this[this.collapsed ? "expand" : "collapse"]();
33821 * Collapses the dialog to its minimized state (only the title bar is visible).
33822 * Equivalent to the user clicking the collapse dialog button.
33824 collapse : function(){
33825 if(!this.collapsed){
33826 this.collapsed = true;
33827 this.el.addClass("x-dlg-collapsed");
33828 this.restoreHeight = this.el.getHeight();
33829 this.resizeTo(this.el.getWidth(), this.header.getHeight());
33834 * Expands a collapsed dialog back to its normal state. Equivalent to the user
33835 * clicking the expand dialog button.
33837 expand : function(){
33838 if(this.collapsed){
33839 this.collapsed = false;
33840 this.el.removeClass("x-dlg-collapsed");
33841 this.resizeTo(this.el.getWidth(), this.restoreHeight);
33846 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33847 * @return {Roo.TabPanel} The tabs component
33849 initTabs : function(){
33850 var tabs = this.getTabs();
33851 while(tabs.getTab(0)){
33854 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33856 tabs.addTab(Roo.id(dom), dom.title);
33864 beforeResize : function(){
33865 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33869 onResize : function(){
33870 this.refreshSize();
33871 this.syncBodyHeight();
33872 this.adjustAssets();
33874 this.fireEvent("resize", this, this.size.width, this.size.height);
33878 onKeyDown : function(e){
33879 if(this.isVisible()){
33880 this.fireEvent("keydown", this, e);
33885 * Resizes the dialog.
33886 * @param {Number} width
33887 * @param {Number} height
33888 * @return {Roo.BasicDialog} this
33890 resizeTo : function(width, height){
33891 this.el.setSize(width, height);
33892 this.size = {width: width, height: height};
33893 this.syncBodyHeight();
33894 if(this.fixedcenter){
33897 if(this.isVisible()){
33898 this.constrainXY();
33899 this.adjustAssets();
33901 this.fireEvent("resize", this, width, height);
33907 * Resizes the dialog to fit the specified content size.
33908 * @param {Number} width
33909 * @param {Number} height
33910 * @return {Roo.BasicDialog} this
33912 setContentSize : function(w, h){
33913 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33914 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33915 //if(!this.el.isBorderBox()){
33916 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33917 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33920 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33921 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33923 this.resizeTo(w, h);
33928 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
33929 * executed in response to a particular key being pressed while the dialog is active.
33930 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33931 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33932 * @param {Function} fn The function to call
33933 * @param {Object} scope (optional) The scope of the function
33934 * @return {Roo.BasicDialog} this
33936 addKeyListener : function(key, fn, scope){
33937 var keyCode, shift, ctrl, alt;
33938 if(typeof key == "object" && !(key instanceof Array)){
33939 keyCode = key["key"];
33940 shift = key["shift"];
33941 ctrl = key["ctrl"];
33946 var handler = function(dlg, e){
33947 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
33948 var k = e.getKey();
33949 if(keyCode instanceof Array){
33950 for(var i = 0, len = keyCode.length; i < len; i++){
33951 if(keyCode[i] == k){
33952 fn.call(scope || window, dlg, k, e);
33958 fn.call(scope || window, dlg, k, e);
33963 this.on("keydown", handler);
33968 * Returns the TabPanel component (creates it if it doesn't exist).
33969 * Note: If you wish to simply check for the existence of tabs without creating them,
33970 * check for a null 'tabs' property.
33971 * @return {Roo.TabPanel} The tabs component
33973 getTabs : function(){
33975 this.el.addClass("x-dlg-auto-tabs");
33976 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33977 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33983 * Adds a button to the footer section of the dialog.
33984 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33985 * object or a valid Roo.DomHelper element config
33986 * @param {Function} handler The function called when the button is clicked
33987 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33988 * @return {Roo.Button} The new button
33990 addButton : function(config, handler, scope){
33991 var dh = Roo.DomHelper;
33993 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33995 if(!this.btnContainer){
33996 var tb = this.footer.createChild({
33998 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33999 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34001 this.btnContainer = tb.firstChild.firstChild.firstChild;
34006 minWidth: this.minButtonWidth,
34009 if(typeof config == "string"){
34010 bconfig.text = config;
34013 bconfig.dhconfig = config;
34015 Roo.apply(bconfig, config);
34019 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34020 bconfig.position = Math.max(0, bconfig.position);
34021 fc = this.btnContainer.childNodes[bconfig.position];
34024 var btn = new Roo.Button(
34026 this.btnContainer.insertBefore(document.createElement("td"),fc)
34027 : this.btnContainer.appendChild(document.createElement("td")),
34028 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
34031 this.syncBodyHeight();
34034 * Array of all the buttons that have been added to this dialog via addButton
34039 this.buttons.push(btn);
34044 * Sets the default button to be focused when the dialog is displayed.
34045 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34046 * @return {Roo.BasicDialog} this
34048 setDefaultButton : function(btn){
34049 this.defaultButton = btn;
34054 getHeaderFooterHeight : function(safe){
34057 height += this.header.getHeight();
34060 var fm = this.footer.getMargins();
34061 height += (this.footer.getHeight()+fm.top+fm.bottom);
34063 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34064 height += this.centerBg.getPadding("tb");
34069 syncBodyHeight : function()
34071 var bd = this.body, // the text
34072 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34074 var height = this.size.height - this.getHeaderFooterHeight(false);
34075 bd.setHeight(height-bd.getMargins("tb"));
34076 var hh = this.header.getHeight();
34077 var h = this.size.height-hh;
34080 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34081 bw.setHeight(h-cb.getPadding("tb"));
34083 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34084 bd.setWidth(bw.getWidth(true));
34086 this.tabs.syncHeight();
34088 this.tabs.el.repaint();
34094 * Restores the previous state of the dialog if Roo.state is configured.
34095 * @return {Roo.BasicDialog} this
34097 restoreState : function(){
34098 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34099 if(box && box.width){
34100 this.xy = [box.x, box.y];
34101 this.resizeTo(box.width, box.height);
34107 beforeShow : function(){
34109 if(this.fixedcenter){
34110 this.xy = this.el.getCenterXY(true);
34113 Roo.get(document.body).addClass("x-body-masked");
34114 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34117 this.constrainXY();
34121 animShow : function(){
34122 var b = Roo.get(this.animateTarget).getBox();
34123 this.proxy.setSize(b.width, b.height);
34124 this.proxy.setLocation(b.x, b.y);
34126 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34127 true, .35, this.showEl.createDelegate(this));
34131 * Shows the dialog.
34132 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34133 * @return {Roo.BasicDialog} this
34135 show : function(animateTarget){
34136 if (this.fireEvent("beforeshow", this) === false){
34139 if(this.syncHeightBeforeShow){
34140 this.syncBodyHeight();
34141 }else if(this.firstShow){
34142 this.firstShow = false;
34143 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34145 this.animateTarget = animateTarget || this.animateTarget;
34146 if(!this.el.isVisible()){
34148 if(this.animateTarget && Roo.get(this.animateTarget)){
34158 showEl : function(){
34160 this.el.setXY(this.xy);
34162 this.adjustAssets(true);
34165 // IE peekaboo bug - fix found by Dave Fenwick
34169 this.fireEvent("show", this);
34173 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
34174 * dialog itself will receive focus.
34176 focus : function(){
34177 if(this.defaultButton){
34178 this.defaultButton.focus();
34180 this.focusEl.focus();
34185 constrainXY : function(){
34186 if(this.constraintoviewport !== false){
34187 if(!this.viewSize){
34188 if(this.container){
34189 var s = this.container.getSize();
34190 this.viewSize = [s.width, s.height];
34192 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34195 var s = Roo.get(this.container||document).getScroll();
34197 var x = this.xy[0], y = this.xy[1];
34198 var w = this.size.width, h = this.size.height;
34199 var vw = this.viewSize[0], vh = this.viewSize[1];
34200 // only move it if it needs it
34202 // first validate right/bottom
34203 if(x + w > vw+s.left){
34207 if(y + h > vh+s.top){
34211 // then make sure top/left isn't negative
34223 if(this.isVisible()){
34224 this.el.setLocation(x, y);
34225 this.adjustAssets();
34232 onDrag : function(){
34233 if(!this.proxyDrag){
34234 this.xy = this.el.getXY();
34235 this.adjustAssets();
34240 adjustAssets : function(doShow){
34241 var x = this.xy[0], y = this.xy[1];
34242 var w = this.size.width, h = this.size.height;
34243 if(doShow === true){
34245 this.shadow.show(this.el);
34251 if(this.shadow && this.shadow.isVisible()){
34252 this.shadow.show(this.el);
34254 if(this.shim && this.shim.isVisible()){
34255 this.shim.setBounds(x, y, w, h);
34260 adjustViewport : function(w, h){
34262 w = Roo.lib.Dom.getViewWidth();
34263 h = Roo.lib.Dom.getViewHeight();
34266 this.viewSize = [w, h];
34267 if(this.modal && this.mask.isVisible()){
34268 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34269 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34271 if(this.isVisible()){
34272 this.constrainXY();
34277 * Destroys this dialog and all its supporting elements (including any tabs, shim,
34278 * shadow, proxy, mask, etc.) Also removes all event listeners.
34279 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34281 destroy : function(removeEl){
34282 if(this.isVisible()){
34283 this.animateTarget = null;
34286 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34288 this.tabs.destroy(removeEl);
34301 for(var i = 0, len = this.buttons.length; i < len; i++){
34302 this.buttons[i].destroy();
34305 this.el.removeAllListeners();
34306 if(removeEl === true){
34307 this.el.update("");
34310 Roo.DialogManager.unregister(this);
34314 startMove : function(){
34315 if(this.proxyDrag){
34318 if(this.constraintoviewport !== false){
34319 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34324 endMove : function(){
34325 if(!this.proxyDrag){
34326 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34328 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34331 this.refreshSize();
34332 this.adjustAssets();
34334 this.fireEvent("move", this, this.xy[0], this.xy[1]);
34338 * Brings this dialog to the front of any other visible dialogs
34339 * @return {Roo.BasicDialog} this
34341 toFront : function(){
34342 Roo.DialogManager.bringToFront(this);
34347 * Sends this dialog to the back (under) of any other visible dialogs
34348 * @return {Roo.BasicDialog} this
34350 toBack : function(){
34351 Roo.DialogManager.sendToBack(this);
34356 * Centers this dialog in the viewport
34357 * @return {Roo.BasicDialog} this
34359 center : function(){
34360 var xy = this.el.getCenterXY(true);
34361 this.moveTo(xy[0], xy[1]);
34366 * Moves the dialog's top-left corner to the specified point
34367 * @param {Number} x
34368 * @param {Number} y
34369 * @return {Roo.BasicDialog} this
34371 moveTo : function(x, y){
34373 if(this.isVisible()){
34374 this.el.setXY(this.xy);
34375 this.adjustAssets();
34381 * Aligns the dialog to the specified element
34382 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34383 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34384 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34385 * @return {Roo.BasicDialog} this
34387 alignTo : function(element, position, offsets){
34388 this.xy = this.el.getAlignToXY(element, position, offsets);
34389 if(this.isVisible()){
34390 this.el.setXY(this.xy);
34391 this.adjustAssets();
34397 * Anchors an element to another element and realigns it when the window is resized.
34398 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34399 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34400 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34401 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34402 * is a number, it is used as the buffer delay (defaults to 50ms).
34403 * @return {Roo.BasicDialog} this
34405 anchorTo : function(el, alignment, offsets, monitorScroll){
34406 var action = function(){
34407 this.alignTo(el, alignment, offsets);
34409 Roo.EventManager.onWindowResize(action, this);
34410 var tm = typeof monitorScroll;
34411 if(tm != 'undefined'){
34412 Roo.EventManager.on(window, 'scroll', action, this,
34413 {buffer: tm == 'number' ? monitorScroll : 50});
34420 * Returns true if the dialog is visible
34421 * @return {Boolean}
34423 isVisible : function(){
34424 return this.el.isVisible();
34428 animHide : function(callback){
34429 var b = Roo.get(this.animateTarget).getBox();
34431 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34433 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34434 this.hideEl.createDelegate(this, [callback]));
34438 * Hides the dialog.
34439 * @param {Function} callback (optional) Function to call when the dialog is hidden
34440 * @return {Roo.BasicDialog} this
34442 hide : function(callback){
34443 if (this.fireEvent("beforehide", this) === false){
34447 this.shadow.hide();
34452 // sometimes animateTarget seems to get set.. causing problems...
34453 // this just double checks..
34454 if(this.animateTarget && Roo.get(this.animateTarget)) {
34455 this.animHide(callback);
34458 this.hideEl(callback);
34464 hideEl : function(callback){
34468 Roo.get(document.body).removeClass("x-body-masked");
34470 this.fireEvent("hide", this);
34471 if(typeof callback == "function"){
34477 hideAction : function(){
34478 this.setLeft("-10000px");
34479 this.setTop("-10000px");
34480 this.setStyle("visibility", "hidden");
34484 refreshSize : function(){
34485 this.size = this.el.getSize();
34486 this.xy = this.el.getXY();
34487 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34491 // z-index is managed by the DialogManager and may be overwritten at any time
34492 setZIndex : function(index){
34494 this.mask.setStyle("z-index", index);
34497 this.shim.setStyle("z-index", ++index);
34500 this.shadow.setZIndex(++index);
34502 this.el.setStyle("z-index", ++index);
34504 this.proxy.setStyle("z-index", ++index);
34507 this.resizer.proxy.setStyle("z-index", ++index);
34510 this.lastZIndex = index;
34514 * Returns the element for this dialog
34515 * @return {Roo.Element} The underlying dialog Element
34517 getEl : function(){
34523 * @class Roo.DialogManager
34524 * Provides global access to BasicDialogs that have been created and
34525 * support for z-indexing (layering) multiple open dialogs.
34527 Roo.DialogManager = function(){
34529 var accessList = [];
34533 var sortDialogs = function(d1, d2){
34534 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34538 var orderDialogs = function(){
34539 accessList.sort(sortDialogs);
34540 var seed = Roo.DialogManager.zseed;
34541 for(var i = 0, len = accessList.length; i < len; i++){
34542 var dlg = accessList[i];
34544 dlg.setZIndex(seed + (i*10));
34551 * The starting z-index for BasicDialogs (defaults to 9000)
34552 * @type Number The z-index value
34557 register : function(dlg){
34558 list[dlg.id] = dlg;
34559 accessList.push(dlg);
34563 unregister : function(dlg){
34564 delete list[dlg.id];
34567 if(!accessList.indexOf){
34568 for( i = 0, len = accessList.length; i < len; i++){
34569 if(accessList[i] == dlg){
34570 accessList.splice(i, 1);
34575 i = accessList.indexOf(dlg);
34577 accessList.splice(i, 1);
34583 * Gets a registered dialog by id
34584 * @param {String/Object} id The id of the dialog or a dialog
34585 * @return {Roo.BasicDialog} this
34587 get : function(id){
34588 return typeof id == "object" ? id : list[id];
34592 * Brings the specified dialog to the front
34593 * @param {String/Object} dlg The id of the dialog or a dialog
34594 * @return {Roo.BasicDialog} this
34596 bringToFront : function(dlg){
34597 dlg = this.get(dlg);
34600 dlg._lastAccess = new Date().getTime();
34607 * Sends the specified dialog to the back
34608 * @param {String/Object} dlg The id of the dialog or a dialog
34609 * @return {Roo.BasicDialog} this
34611 sendToBack : function(dlg){
34612 dlg = this.get(dlg);
34613 dlg._lastAccess = -(new Date().getTime());
34619 * Hides all dialogs
34621 hideAll : function(){
34622 for(var id in list){
34623 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34632 * @class Roo.LayoutDialog
34633 * @extends Roo.BasicDialog
34634 * @children Roo.ContentPanel
34635 * @parent builder none
34636 * Dialog which provides adjustments for working with a layout in a Dialog.
34637 * Add your necessary layout config options to the dialog's config.<br>
34638 * Example usage (including a nested layout):
34641 dialog = new Roo.LayoutDialog("download-dlg", {
34650 // layout config merges with the dialog config
34652 tabPosition: "top",
34653 alwaysShowTabs: true
34656 dialog.addKeyListener(27, dialog.hide, dialog);
34657 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34658 dialog.addButton("Build It!", this.getDownload, this);
34660 // we can even add nested layouts
34661 var innerLayout = new Roo.BorderLayout("dl-inner", {
34671 innerLayout.beginUpdate();
34672 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34673 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34674 innerLayout.endUpdate(true);
34676 var layout = dialog.getLayout();
34677 layout.beginUpdate();
34678 layout.add("center", new Roo.ContentPanel("standard-panel",
34679 {title: "Download the Source", fitToFrame:true}));
34680 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34681 {title: "Build your own roo.js"}));
34682 layout.getRegion("center").showPanel(sp);
34683 layout.endUpdate();
34687 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34688 * @param {Object} config configuration options
34690 Roo.LayoutDialog = function(el, cfg){
34693 if (typeof(cfg) == 'undefined') {
34694 config = Roo.apply({}, el);
34695 // not sure why we use documentElement here.. - it should always be body.
34696 // IE7 borks horribly if we use documentElement.
34697 // webkit also does not like documentElement - it creates a body element...
34698 el = Roo.get( document.body || document.documentElement ).createChild();
34699 //config.autoCreate = true;
34703 config.autoTabs = false;
34704 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34705 this.body.setStyle({overflow:"hidden", position:"relative"});
34706 this.layout = new Roo.BorderLayout(this.body.dom, config);
34707 this.layout.monitorWindowResize = false;
34708 this.el.addClass("x-dlg-auto-layout");
34709 // fix case when center region overwrites center function
34710 this.center = Roo.BasicDialog.prototype.center;
34711 this.on("show", this.layout.layout, this.layout, true);
34712 if (config.items) {
34713 var xitems = config.items;
34714 delete config.items;
34715 Roo.each(xitems, this.addxtype, this);
34720 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34724 * @cfg {Roo.LayoutRegion} east
34727 * @cfg {Roo.LayoutRegion} west
34730 * @cfg {Roo.LayoutRegion} south
34733 * @cfg {Roo.LayoutRegion} north
34736 * @cfg {Roo.LayoutRegion} center
34739 * @cfg {Roo.Button} buttons[] Bottom buttons..
34744 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34747 endUpdate : function(){
34748 this.layout.endUpdate();
34752 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34755 beginUpdate : function(){
34756 this.layout.beginUpdate();
34760 * Get the BorderLayout for this dialog
34761 * @return {Roo.BorderLayout}
34763 getLayout : function(){
34764 return this.layout;
34767 showEl : function(){
34768 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34770 this.layout.layout();
34775 // Use the syncHeightBeforeShow config option to control this automatically
34776 syncBodyHeight : function(){
34777 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34778 if(this.layout){this.layout.layout();}
34782 * Add an xtype element (actually adds to the layout.)
34783 * @return {Object} xdata xtype object data.
34786 addxtype : function(c) {
34787 return this.layout.addxtype(c);
34791 * Ext JS Library 1.1.1
34792 * Copyright(c) 2006-2007, Ext JS, LLC.
34794 * Originally Released Under LGPL - original licence link has changed is not relivant.
34797 * <script type="text/javascript">
34801 * @class Roo.MessageBox
34803 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
34807 Roo.Msg.alert('Status', 'Changes saved successfully.');
34809 // Prompt for user data:
34810 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34812 // process text value...
34816 // Show a dialog using config options:
34818 title:'Save Changes?',
34819 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34820 buttons: Roo.Msg.YESNOCANCEL,
34827 Roo.MessageBox = function(){
34828 var dlg, opt, mask, waitTimer;
34829 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34830 var buttons, activeTextEl, bwidth;
34833 var handleButton = function(button){
34835 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34839 var handleHide = function(){
34840 if(opt && opt.cls){
34841 dlg.el.removeClass(opt.cls);
34844 Roo.TaskMgr.stop(waitTimer);
34850 var updateButtons = function(b){
34853 buttons["ok"].hide();
34854 buttons["cancel"].hide();
34855 buttons["yes"].hide();
34856 buttons["no"].hide();
34857 dlg.footer.dom.style.display = 'none';
34860 dlg.footer.dom.style.display = '';
34861 for(var k in buttons){
34862 if(typeof buttons[k] != "function"){
34865 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34866 width += buttons[k].el.getWidth()+15;
34876 var handleEsc = function(d, k, e){
34877 if(opt && opt.closable !== false){
34887 * Returns a reference to the underlying {@link Roo.BasicDialog} element
34888 * @return {Roo.BasicDialog} The BasicDialog element
34890 getDialog : function(){
34892 dlg = new Roo.BasicDialog("x-msg-box", {
34897 constraintoviewport:false,
34899 collapsible : false,
34902 width:400, height:100,
34903 buttonAlign:"center",
34904 closeClick : function(){
34905 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34906 handleButton("no");
34908 handleButton("cancel");
34913 dlg.on("hide", handleHide);
34915 dlg.addKeyListener(27, handleEsc);
34917 var bt = this.buttonText;
34918 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34919 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34920 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34921 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34922 bodyEl = dlg.body.createChild({
34924 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>'
34926 msgEl = bodyEl.dom.firstChild;
34927 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34928 textboxEl.enableDisplayMode();
34929 textboxEl.addKeyListener([10,13], function(){
34930 if(dlg.isVisible() && opt && opt.buttons){
34931 if(opt.buttons.ok){
34932 handleButton("ok");
34933 }else if(opt.buttons.yes){
34934 handleButton("yes");
34938 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34939 textareaEl.enableDisplayMode();
34940 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34941 progressEl.enableDisplayMode();
34942 var pf = progressEl.dom.firstChild;
34944 pp = Roo.get(pf.firstChild);
34945 pp.setHeight(pf.offsetHeight);
34953 * Updates the message box body text
34954 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34955 * the XHTML-compliant non-breaking space character '&#160;')
34956 * @return {Roo.MessageBox} This message box
34958 updateText : function(text){
34959 if(!dlg.isVisible() && !opt.width){
34960 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34962 msgEl.innerHTML = text || ' ';
34964 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34965 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34967 Math.min(opt.width || cw , this.maxWidth),
34968 Math.max(opt.minWidth || this.minWidth, bwidth)
34971 activeTextEl.setWidth(w);
34973 if(dlg.isVisible()){
34974 dlg.fixedcenter = false;
34976 // to big, make it scroll. = But as usual stupid IE does not support
34979 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34980 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34981 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34983 bodyEl.dom.style.height = '';
34984 bodyEl.dom.style.overflowY = '';
34987 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34989 bodyEl.dom.style.overflowX = '';
34992 dlg.setContentSize(w, bodyEl.getHeight());
34993 if(dlg.isVisible()){
34994 dlg.fixedcenter = true;
35000 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
35001 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35002 * @param {Number} value Any number between 0 and 1 (e.g., .5)
35003 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35004 * @return {Roo.MessageBox} This message box
35006 updateProgress : function(value, text){
35008 this.updateText(text);
35010 if (pp) { // weird bug on my firefox - for some reason this is not defined
35011 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35017 * Returns true if the message box is currently displayed
35018 * @return {Boolean} True if the message box is visible, else false
35020 isVisible : function(){
35021 return dlg && dlg.isVisible();
35025 * Hides the message box if it is displayed
35028 if(this.isVisible()){
35034 * Displays a new message box, or reinitializes an existing message box, based on the config options
35035 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35036 * The following config object properties are supported:
35038 Property Type Description
35039 ---------- --------------- ------------------------------------------------------------------------------------
35040 animEl String/Element An id or Element from which the message box should animate as it opens and
35041 closes (defaults to undefined)
35042 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35043 cancel:'Bar'}), or false to not show any buttons (defaults to false)
35044 closable Boolean False to hide the top-right close button (defaults to true). Note that
35045 progress and wait dialogs will ignore this property and always hide the
35046 close button as they can only be closed programmatically.
35047 cls String A custom CSS class to apply to the message box element
35048 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
35049 displayed (defaults to 75)
35050 fn Function A callback function to execute after closing the dialog. The arguments to the
35051 function will be btn (the name of the button that was clicked, if applicable,
35052 e.g. "ok"), and text (the value of the active text field, if applicable).
35053 Progress and wait dialogs will ignore this option since they do not respond to
35054 user actions and can only be closed programmatically, so any required function
35055 should be called by the same code after it closes the dialog.
35056 icon String A CSS class that provides a background image to be used as an icon for
35057 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35058 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
35059 minWidth Number The minimum width in pixels of the message box (defaults to 100)
35060 modal Boolean False to allow user interaction with the page while the message box is
35061 displayed (defaults to true)
35062 msg String A string that will replace the existing message box body text (defaults
35063 to the XHTML-compliant non-breaking space character ' ')
35064 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
35065 progress Boolean True to display a progress bar (defaults to false)
35066 progressText String The text to display inside the progress bar if progress = true (defaults to '')
35067 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
35068 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
35069 title String The title text
35070 value String The string value to set into the active textbox element if displayed
35071 wait Boolean True to display a progress bar (defaults to false)
35072 width Number The width of the dialog in pixels
35079 msg: 'Please enter your address:',
35081 buttons: Roo.MessageBox.OKCANCEL,
35084 animEl: 'addAddressBtn'
35087 * @param {Object} config Configuration options
35088 * @return {Roo.MessageBox} This message box
35090 show : function(options)
35093 // this causes nightmares if you show one dialog after another
35094 // especially on callbacks..
35096 if(this.isVisible()){
35099 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35100 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
35101 Roo.log("New Dialog Message:" + options.msg )
35102 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35103 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35106 var d = this.getDialog();
35108 d.setTitle(opt.title || " ");
35109 d.close.setDisplayed(opt.closable !== false);
35110 activeTextEl = textboxEl;
35111 opt.prompt = opt.prompt || (opt.multiline ? true : false);
35116 textareaEl.setHeight(typeof opt.multiline == "number" ?
35117 opt.multiline : this.defaultTextHeight);
35118 activeTextEl = textareaEl;
35127 progressEl.setDisplayed(opt.progress === true);
35128 this.updateProgress(0);
35129 activeTextEl.dom.value = opt.value || "";
35131 dlg.setDefaultButton(activeTextEl);
35133 var bs = opt.buttons;
35136 db = buttons["ok"];
35137 }else if(bs && bs.yes){
35138 db = buttons["yes"];
35140 dlg.setDefaultButton(db);
35142 bwidth = updateButtons(opt.buttons);
35143 this.updateText(opt.msg);
35145 d.el.addClass(opt.cls);
35147 d.proxyDrag = opt.proxyDrag === true;
35148 d.modal = opt.modal !== false;
35149 d.mask = opt.modal !== false ? mask : false;
35150 if(!d.isVisible()){
35151 // force it to the end of the z-index stack so it gets a cursor in FF
35152 document.body.appendChild(dlg.el.dom);
35153 d.animateTarget = null;
35154 d.show(options.animEl);
35161 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
35162 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35163 * and closing the message box when the process is complete.
35164 * @param {String} title The title bar text
35165 * @param {String} msg The message box body text
35166 * @return {Roo.MessageBox} This message box
35168 progress : function(title, msg){
35175 minWidth: this.minProgressWidth,
35182 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35183 * If a callback function is passed it will be called after the user clicks the button, and the
35184 * id of the button that was clicked will be passed as the only parameter to the callback
35185 * (could also be the top-right close button).
35186 * @param {String} title The title bar text
35187 * @param {String} msg The message box body text
35188 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35189 * @param {Object} scope (optional) The scope of the callback function
35190 * @return {Roo.MessageBox} This message box
35192 alert : function(title, msg, fn, scope){
35205 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
35206 * interaction while waiting for a long-running process to complete that does not have defined intervals.
35207 * You are responsible for closing the message box when the process is complete.
35208 * @param {String} msg The message box body text
35209 * @param {String} title (optional) The title bar text
35210 * @return {Roo.MessageBox} This message box
35212 wait : function(msg, title){
35223 waitTimer = Roo.TaskMgr.start({
35225 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35233 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35234 * If a callback function is passed it will be called after the user clicks either button, and the id of the
35235 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35236 * @param {String} title The title bar text
35237 * @param {String} msg The message box body text
35238 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35239 * @param {Object} scope (optional) The scope of the callback function
35240 * @return {Roo.MessageBox} This message box
35242 confirm : function(title, msg, fn, scope){
35246 buttons: this.YESNO,
35255 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35256 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
35257 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35258 * (could also be the top-right close button) and the text that was entered will be passed as the two
35259 * parameters to the callback.
35260 * @param {String} title The title bar text
35261 * @param {String} msg The message box body text
35262 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35263 * @param {Object} scope (optional) The scope of the callback function
35264 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35265 * property, or the height in pixels to create the textbox (defaults to false / single-line)
35266 * @return {Roo.MessageBox} This message box
35268 prompt : function(title, msg, fn, scope, multiline){
35272 buttons: this.OKCANCEL,
35277 multiline: multiline,
35284 * Button config that displays a single OK button
35289 * Button config that displays Yes and No buttons
35292 YESNO : {yes:true, no:true},
35294 * Button config that displays OK and Cancel buttons
35297 OKCANCEL : {ok:true, cancel:true},
35299 * Button config that displays Yes, No and Cancel buttons
35302 YESNOCANCEL : {yes:true, no:true, cancel:true},
35305 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35308 defaultTextHeight : 75,
35310 * The maximum width in pixels of the message box (defaults to 600)
35315 * The minimum width in pixels of the message box (defaults to 100)
35320 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
35321 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35324 minProgressWidth : 250,
35326 * An object containing the default button text strings that can be overriden for localized language support.
35327 * Supported properties are: ok, cancel, yes and no.
35328 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35341 * Shorthand for {@link Roo.MessageBox}
35343 Roo.Msg = Roo.MessageBox;/*
35345 * Ext JS Library 1.1.1
35346 * Copyright(c) 2006-2007, Ext JS, LLC.
35348 * Originally Released Under LGPL - original licence link has changed is not relivant.
35351 * <script type="text/javascript">
35354 * @class Roo.QuickTips
35355 * Provides attractive and customizable tooltips for any element.
35358 Roo.QuickTips = function(){
35359 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35360 var ce, bd, xy, dd;
35361 var visible = false, disabled = true, inited = false;
35362 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35364 var onOver = function(e){
35368 var t = e.getTarget();
35369 if(!t || t.nodeType !== 1 || t == document || t == document.body){
35372 if(ce && t == ce.el){
35373 clearTimeout(hideProc);
35376 if(t && tagEls[t.id]){
35377 tagEls[t.id].el = t;
35378 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35381 var ttp, et = Roo.fly(t);
35382 var ns = cfg.namespace;
35383 if(tm.interceptTitles && t.title){
35386 t.removeAttribute("title");
35387 e.preventDefault();
35389 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35392 showProc = show.defer(tm.showDelay, tm, [{
35394 text: ttp.replace(/\\n/g,'<br/>'),
35395 width: et.getAttributeNS(ns, cfg.width),
35396 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35397 title: et.getAttributeNS(ns, cfg.title),
35398 cls: et.getAttributeNS(ns, cfg.cls)
35403 var onOut = function(e){
35404 clearTimeout(showProc);
35405 var t = e.getTarget();
35406 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35407 hideProc = setTimeout(hide, tm.hideDelay);
35411 var onMove = function(e){
35417 if(tm.trackMouse && ce){
35422 var onDown = function(e){
35423 clearTimeout(showProc);
35424 clearTimeout(hideProc);
35426 if(tm.hideOnClick){
35429 tm.enable.defer(100, tm);
35434 var getPad = function(){
35435 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35438 var show = function(o){
35442 clearTimeout(dismissProc);
35444 if(removeCls){ // in case manually hidden
35445 el.removeClass(removeCls);
35449 el.addClass(ce.cls);
35450 removeCls = ce.cls;
35453 tipTitle.update(ce.title);
35456 tipTitle.update('');
35459 el.dom.style.width = tm.maxWidth+'px';
35460 //tipBody.dom.style.width = '';
35461 tipBodyText.update(o.text);
35462 var p = getPad(), w = ce.width;
35464 var td = tipBodyText.dom;
35465 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35466 if(aw > tm.maxWidth){
35468 }else if(aw < tm.minWidth){
35474 //tipBody.setWidth(w);
35475 el.setWidth(parseInt(w, 10) + p);
35476 if(ce.autoHide === false){
35477 close.setDisplayed(true);
35482 close.setDisplayed(false);
35488 el.avoidY = xy[1]-18;
35493 el.setStyle("visibility", "visible");
35494 el.fadeIn({callback: afterShow});
35500 var afterShow = function(){
35504 if(tm.autoDismiss && ce.autoHide !== false){
35505 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35510 var hide = function(noanim){
35511 clearTimeout(dismissProc);
35512 clearTimeout(hideProc);
35514 if(el.isVisible()){
35516 if(noanim !== true && tm.animate){
35517 el.fadeOut({callback: afterHide});
35524 var afterHide = function(){
35527 el.removeClass(removeCls);
35534 * @cfg {Number} minWidth
35535 * The minimum width of the quick tip (defaults to 40)
35539 * @cfg {Number} maxWidth
35540 * The maximum width of the quick tip (defaults to 300)
35544 * @cfg {Boolean} interceptTitles
35545 * True to automatically use the element's DOM title value if available (defaults to false)
35547 interceptTitles : false,
35549 * @cfg {Boolean} trackMouse
35550 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35552 trackMouse : false,
35554 * @cfg {Boolean} hideOnClick
35555 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35557 hideOnClick : true,
35559 * @cfg {Number} showDelay
35560 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35564 * @cfg {Number} hideDelay
35565 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35569 * @cfg {Boolean} autoHide
35570 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35571 * Used in conjunction with hideDelay.
35576 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35577 * (defaults to true). Used in conjunction with autoDismissDelay.
35579 autoDismiss : true,
35582 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35584 autoDismissDelay : 5000,
35586 * @cfg {Boolean} animate
35587 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35592 * @cfg {String} title
35593 * Title text to display (defaults to ''). This can be any valid HTML markup.
35597 * @cfg {String} text
35598 * Body text to display (defaults to ''). This can be any valid HTML markup.
35602 * @cfg {String} cls
35603 * A CSS class to apply to the base quick tip element (defaults to '').
35607 * @cfg {Number} width
35608 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
35609 * minWidth or maxWidth.
35614 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
35615 * or display QuickTips in a page.
35618 tm = Roo.QuickTips;
35619 cfg = tm.tagConfig;
35621 if(!Roo.isReady){ // allow calling of init() before onReady
35622 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35625 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35626 el.fxDefaults = {stopFx: true};
35627 // maximum custom styling
35628 //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>');
35629 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>');
35630 tipTitle = el.child('h3');
35631 tipTitle.enableDisplayMode("block");
35632 tipBody = el.child('div.x-tip-bd');
35633 tipBodyText = el.child('div.x-tip-bd-inner');
35634 //bdLeft = el.child('div.x-tip-bd-left');
35635 //bdRight = el.child('div.x-tip-bd-right');
35636 close = el.child('div.x-tip-close');
35637 close.enableDisplayMode("block");
35638 close.on("click", hide);
35639 var d = Roo.get(document);
35640 d.on("mousedown", onDown);
35641 d.on("mouseover", onOver);
35642 d.on("mouseout", onOut);
35643 d.on("mousemove", onMove);
35644 esc = d.addKeyListener(27, hide);
35647 dd = el.initDD("default", null, {
35648 onDrag : function(){
35652 dd.setHandleElId(tipTitle.id);
35661 * Configures a new quick tip instance and assigns it to a target element. The following config options
35664 Property Type Description
35665 ---------- --------------------- ------------------------------------------------------------------------
35666 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
35668 * @param {Object} config The config object
35670 register : function(config){
35671 var cs = config instanceof Array ? config : arguments;
35672 for(var i = 0, len = cs.length; i < len; i++) {
35674 var target = c.target;
35676 if(target instanceof Array){
35677 for(var j = 0, jlen = target.length; j < jlen; j++){
35678 tagEls[target[j]] = c;
35681 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35688 * Removes this quick tip from its element and destroys it.
35689 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35691 unregister : function(el){
35692 delete tagEls[Roo.id(el)];
35696 * Enable this quick tip.
35698 enable : function(){
35699 if(inited && disabled){
35701 if(locks.length < 1){
35708 * Disable this quick tip.
35710 disable : function(){
35712 clearTimeout(showProc);
35713 clearTimeout(hideProc);
35714 clearTimeout(dismissProc);
35722 * Returns true if the quick tip is enabled, else false.
35724 isEnabled : function(){
35730 namespace : "roo", // was ext?? this may break..
35731 alt_namespace : "ext",
35732 attribute : "qtip",
35742 // backwards compat
35743 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35745 * Ext JS Library 1.1.1
35746 * Copyright(c) 2006-2007, Ext JS, LLC.
35748 * Originally Released Under LGPL - original licence link has changed is not relivant.
35751 * <script type="text/javascript">
35756 * @class Roo.tree.TreePanel
35757 * @extends Roo.data.Tree
35758 * @cfg {Roo.tree.TreeNode} root The root node
35759 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35760 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35761 * @cfg {Boolean} enableDD true to enable drag and drop
35762 * @cfg {Boolean} enableDrag true to enable just drag
35763 * @cfg {Boolean} enableDrop true to enable just drop
35764 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35765 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35766 * @cfg {String} ddGroup The DD group this TreePanel belongs to
35767 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35768 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35769 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35770 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35771 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35772 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35773 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35774 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35775 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35776 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35777 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35778 * @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>
35779 * @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>
35782 * @param {String/HTMLElement/Element} el The container element
35783 * @param {Object} config
35785 Roo.tree.TreePanel = function(el, config){
35787 var loader = false;
35789 root = config.root;
35790 delete config.root;
35792 if (config.loader) {
35793 loader = config.loader;
35794 delete config.loader;
35797 Roo.apply(this, config);
35798 Roo.tree.TreePanel.superclass.constructor.call(this);
35799 this.el = Roo.get(el);
35800 this.el.addClass('x-tree');
35801 //console.log(root);
35803 this.setRootNode( Roo.factory(root, Roo.tree));
35806 this.loader = Roo.factory(loader, Roo.tree);
35809 * Read-only. The id of the container element becomes this TreePanel's id.
35811 this.id = this.el.id;
35814 * @event beforeload
35815 * Fires before a node is loaded, return false to cancel
35816 * @param {Node} node The node being loaded
35818 "beforeload" : true,
35821 * Fires when a node is loaded
35822 * @param {Node} node The node that was loaded
35826 * @event textchange
35827 * Fires when the text for a node is changed
35828 * @param {Node} node The node
35829 * @param {String} text The new text
35830 * @param {String} oldText The old text
35832 "textchange" : true,
35834 * @event beforeexpand
35835 * Fires before a node is expanded, return false to cancel.
35836 * @param {Node} node The node
35837 * @param {Boolean} deep
35838 * @param {Boolean} anim
35840 "beforeexpand" : true,
35842 * @event beforecollapse
35843 * Fires before a node is collapsed, return false to cancel.
35844 * @param {Node} node The node
35845 * @param {Boolean} deep
35846 * @param {Boolean} anim
35848 "beforecollapse" : true,
35851 * Fires when a node is expanded
35852 * @param {Node} node The node
35856 * @event disabledchange
35857 * Fires when the disabled status of a node changes
35858 * @param {Node} node The node
35859 * @param {Boolean} disabled
35861 "disabledchange" : true,
35864 * Fires when a node is collapsed
35865 * @param {Node} node The node
35869 * @event beforeclick
35870 * Fires before click processing on a node. Return false to cancel the default action.
35871 * @param {Node} node The node
35872 * @param {Roo.EventObject} e The event object
35874 "beforeclick":true,
35876 * @event checkchange
35877 * Fires when a node with a checkbox's checked property changes
35878 * @param {Node} this This node
35879 * @param {Boolean} checked
35881 "checkchange":true,
35884 * Fires when a node is clicked
35885 * @param {Node} node The node
35886 * @param {Roo.EventObject} e The event object
35891 * Fires when a node is double clicked
35892 * @param {Node} node The node
35893 * @param {Roo.EventObject} e The event object
35897 * @event contextmenu
35898 * Fires when a node is right clicked
35899 * @param {Node} node The node
35900 * @param {Roo.EventObject} e The event object
35902 "contextmenu":true,
35904 * @event beforechildrenrendered
35905 * Fires right before the child nodes for a node are rendered
35906 * @param {Node} node The node
35908 "beforechildrenrendered":true,
35911 * Fires when a node starts being dragged
35912 * @param {Roo.tree.TreePanel} this
35913 * @param {Roo.tree.TreeNode} node
35914 * @param {event} e The raw browser event
35916 "startdrag" : true,
35919 * Fires when a drag operation is complete
35920 * @param {Roo.tree.TreePanel} this
35921 * @param {Roo.tree.TreeNode} node
35922 * @param {event} e The raw browser event
35927 * Fires when a dragged node is dropped on a valid DD target
35928 * @param {Roo.tree.TreePanel} this
35929 * @param {Roo.tree.TreeNode} node
35930 * @param {DD} dd The dd it was dropped on
35931 * @param {event} e The raw browser event
35935 * @event beforenodedrop
35936 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35937 * passed to handlers has the following properties:<br />
35938 * <ul style="padding:5px;padding-left:16px;">
35939 * <li>tree - The TreePanel</li>
35940 * <li>target - The node being targeted for the drop</li>
35941 * <li>data - The drag data from the drag source</li>
35942 * <li>point - The point of the drop - append, above or below</li>
35943 * <li>source - The drag source</li>
35944 * <li>rawEvent - Raw mouse event</li>
35945 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35946 * to be inserted by setting them on this object.</li>
35947 * <li>cancel - Set this to true to cancel the drop.</li>
35949 * @param {Object} dropEvent
35951 "beforenodedrop" : true,
35954 * Fires after a DD object is dropped on a node in this tree. The dropEvent
35955 * passed to handlers has the following properties:<br />
35956 * <ul style="padding:5px;padding-left:16px;">
35957 * <li>tree - The TreePanel</li>
35958 * <li>target - The node being targeted for the drop</li>
35959 * <li>data - The drag data from the drag source</li>
35960 * <li>point - The point of the drop - append, above or below</li>
35961 * <li>source - The drag source</li>
35962 * <li>rawEvent - Raw mouse event</li>
35963 * <li>dropNode - Dropped node(s).</li>
35965 * @param {Object} dropEvent
35969 * @event nodedragover
35970 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35971 * passed to handlers has the following properties:<br />
35972 * <ul style="padding:5px;padding-left:16px;">
35973 * <li>tree - The TreePanel</li>
35974 * <li>target - The node being targeted for the drop</li>
35975 * <li>data - The drag data from the drag source</li>
35976 * <li>point - The point of the drop - append, above or below</li>
35977 * <li>source - The drag source</li>
35978 * <li>rawEvent - Raw mouse event</li>
35979 * <li>dropNode - Drop node(s) provided by the source.</li>
35980 * <li>cancel - Set this to true to signal drop not allowed.</li>
35982 * @param {Object} dragOverEvent
35984 "nodedragover" : true,
35986 * @event appendnode
35987 * Fires when append node to the tree
35988 * @param {Roo.tree.TreePanel} this
35989 * @param {Roo.tree.TreeNode} node
35990 * @param {Number} index The index of the newly appended node
35992 "appendnode" : true
35995 if(this.singleExpand){
35996 this.on("beforeexpand", this.restrictExpand, this);
35999 this.editor.tree = this;
36000 this.editor = Roo.factory(this.editor, Roo.tree);
36003 if (this.selModel) {
36004 this.selModel = Roo.factory(this.selModel, Roo.tree);
36008 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36009 rootVisible : true,
36010 animate: Roo.enableFx,
36013 hlDrop : Roo.enableFx,
36017 rendererTip: false,
36019 restrictExpand : function(node){
36020 var p = node.parentNode;
36022 if(p.expandedChild && p.expandedChild.parentNode == p){
36023 p.expandedChild.collapse();
36025 p.expandedChild = node;
36029 // private override
36030 setRootNode : function(node){
36031 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36032 if(!this.rootVisible){
36033 node.ui = new Roo.tree.RootTreeNodeUI(node);
36039 * Returns the container element for this TreePanel
36041 getEl : function(){
36046 * Returns the default TreeLoader for this TreePanel
36048 getLoader : function(){
36049 return this.loader;
36055 expandAll : function(){
36056 this.root.expand(true);
36060 * Collapse all nodes
36062 collapseAll : function(){
36063 this.root.collapse(true);
36067 * Returns the selection model used by this TreePanel
36069 getSelectionModel : function(){
36070 if(!this.selModel){
36071 this.selModel = new Roo.tree.DefaultSelectionModel();
36073 return this.selModel;
36077 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36078 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36079 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36082 getChecked : function(a, startNode){
36083 startNode = startNode || this.root;
36085 var f = function(){
36086 if(this.attributes.checked){
36087 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36090 startNode.cascade(f);
36095 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36096 * @param {String} path
36097 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36098 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36099 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36101 expandPath : function(path, attr, callback){
36102 attr = attr || "id";
36103 var keys = path.split(this.pathSeparator);
36104 var curNode = this.root;
36105 if(curNode.attributes[attr] != keys[1]){ // invalid root
36107 callback(false, null);
36112 var f = function(){
36113 if(++index == keys.length){
36115 callback(true, curNode);
36119 var c = curNode.findChild(attr, keys[index]);
36122 callback(false, curNode);
36127 c.expand(false, false, f);
36129 curNode.expand(false, false, f);
36133 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36134 * @param {String} path
36135 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36136 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36137 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36139 selectPath : function(path, attr, callback){
36140 attr = attr || "id";
36141 var keys = path.split(this.pathSeparator);
36142 var v = keys.pop();
36143 if(keys.length > 0){
36144 var f = function(success, node){
36145 if(success && node){
36146 var n = node.findChild(attr, v);
36152 }else if(callback){
36153 callback(false, n);
36157 callback(false, n);
36161 this.expandPath(keys.join(this.pathSeparator), attr, f);
36163 this.root.select();
36165 callback(true, this.root);
36170 getTreeEl : function(){
36175 * Trigger rendering of this TreePanel
36177 render : function(){
36178 if (this.innerCt) {
36179 return this; // stop it rendering more than once!!
36182 this.innerCt = this.el.createChild({tag:"ul",
36183 cls:"x-tree-root-ct " +
36184 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36186 if(this.containerScroll){
36187 Roo.dd.ScrollManager.register(this.el);
36189 if((this.enableDD || this.enableDrop) && !this.dropZone){
36191 * The dropZone used by this tree if drop is enabled
36192 * @type Roo.tree.TreeDropZone
36194 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36195 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36198 if((this.enableDD || this.enableDrag) && !this.dragZone){
36200 * The dragZone used by this tree if drag is enabled
36201 * @type Roo.tree.TreeDragZone
36203 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36204 ddGroup: this.ddGroup || "TreeDD",
36205 scroll: this.ddScroll
36208 this.getSelectionModel().init(this);
36210 Roo.log("ROOT not set in tree");
36213 this.root.render();
36214 if(!this.rootVisible){
36215 this.root.renderChildren();
36221 * Ext JS Library 1.1.1
36222 * Copyright(c) 2006-2007, Ext JS, LLC.
36224 * Originally Released Under LGPL - original licence link has changed is not relivant.
36227 * <script type="text/javascript">
36232 * @class Roo.tree.DefaultSelectionModel
36233 * @extends Roo.util.Observable
36234 * The default single selection for a TreePanel.
36235 * @param {Object} cfg Configuration
36237 Roo.tree.DefaultSelectionModel = function(cfg){
36238 this.selNode = null;
36244 * @event selectionchange
36245 * Fires when the selected node changes
36246 * @param {DefaultSelectionModel} this
36247 * @param {TreeNode} node the new selection
36249 "selectionchange" : true,
36252 * @event beforeselect
36253 * Fires before the selected node changes, return false to cancel the change
36254 * @param {DefaultSelectionModel} this
36255 * @param {TreeNode} node the new selection
36256 * @param {TreeNode} node the old selection
36258 "beforeselect" : true
36261 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36264 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36265 init : function(tree){
36267 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36268 tree.on("click", this.onNodeClick, this);
36271 onNodeClick : function(node, e){
36272 if (e.ctrlKey && this.selNode == node) {
36273 this.unselect(node);
36281 * @param {TreeNode} node The node to select
36282 * @return {TreeNode} The selected node
36284 select : function(node){
36285 var last = this.selNode;
36286 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36288 last.ui.onSelectedChange(false);
36290 this.selNode = node;
36291 node.ui.onSelectedChange(true);
36292 this.fireEvent("selectionchange", this, node, last);
36299 * @param {TreeNode} node The node to unselect
36301 unselect : function(node){
36302 if(this.selNode == node){
36303 this.clearSelections();
36308 * Clear all selections
36310 clearSelections : function(){
36311 var n = this.selNode;
36313 n.ui.onSelectedChange(false);
36314 this.selNode = null;
36315 this.fireEvent("selectionchange", this, null);
36321 * Get the selected node
36322 * @return {TreeNode} The selected node
36324 getSelectedNode : function(){
36325 return this.selNode;
36329 * Returns true if the node is selected
36330 * @param {TreeNode} node The node to check
36331 * @return {Boolean}
36333 isSelected : function(node){
36334 return this.selNode == node;
36338 * Selects the node above the selected node in the tree, intelligently walking the nodes
36339 * @return TreeNode The new selection
36341 selectPrevious : function(){
36342 var s = this.selNode || this.lastSelNode;
36346 var ps = s.previousSibling;
36348 if(!ps.isExpanded() || ps.childNodes.length < 1){
36349 return this.select(ps);
36351 var lc = ps.lastChild;
36352 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36355 return this.select(lc);
36357 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36358 return this.select(s.parentNode);
36364 * Selects the node above the selected node in the tree, intelligently walking the nodes
36365 * @return TreeNode The new selection
36367 selectNext : function(){
36368 var s = this.selNode || this.lastSelNode;
36372 if(s.firstChild && s.isExpanded()){
36373 return this.select(s.firstChild);
36374 }else if(s.nextSibling){
36375 return this.select(s.nextSibling);
36376 }else if(s.parentNode){
36378 s.parentNode.bubble(function(){
36379 if(this.nextSibling){
36380 newS = this.getOwnerTree().selModel.select(this.nextSibling);
36389 onKeyDown : function(e){
36390 var s = this.selNode || this.lastSelNode;
36391 // undesirable, but required
36396 var k = e.getKey();
36404 this.selectPrevious();
36407 e.preventDefault();
36408 if(s.hasChildNodes()){
36409 if(!s.isExpanded()){
36411 }else if(s.firstChild){
36412 this.select(s.firstChild, e);
36417 e.preventDefault();
36418 if(s.hasChildNodes() && s.isExpanded()){
36420 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36421 this.select(s.parentNode, e);
36429 * @class Roo.tree.MultiSelectionModel
36430 * @extends Roo.util.Observable
36431 * Multi selection for a TreePanel.
36432 * @param {Object} cfg Configuration
36434 Roo.tree.MultiSelectionModel = function(){
36435 this.selNodes = [];
36439 * @event selectionchange
36440 * Fires when the selected nodes change
36441 * @param {MultiSelectionModel} this
36442 * @param {Array} nodes Array of the selected nodes
36444 "selectionchange" : true
36446 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36450 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36451 init : function(tree){
36453 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36454 tree.on("click", this.onNodeClick, this);
36457 onNodeClick : function(node, e){
36458 this.select(node, e, e.ctrlKey);
36463 * @param {TreeNode} node The node to select
36464 * @param {EventObject} e (optional) An event associated with the selection
36465 * @param {Boolean} keepExisting True to retain existing selections
36466 * @return {TreeNode} The selected node
36468 select : function(node, e, keepExisting){
36469 if(keepExisting !== true){
36470 this.clearSelections(true);
36472 if(this.isSelected(node)){
36473 this.lastSelNode = node;
36476 this.selNodes.push(node);
36477 this.selMap[node.id] = node;
36478 this.lastSelNode = node;
36479 node.ui.onSelectedChange(true);
36480 this.fireEvent("selectionchange", this, this.selNodes);
36486 * @param {TreeNode} node The node to unselect
36488 unselect : function(node){
36489 if(this.selMap[node.id]){
36490 node.ui.onSelectedChange(false);
36491 var sn = this.selNodes;
36494 index = sn.indexOf(node);
36496 for(var i = 0, len = sn.length; i < len; i++){
36504 this.selNodes.splice(index, 1);
36506 delete this.selMap[node.id];
36507 this.fireEvent("selectionchange", this, this.selNodes);
36512 * Clear all selections
36514 clearSelections : function(suppressEvent){
36515 var sn = this.selNodes;
36517 for(var i = 0, len = sn.length; i < len; i++){
36518 sn[i].ui.onSelectedChange(false);
36520 this.selNodes = [];
36522 if(suppressEvent !== true){
36523 this.fireEvent("selectionchange", this, this.selNodes);
36529 * Returns true if the node is selected
36530 * @param {TreeNode} node The node to check
36531 * @return {Boolean}
36533 isSelected : function(node){
36534 return this.selMap[node.id] ? true : false;
36538 * Returns an array of the selected nodes
36541 getSelectedNodes : function(){
36542 return this.selNodes;
36545 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36547 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36549 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36552 * Ext JS Library 1.1.1
36553 * Copyright(c) 2006-2007, Ext JS, LLC.
36555 * Originally Released Under LGPL - original licence link has changed is not relivant.
36558 * <script type="text/javascript">
36562 * @class Roo.tree.TreeNode
36563 * @extends Roo.data.Node
36564 * @cfg {String} text The text for this node
36565 * @cfg {Boolean} expanded true to start the node expanded
36566 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36567 * @cfg {Boolean} allowDrop false if this node cannot be drop on
36568 * @cfg {Boolean} disabled true to start the node disabled
36569 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36570 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
36571 * @cfg {String} cls A css class to be added to the node
36572 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36573 * @cfg {String} href URL of the link used for the node (defaults to #)
36574 * @cfg {String} hrefTarget target frame for the link
36575 * @cfg {String} qtip An Ext QuickTip for the node
36576 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36577 * @cfg {Boolean} singleClickExpand True for single click expand on this node
36578 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36579 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36580 * (defaults to undefined with no checkbox rendered)
36582 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36584 Roo.tree.TreeNode = function(attributes){
36585 attributes = attributes || {};
36586 if(typeof attributes == "string"){
36587 attributes = {text: attributes};
36589 this.childrenRendered = false;
36590 this.rendered = false;
36591 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36592 this.expanded = attributes.expanded === true;
36593 this.isTarget = attributes.isTarget !== false;
36594 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36595 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36598 * Read-only. The text for this node. To change it use setText().
36601 this.text = attributes.text;
36603 * True if this node is disabled.
36606 this.disabled = attributes.disabled === true;
36610 * @event textchange
36611 * Fires when the text for this node is changed
36612 * @param {Node} this This node
36613 * @param {String} text The new text
36614 * @param {String} oldText The old text
36616 "textchange" : true,
36618 * @event beforeexpand
36619 * Fires before this node is expanded, return false to cancel.
36620 * @param {Node} this This node
36621 * @param {Boolean} deep
36622 * @param {Boolean} anim
36624 "beforeexpand" : true,
36626 * @event beforecollapse
36627 * Fires before this node is collapsed, return false to cancel.
36628 * @param {Node} this This node
36629 * @param {Boolean} deep
36630 * @param {Boolean} anim
36632 "beforecollapse" : true,
36635 * Fires when this node is expanded
36636 * @param {Node} this This node
36640 * @event disabledchange
36641 * Fires when the disabled status of this node changes
36642 * @param {Node} this This node
36643 * @param {Boolean} disabled
36645 "disabledchange" : true,
36648 * Fires when this node is collapsed
36649 * @param {Node} this This node
36653 * @event beforeclick
36654 * Fires before click processing. Return false to cancel the default action.
36655 * @param {Node} this This node
36656 * @param {Roo.EventObject} e The event object
36658 "beforeclick":true,
36660 * @event checkchange
36661 * Fires when a node with a checkbox's checked property changes
36662 * @param {Node} this This node
36663 * @param {Boolean} checked
36665 "checkchange":true,
36668 * Fires when this node is clicked
36669 * @param {Node} this This node
36670 * @param {Roo.EventObject} e The event object
36675 * Fires when this node is double clicked
36676 * @param {Node} this This node
36677 * @param {Roo.EventObject} e The event object
36681 * @event contextmenu
36682 * Fires when this node is right clicked
36683 * @param {Node} this This node
36684 * @param {Roo.EventObject} e The event object
36686 "contextmenu":true,
36688 * @event beforechildrenrendered
36689 * Fires right before the child nodes for this node are rendered
36690 * @param {Node} this This node
36692 "beforechildrenrendered":true
36695 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36698 * Read-only. The UI for this node
36701 this.ui = new uiClass(this);
36703 // finally support items[]
36704 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36709 Roo.each(this.attributes.items, function(c) {
36710 this.appendChild(Roo.factory(c,Roo.Tree));
36712 delete this.attributes.items;
36717 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36718 preventHScroll: true,
36720 * Returns true if this node is expanded
36721 * @return {Boolean}
36723 isExpanded : function(){
36724 return this.expanded;
36728 * Returns the UI object for this node
36729 * @return {TreeNodeUI}
36731 getUI : function(){
36735 // private override
36736 setFirstChild : function(node){
36737 var of = this.firstChild;
36738 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36739 if(this.childrenRendered && of && node != of){
36740 of.renderIndent(true, true);
36743 this.renderIndent(true, true);
36747 // private override
36748 setLastChild : function(node){
36749 var ol = this.lastChild;
36750 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36751 if(this.childrenRendered && ol && node != ol){
36752 ol.renderIndent(true, true);
36755 this.renderIndent(true, true);
36759 // these methods are overridden to provide lazy rendering support
36760 // private override
36761 appendChild : function()
36763 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36764 if(node && this.childrenRendered){
36767 this.ui.updateExpandIcon();
36771 // private override
36772 removeChild : function(node){
36773 this.ownerTree.getSelectionModel().unselect(node);
36774 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36775 // if it's been rendered remove dom node
36776 if(this.childrenRendered){
36779 if(this.childNodes.length < 1){
36780 this.collapse(false, false);
36782 this.ui.updateExpandIcon();
36784 if(!this.firstChild) {
36785 this.childrenRendered = false;
36790 // private override
36791 insertBefore : function(node, refNode){
36792 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36793 if(newNode && refNode && this.childrenRendered){
36796 this.ui.updateExpandIcon();
36801 * Sets the text for this node
36802 * @param {String} text
36804 setText : function(text){
36805 var oldText = this.text;
36807 this.attributes.text = text;
36808 if(this.rendered){ // event without subscribing
36809 this.ui.onTextChange(this, text, oldText);
36811 this.fireEvent("textchange", this, text, oldText);
36815 * Triggers selection of this node
36817 select : function(){
36818 this.getOwnerTree().getSelectionModel().select(this);
36822 * Triggers deselection of this node
36824 unselect : function(){
36825 this.getOwnerTree().getSelectionModel().unselect(this);
36829 * Returns true if this node is selected
36830 * @return {Boolean}
36832 isSelected : function(){
36833 return this.getOwnerTree().getSelectionModel().isSelected(this);
36837 * Expand this node.
36838 * @param {Boolean} deep (optional) True to expand all children as well
36839 * @param {Boolean} anim (optional) false to cancel the default animation
36840 * @param {Function} callback (optional) A callback to be called when
36841 * expanding this node completes (does not wait for deep expand to complete).
36842 * Called with 1 parameter, this node.
36844 expand : function(deep, anim, callback){
36845 if(!this.expanded){
36846 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36849 if(!this.childrenRendered){
36850 this.renderChildren();
36852 this.expanded = true;
36854 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36855 this.ui.animExpand(function(){
36856 this.fireEvent("expand", this);
36857 if(typeof callback == "function"){
36861 this.expandChildNodes(true);
36863 }.createDelegate(this));
36867 this.fireEvent("expand", this);
36868 if(typeof callback == "function"){
36873 if(typeof callback == "function"){
36878 this.expandChildNodes(true);
36882 isHiddenRoot : function(){
36883 return this.isRoot && !this.getOwnerTree().rootVisible;
36887 * Collapse this node.
36888 * @param {Boolean} deep (optional) True to collapse all children as well
36889 * @param {Boolean} anim (optional) false to cancel the default animation
36891 collapse : function(deep, anim){
36892 if(this.expanded && !this.isHiddenRoot()){
36893 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36896 this.expanded = false;
36897 if((this.getOwnerTree().animate && anim !== false) || anim){
36898 this.ui.animCollapse(function(){
36899 this.fireEvent("collapse", this);
36901 this.collapseChildNodes(true);
36903 }.createDelegate(this));
36906 this.ui.collapse();
36907 this.fireEvent("collapse", this);
36911 var cs = this.childNodes;
36912 for(var i = 0, len = cs.length; i < len; i++) {
36913 cs[i].collapse(true, false);
36919 delayedExpand : function(delay){
36920 if(!this.expandProcId){
36921 this.expandProcId = this.expand.defer(delay, this);
36926 cancelExpand : function(){
36927 if(this.expandProcId){
36928 clearTimeout(this.expandProcId);
36930 this.expandProcId = false;
36934 * Toggles expanded/collapsed state of the node
36936 toggle : function(){
36945 * Ensures all parent nodes are expanded
36947 ensureVisible : function(callback){
36948 var tree = this.getOwnerTree();
36949 tree.expandPath(this.parentNode.getPath(), false, function(){
36950 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36951 Roo.callback(callback);
36952 }.createDelegate(this));
36956 * Expand all child nodes
36957 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36959 expandChildNodes : function(deep){
36960 var cs = this.childNodes;
36961 for(var i = 0, len = cs.length; i < len; i++) {
36962 cs[i].expand(deep);
36967 * Collapse all child nodes
36968 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36970 collapseChildNodes : function(deep){
36971 var cs = this.childNodes;
36972 for(var i = 0, len = cs.length; i < len; i++) {
36973 cs[i].collapse(deep);
36978 * Disables this node
36980 disable : function(){
36981 this.disabled = true;
36983 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36984 this.ui.onDisableChange(this, true);
36986 this.fireEvent("disabledchange", this, true);
36990 * Enables this node
36992 enable : function(){
36993 this.disabled = false;
36994 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36995 this.ui.onDisableChange(this, false);
36997 this.fireEvent("disabledchange", this, false);
37001 renderChildren : function(suppressEvent){
37002 if(suppressEvent !== false){
37003 this.fireEvent("beforechildrenrendered", this);
37005 var cs = this.childNodes;
37006 for(var i = 0, len = cs.length; i < len; i++){
37007 cs[i].render(true);
37009 this.childrenRendered = true;
37013 sort : function(fn, scope){
37014 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37015 if(this.childrenRendered){
37016 var cs = this.childNodes;
37017 for(var i = 0, len = cs.length; i < len; i++){
37018 cs[i].render(true);
37024 render : function(bulkRender){
37025 this.ui.render(bulkRender);
37026 if(!this.rendered){
37027 this.rendered = true;
37029 this.expanded = false;
37030 this.expand(false, false);
37036 renderIndent : function(deep, refresh){
37038 this.ui.childIndent = null;
37040 this.ui.renderIndent();
37041 if(deep === true && this.childrenRendered){
37042 var cs = this.childNodes;
37043 for(var i = 0, len = cs.length; i < len; i++){
37044 cs[i].renderIndent(true, refresh);
37050 * Ext JS Library 1.1.1
37051 * Copyright(c) 2006-2007, Ext JS, LLC.
37053 * Originally Released Under LGPL - original licence link has changed is not relivant.
37056 * <script type="text/javascript">
37060 * @class Roo.tree.AsyncTreeNode
37061 * @extends Roo.tree.TreeNode
37062 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37064 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
37066 Roo.tree.AsyncTreeNode = function(config){
37067 this.loaded = false;
37068 this.loading = false;
37069 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37071 * @event beforeload
37072 * Fires before this node is loaded, return false to cancel
37073 * @param {Node} this This node
37075 this.addEvents({'beforeload':true, 'load': true});
37078 * Fires when this node is loaded
37079 * @param {Node} this This node
37082 * The loader used by this node (defaults to using the tree's defined loader)
37087 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37088 expand : function(deep, anim, callback){
37089 if(this.loading){ // if an async load is already running, waiting til it's done
37091 var f = function(){
37092 if(!this.loading){ // done loading
37093 clearInterval(timer);
37094 this.expand(deep, anim, callback);
37096 }.createDelegate(this);
37097 timer = setInterval(f, 200);
37101 if(this.fireEvent("beforeload", this) === false){
37104 this.loading = true;
37105 this.ui.beforeLoad(this);
37106 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37108 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37112 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37116 * Returns true if this node is currently loading
37117 * @return {Boolean}
37119 isLoading : function(){
37120 return this.loading;
37123 loadComplete : function(deep, anim, callback){
37124 this.loading = false;
37125 this.loaded = true;
37126 this.ui.afterLoad(this);
37127 this.fireEvent("load", this);
37128 this.expand(deep, anim, callback);
37132 * Returns true if this node has been loaded
37133 * @return {Boolean}
37135 isLoaded : function(){
37136 return this.loaded;
37139 hasChildNodes : function(){
37140 if(!this.isLeaf() && !this.loaded){
37143 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37148 * Trigger a reload for this node
37149 * @param {Function} callback
37151 reload : function(callback){
37152 this.collapse(false, false);
37153 while(this.firstChild){
37154 this.removeChild(this.firstChild);
37156 this.childrenRendered = false;
37157 this.loaded = false;
37158 if(this.isHiddenRoot()){
37159 this.expanded = false;
37161 this.expand(false, false, callback);
37165 * Ext JS Library 1.1.1
37166 * Copyright(c) 2006-2007, Ext JS, LLC.
37168 * Originally Released Under LGPL - original licence link has changed is not relivant.
37171 * <script type="text/javascript">
37175 * @class Roo.tree.TreeNodeUI
37177 * @param {Object} node The node to render
37178 * The TreeNode UI implementation is separate from the
37179 * tree implementation. Unless you are customizing the tree UI,
37180 * you should never have to use this directly.
37182 Roo.tree.TreeNodeUI = function(node){
37184 this.rendered = false;
37185 this.animating = false;
37186 this.emptyIcon = Roo.BLANK_IMAGE_URL;
37189 Roo.tree.TreeNodeUI.prototype = {
37190 removeChild : function(node){
37192 this.ctNode.removeChild(node.ui.getEl());
37196 beforeLoad : function(){
37197 this.addClass("x-tree-node-loading");
37200 afterLoad : function(){
37201 this.removeClass("x-tree-node-loading");
37204 onTextChange : function(node, text, oldText){
37206 this.textNode.innerHTML = text;
37210 onDisableChange : function(node, state){
37211 this.disabled = state;
37213 this.addClass("x-tree-node-disabled");
37215 this.removeClass("x-tree-node-disabled");
37219 onSelectedChange : function(state){
37222 this.addClass("x-tree-selected");
37225 this.removeClass("x-tree-selected");
37229 onMove : function(tree, node, oldParent, newParent, index, refNode){
37230 this.childIndent = null;
37232 var targetNode = newParent.ui.getContainer();
37233 if(!targetNode){//target not rendered
37234 this.holder = document.createElement("div");
37235 this.holder.appendChild(this.wrap);
37238 var insertBefore = refNode ? refNode.ui.getEl() : null;
37240 targetNode.insertBefore(this.wrap, insertBefore);
37242 targetNode.appendChild(this.wrap);
37244 this.node.renderIndent(true);
37248 addClass : function(cls){
37250 Roo.fly(this.elNode).addClass(cls);
37254 removeClass : function(cls){
37256 Roo.fly(this.elNode).removeClass(cls);
37260 remove : function(){
37262 this.holder = document.createElement("div");
37263 this.holder.appendChild(this.wrap);
37267 fireEvent : function(){
37268 return this.node.fireEvent.apply(this.node, arguments);
37271 initEvents : function(){
37272 this.node.on("move", this.onMove, this);
37273 var E = Roo.EventManager;
37274 var a = this.anchor;
37276 var el = Roo.fly(a, '_treeui');
37278 if(Roo.isOpera){ // opera render bug ignores the CSS
37279 el.setStyle("text-decoration", "none");
37282 el.on("click", this.onClick, this);
37283 el.on("dblclick", this.onDblClick, this);
37286 Roo.EventManager.on(this.checkbox,
37287 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37290 el.on("contextmenu", this.onContextMenu, this);
37292 var icon = Roo.fly(this.iconNode);
37293 icon.on("click", this.onClick, this);
37294 icon.on("dblclick", this.onDblClick, this);
37295 icon.on("contextmenu", this.onContextMenu, this);
37296 E.on(this.ecNode, "click", this.ecClick, this, true);
37298 if(this.node.disabled){
37299 this.addClass("x-tree-node-disabled");
37301 if(this.node.hidden){
37302 this.addClass("x-tree-node-disabled");
37304 var ot = this.node.getOwnerTree();
37305 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37306 if(dd && (!this.node.isRoot || ot.rootVisible)){
37307 Roo.dd.Registry.register(this.elNode, {
37309 handles: this.getDDHandles(),
37315 getDDHandles : function(){
37316 return [this.iconNode, this.textNode];
37321 this.wrap.style.display = "none";
37327 this.wrap.style.display = "";
37331 onContextMenu : function(e){
37332 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37333 e.preventDefault();
37335 this.fireEvent("contextmenu", this.node, e);
37339 onClick : function(e){
37344 if(this.fireEvent("beforeclick", this.node, e) !== false){
37345 if(!this.disabled && this.node.attributes.href){
37346 this.fireEvent("click", this.node, e);
37349 e.preventDefault();
37354 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37355 this.node.toggle();
37358 this.fireEvent("click", this.node, e);
37364 onDblClick : function(e){
37365 e.preventDefault();
37370 this.toggleCheck();
37372 if(!this.animating && this.node.hasChildNodes()){
37373 this.node.toggle();
37375 this.fireEvent("dblclick", this.node, e);
37378 onCheckChange : function(){
37379 var checked = this.checkbox.checked;
37380 this.node.attributes.checked = checked;
37381 this.fireEvent('checkchange', this.node, checked);
37384 ecClick : function(e){
37385 if(!this.animating && this.node.hasChildNodes()){
37386 this.node.toggle();
37390 startDrop : function(){
37391 this.dropping = true;
37394 // delayed drop so the click event doesn't get fired on a drop
37395 endDrop : function(){
37396 setTimeout(function(){
37397 this.dropping = false;
37398 }.createDelegate(this), 50);
37401 expand : function(){
37402 this.updateExpandIcon();
37403 this.ctNode.style.display = "";
37406 focus : function(){
37407 if(!this.node.preventHScroll){
37408 try{this.anchor.focus();
37410 }else if(!Roo.isIE){
37412 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37413 var l = noscroll.scrollLeft;
37414 this.anchor.focus();
37415 noscroll.scrollLeft = l;
37420 toggleCheck : function(value){
37421 var cb = this.checkbox;
37423 cb.checked = (value === undefined ? !cb.checked : value);
37429 this.anchor.blur();
37433 animExpand : function(callback){
37434 var ct = Roo.get(this.ctNode);
37436 if(!this.node.hasChildNodes()){
37437 this.updateExpandIcon();
37438 this.ctNode.style.display = "";
37439 Roo.callback(callback);
37442 this.animating = true;
37443 this.updateExpandIcon();
37446 callback : function(){
37447 this.animating = false;
37448 Roo.callback(callback);
37451 duration: this.node.ownerTree.duration || .25
37455 highlight : function(){
37456 var tree = this.node.getOwnerTree();
37457 Roo.fly(this.wrap).highlight(
37458 tree.hlColor || "C3DAF9",
37459 {endColor: tree.hlBaseColor}
37463 collapse : function(){
37464 this.updateExpandIcon();
37465 this.ctNode.style.display = "none";
37468 animCollapse : function(callback){
37469 var ct = Roo.get(this.ctNode);
37470 ct.enableDisplayMode('block');
37473 this.animating = true;
37474 this.updateExpandIcon();
37477 callback : function(){
37478 this.animating = false;
37479 Roo.callback(callback);
37482 duration: this.node.ownerTree.duration || .25
37486 getContainer : function(){
37487 return this.ctNode;
37490 getEl : function(){
37494 appendDDGhost : function(ghostNode){
37495 ghostNode.appendChild(this.elNode.cloneNode(true));
37498 getDDRepairXY : function(){
37499 return Roo.lib.Dom.getXY(this.iconNode);
37502 onRender : function(){
37506 render : function(bulkRender){
37507 var n = this.node, a = n.attributes;
37508 var targetNode = n.parentNode ?
37509 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37511 if(!this.rendered){
37512 this.rendered = true;
37514 this.renderElements(n, a, targetNode, bulkRender);
37517 if(this.textNode.setAttributeNS){
37518 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37520 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37523 this.textNode.setAttribute("ext:qtip", a.qtip);
37525 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37528 }else if(a.qtipCfg){
37529 a.qtipCfg.target = Roo.id(this.textNode);
37530 Roo.QuickTips.register(a.qtipCfg);
37533 if(!this.node.expanded){
37534 this.updateExpandIcon();
37537 if(bulkRender === true) {
37538 targetNode.appendChild(this.wrap);
37543 renderElements : function(n, a, targetNode, bulkRender)
37545 // add some indent caching, this helps performance when rendering a large tree
37546 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37547 var t = n.getOwnerTree();
37548 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37549 if (typeof(n.attributes.html) != 'undefined') {
37550 txt = n.attributes.html;
37552 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37553 var cb = typeof a.checked == 'boolean';
37554 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37555 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37556 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37557 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37558 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37559 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37560 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37561 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
37562 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37563 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37566 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37567 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37568 n.nextSibling.ui.getEl(), buf.join(""));
37570 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37573 this.elNode = this.wrap.childNodes[0];
37574 this.ctNode = this.wrap.childNodes[1];
37575 var cs = this.elNode.childNodes;
37576 this.indentNode = cs[0];
37577 this.ecNode = cs[1];
37578 this.iconNode = cs[2];
37581 this.checkbox = cs[3];
37584 this.anchor = cs[index];
37585 this.textNode = cs[index].firstChild;
37588 getAnchor : function(){
37589 return this.anchor;
37592 getTextEl : function(){
37593 return this.textNode;
37596 getIconEl : function(){
37597 return this.iconNode;
37600 isChecked : function(){
37601 return this.checkbox ? this.checkbox.checked : false;
37604 updateExpandIcon : function(){
37606 var n = this.node, c1, c2;
37607 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37608 var hasChild = n.hasChildNodes();
37612 c1 = "x-tree-node-collapsed";
37613 c2 = "x-tree-node-expanded";
37616 c1 = "x-tree-node-expanded";
37617 c2 = "x-tree-node-collapsed";
37620 this.removeClass("x-tree-node-leaf");
37621 this.wasLeaf = false;
37623 if(this.c1 != c1 || this.c2 != c2){
37624 Roo.fly(this.elNode).replaceClass(c1, c2);
37625 this.c1 = c1; this.c2 = c2;
37628 // this changes non-leafs into leafs if they have no children.
37629 // it's not very rational behaviour..
37631 if(!this.wasLeaf && this.node.leaf){
37632 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37635 this.wasLeaf = true;
37638 var ecc = "x-tree-ec-icon "+cls;
37639 if(this.ecc != ecc){
37640 this.ecNode.className = ecc;
37646 getChildIndent : function(){
37647 if(!this.childIndent){
37651 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37653 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37655 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37660 this.childIndent = buf.join("");
37662 return this.childIndent;
37665 renderIndent : function(){
37668 var p = this.node.parentNode;
37670 indent = p.ui.getChildIndent();
37672 if(this.indentMarkup != indent){ // don't rerender if not required
37673 this.indentNode.innerHTML = indent;
37674 this.indentMarkup = indent;
37676 this.updateExpandIcon();
37681 Roo.tree.RootTreeNodeUI = function(){
37682 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37684 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37685 render : function(){
37686 if(!this.rendered){
37687 var targetNode = this.node.ownerTree.innerCt.dom;
37688 this.node.expanded = true;
37689 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37690 this.wrap = this.ctNode = targetNode.firstChild;
37693 collapse : function(){
37695 expand : function(){
37699 * Ext JS Library 1.1.1
37700 * Copyright(c) 2006-2007, Ext JS, LLC.
37702 * Originally Released Under LGPL - original licence link has changed is not relivant.
37705 * <script type="text/javascript">
37708 * @class Roo.tree.TreeLoader
37709 * @extends Roo.util.Observable
37710 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37711 * nodes from a specified URL. The response must be a javascript Array definition
37712 * who's elements are node definition objects. eg:
37717 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37718 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37725 * The old style respose with just an array is still supported, but not recommended.
37728 * A server request is sent, and child nodes are loaded only when a node is expanded.
37729 * The loading node's id is passed to the server under the parameter name "node" to
37730 * enable the server to produce the correct child nodes.
37732 * To pass extra parameters, an event handler may be attached to the "beforeload"
37733 * event, and the parameters specified in the TreeLoader's baseParams property:
37735 myTreeLoader.on("beforeload", function(treeLoader, node) {
37736 this.baseParams.category = node.attributes.category;
37741 * This would pass an HTTP parameter called "category" to the server containing
37742 * the value of the Node's "category" attribute.
37744 * Creates a new Treeloader.
37745 * @param {Object} config A config object containing config properties.
37747 Roo.tree.TreeLoader = function(config){
37748 this.baseParams = {};
37749 this.requestMethod = "POST";
37750 Roo.apply(this, config);
37755 * @event beforeload
37756 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37757 * @param {Object} This TreeLoader object.
37758 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37759 * @param {Object} callback The callback function specified in the {@link #load} call.
37764 * Fires when the node has been successfuly loaded.
37765 * @param {Object} This TreeLoader object.
37766 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37767 * @param {Object} response The response object containing the data from the server.
37771 * @event loadexception
37772 * Fires if the network request failed.
37773 * @param {Object} This TreeLoader object.
37774 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37775 * @param {Object} response The response object containing the data from the server.
37777 loadexception : true,
37780 * Fires before a node is created, enabling you to return custom Node types
37781 * @param {Object} This TreeLoader object.
37782 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37787 Roo.tree.TreeLoader.superclass.constructor.call(this);
37790 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37792 * @cfg {String} dataUrl The URL from which to request a Json string which
37793 * specifies an array of node definition object representing the child nodes
37797 * @cfg {String} requestMethod either GET or POST
37798 * defaults to POST (due to BC)
37802 * @cfg {Object} baseParams (optional) An object containing properties which
37803 * specify HTTP parameters to be passed to each request for child nodes.
37806 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37807 * created by this loader. If the attributes sent by the server have an attribute in this object,
37808 * they take priority.
37811 * @cfg {Object} uiProviders (optional) An object containing properties which
37813 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37814 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37815 * <i>uiProvider</i> attribute of a returned child node is a string rather
37816 * than a reference to a TreeNodeUI implementation, this that string value
37817 * is used as a property name in the uiProviders object. You can define the provider named
37818 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37823 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37824 * child nodes before loading.
37826 clearOnLoad : true,
37829 * @cfg {String} root (optional) Default to false. Use this to read data from an object
37830 * property on loading, rather than expecting an array. (eg. more compatible to a standard
37831 * Grid query { data : [ .....] }
37836 * @cfg {String} queryParam (optional)
37837 * Name of the query as it will be passed on the querystring (defaults to 'node')
37838 * eg. the request will be ?node=[id]
37845 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37846 * This is called automatically when a node is expanded, but may be used to reload
37847 * a node (or append new children if the {@link #clearOnLoad} option is false.)
37848 * @param {Roo.tree.TreeNode} node
37849 * @param {Function} callback
37851 load : function(node, callback){
37852 if(this.clearOnLoad){
37853 while(node.firstChild){
37854 node.removeChild(node.firstChild);
37857 if(node.attributes.children){ // preloaded json children
37858 var cs = node.attributes.children;
37859 for(var i = 0, len = cs.length; i < len; i++){
37860 node.appendChild(this.createNode(cs[i]));
37862 if(typeof callback == "function"){
37865 }else if(this.dataUrl){
37866 this.requestData(node, callback);
37870 getParams: function(node){
37871 var buf = [], bp = this.baseParams;
37872 for(var key in bp){
37873 if(typeof bp[key] != "function"){
37874 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37877 var n = this.queryParam === false ? 'node' : this.queryParam;
37878 buf.push(n + "=", encodeURIComponent(node.id));
37879 return buf.join("");
37882 requestData : function(node, callback){
37883 if(this.fireEvent("beforeload", this, node, callback) !== false){
37884 this.transId = Roo.Ajax.request({
37885 method:this.requestMethod,
37886 url: this.dataUrl||this.url,
37887 success: this.handleResponse,
37888 failure: this.handleFailure,
37890 argument: {callback: callback, node: node},
37891 params: this.getParams(node)
37894 // if the load is cancelled, make sure we notify
37895 // the node that we are done
37896 if(typeof callback == "function"){
37902 isLoading : function(){
37903 return this.transId ? true : false;
37906 abort : function(){
37907 if(this.isLoading()){
37908 Roo.Ajax.abort(this.transId);
37913 createNode : function(attr)
37915 // apply baseAttrs, nice idea Corey!
37916 if(this.baseAttrs){
37917 Roo.applyIf(attr, this.baseAttrs);
37919 if(this.applyLoader !== false){
37920 attr.loader = this;
37922 // uiProvider = depreciated..
37924 if(typeof(attr.uiProvider) == 'string'){
37925 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
37926 /** eval:var:attr */ eval(attr.uiProvider);
37928 if(typeof(this.uiProviders['default']) != 'undefined') {
37929 attr.uiProvider = this.uiProviders['default'];
37932 this.fireEvent('create', this, attr);
37934 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37936 new Roo.tree.TreeNode(attr) :
37937 new Roo.tree.AsyncTreeNode(attr));
37940 processResponse : function(response, node, callback)
37942 var json = response.responseText;
37945 var o = Roo.decode(json);
37947 if (this.root === false && typeof(o.success) != undefined) {
37948 this.root = 'data'; // the default behaviour for list like data..
37951 if (this.root !== false && !o.success) {
37952 // it's a failure condition.
37953 var a = response.argument;
37954 this.fireEvent("loadexception", this, a.node, response);
37955 Roo.log("Load failed - should have a handler really");
37961 if (this.root !== false) {
37965 for(var i = 0, len = o.length; i < len; i++){
37966 var n = this.createNode(o[i]);
37968 node.appendChild(n);
37971 if(typeof callback == "function"){
37972 callback(this, node);
37975 this.handleFailure(response);
37979 handleResponse : function(response){
37980 this.transId = false;
37981 var a = response.argument;
37982 this.processResponse(response, a.node, a.callback);
37983 this.fireEvent("load", this, a.node, response);
37986 handleFailure : function(response)
37988 // should handle failure better..
37989 this.transId = false;
37990 var a = response.argument;
37991 this.fireEvent("loadexception", this, a.node, response);
37992 if(typeof a.callback == "function"){
37993 a.callback(this, a.node);
37998 * Ext JS Library 1.1.1
37999 * Copyright(c) 2006-2007, Ext JS, LLC.
38001 * Originally Released Under LGPL - original licence link has changed is not relivant.
38004 * <script type="text/javascript">
38008 * @class Roo.tree.TreeFilter
38009 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38010 * @param {TreePanel} tree
38011 * @param {Object} config (optional)
38013 Roo.tree.TreeFilter = function(tree, config){
38015 this.filtered = {};
38016 Roo.apply(this, config);
38019 Roo.tree.TreeFilter.prototype = {
38026 * Filter the data by a specific attribute.
38027 * @param {String/RegExp} value Either string that the attribute value
38028 * should start with or a RegExp to test against the attribute
38029 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38030 * @param {TreeNode} startNode (optional) The node to start the filter at.
38032 filter : function(value, attr, startNode){
38033 attr = attr || "text";
38035 if(typeof value == "string"){
38036 var vlen = value.length;
38037 // auto clear empty filter
38038 if(vlen == 0 && this.clearBlank){
38042 value = value.toLowerCase();
38044 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38046 }else if(value.exec){ // regex?
38048 return value.test(n.attributes[attr]);
38051 throw 'Illegal filter type, must be string or regex';
38053 this.filterBy(f, null, startNode);
38057 * Filter by a function. The passed function will be called with each
38058 * node in the tree (or from the startNode). If the function returns true, the node is kept
38059 * otherwise it is filtered. If a node is filtered, its children are also filtered.
38060 * @param {Function} fn The filter function
38061 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38063 filterBy : function(fn, scope, startNode){
38064 startNode = startNode || this.tree.root;
38065 if(this.autoClear){
38068 var af = this.filtered, rv = this.reverse;
38069 var f = function(n){
38070 if(n == startNode){
38076 var m = fn.call(scope || n, n);
38084 startNode.cascade(f);
38087 if(typeof id != "function"){
38089 if(n && n.parentNode){
38090 n.parentNode.removeChild(n);
38098 * Clears the current filter. Note: with the "remove" option
38099 * set a filter cannot be cleared.
38101 clear : function(){
38103 var af = this.filtered;
38105 if(typeof id != "function"){
38112 this.filtered = {};
38117 * Ext JS Library 1.1.1
38118 * Copyright(c) 2006-2007, Ext JS, LLC.
38120 * Originally Released Under LGPL - original licence link has changed is not relivant.
38123 * <script type="text/javascript">
38128 * @class Roo.tree.TreeSorter
38129 * Provides sorting of nodes in a TreePanel
38131 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38132 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38133 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38134 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38135 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38136 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38138 * @param {TreePanel} tree
38139 * @param {Object} config
38141 Roo.tree.TreeSorter = function(tree, config){
38142 Roo.apply(this, config);
38143 tree.on("beforechildrenrendered", this.doSort, this);
38144 tree.on("append", this.updateSort, this);
38145 tree.on("insert", this.updateSort, this);
38147 var dsc = this.dir && this.dir.toLowerCase() == "desc";
38148 var p = this.property || "text";
38149 var sortType = this.sortType;
38150 var fs = this.folderSort;
38151 var cs = this.caseSensitive === true;
38152 var leafAttr = this.leafAttr || 'leaf';
38154 this.sortFn = function(n1, n2){
38156 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38159 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38163 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38164 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38166 return dsc ? +1 : -1;
38168 return dsc ? -1 : +1;
38175 Roo.tree.TreeSorter.prototype = {
38176 doSort : function(node){
38177 node.sort(this.sortFn);
38180 compareNodes : function(n1, n2){
38181 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38184 updateSort : function(tree, node){
38185 if(node.childrenRendered){
38186 this.doSort.defer(1, this, [node]);
38191 * Ext JS Library 1.1.1
38192 * Copyright(c) 2006-2007, Ext JS, LLC.
38194 * Originally Released Under LGPL - original licence link has changed is not relivant.
38197 * <script type="text/javascript">
38200 if(Roo.dd.DropZone){
38202 Roo.tree.TreeDropZone = function(tree, config){
38203 this.allowParentInsert = false;
38204 this.allowContainerDrop = false;
38205 this.appendOnly = false;
38206 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38208 this.lastInsertClass = "x-tree-no-status";
38209 this.dragOverData = {};
38212 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38213 ddGroup : "TreeDD",
38216 expandDelay : 1000,
38218 expandNode : function(node){
38219 if(node.hasChildNodes() && !node.isExpanded()){
38220 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38224 queueExpand : function(node){
38225 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38228 cancelExpand : function(){
38229 if(this.expandProcId){
38230 clearTimeout(this.expandProcId);
38231 this.expandProcId = false;
38235 isValidDropPoint : function(n, pt, dd, e, data){
38236 if(!n || !data){ return false; }
38237 var targetNode = n.node;
38238 var dropNode = data.node;
38239 // default drop rules
38240 if(!(targetNode && targetNode.isTarget && pt)){
38243 if(pt == "append" && targetNode.allowChildren === false){
38246 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38249 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38252 // reuse the object
38253 var overEvent = this.dragOverData;
38254 overEvent.tree = this.tree;
38255 overEvent.target = targetNode;
38256 overEvent.data = data;
38257 overEvent.point = pt;
38258 overEvent.source = dd;
38259 overEvent.rawEvent = e;
38260 overEvent.dropNode = dropNode;
38261 overEvent.cancel = false;
38262 var result = this.tree.fireEvent("nodedragover", overEvent);
38263 return overEvent.cancel === false && result !== false;
38266 getDropPoint : function(e, n, dd)
38270 return tn.allowChildren !== false ? "append" : false; // always append for root
38272 var dragEl = n.ddel;
38273 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38274 var y = Roo.lib.Event.getPageY(e);
38275 //var noAppend = tn.allowChildren === false || tn.isLeaf();
38277 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38278 var noAppend = tn.allowChildren === false;
38279 if(this.appendOnly || tn.parentNode.allowChildren === false){
38280 return noAppend ? false : "append";
38282 var noBelow = false;
38283 if(!this.allowParentInsert){
38284 noBelow = tn.hasChildNodes() && tn.isExpanded();
38286 var q = (b - t) / (noAppend ? 2 : 3);
38287 if(y >= t && y < (t + q)){
38289 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38296 onNodeEnter : function(n, dd, e, data)
38298 this.cancelExpand();
38301 onNodeOver : function(n, dd, e, data)
38304 var pt = this.getDropPoint(e, n, dd);
38307 // auto node expand check
38308 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38309 this.queueExpand(node);
38310 }else if(pt != "append"){
38311 this.cancelExpand();
38314 // set the insert point style on the target node
38315 var returnCls = this.dropNotAllowed;
38316 if(this.isValidDropPoint(n, pt, dd, e, data)){
38321 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38322 cls = "x-tree-drag-insert-above";
38323 }else if(pt == "below"){
38324 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38325 cls = "x-tree-drag-insert-below";
38327 returnCls = "x-tree-drop-ok-append";
38328 cls = "x-tree-drag-append";
38330 if(this.lastInsertClass != cls){
38331 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38332 this.lastInsertClass = cls;
38339 onNodeOut : function(n, dd, e, data){
38341 this.cancelExpand();
38342 this.removeDropIndicators(n);
38345 onNodeDrop : function(n, dd, e, data){
38346 var point = this.getDropPoint(e, n, dd);
38347 var targetNode = n.node;
38348 targetNode.ui.startDrop();
38349 if(!this.isValidDropPoint(n, point, dd, e, data)){
38350 targetNode.ui.endDrop();
38353 // first try to find the drop node
38354 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38357 target: targetNode,
38362 dropNode: dropNode,
38365 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38366 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38367 targetNode.ui.endDrop();
38370 // allow target changing
38371 targetNode = dropEvent.target;
38372 if(point == "append" && !targetNode.isExpanded()){
38373 targetNode.expand(false, null, function(){
38374 this.completeDrop(dropEvent);
38375 }.createDelegate(this));
38377 this.completeDrop(dropEvent);
38382 completeDrop : function(de){
38383 var ns = de.dropNode, p = de.point, t = de.target;
38384 if(!(ns instanceof Array)){
38388 for(var i = 0, len = ns.length; i < len; i++){
38391 t.parentNode.insertBefore(n, t);
38392 }else if(p == "below"){
38393 t.parentNode.insertBefore(n, t.nextSibling);
38399 if(this.tree.hlDrop){
38403 this.tree.fireEvent("nodedrop", de);
38406 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38407 if(this.tree.hlDrop){
38408 dropNode.ui.focus();
38409 dropNode.ui.highlight();
38411 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38414 getTree : function(){
38418 removeDropIndicators : function(n){
38421 Roo.fly(el).removeClass([
38422 "x-tree-drag-insert-above",
38423 "x-tree-drag-insert-below",
38424 "x-tree-drag-append"]);
38425 this.lastInsertClass = "_noclass";
38429 beforeDragDrop : function(target, e, id){
38430 this.cancelExpand();
38434 afterRepair : function(data){
38435 if(data && Roo.enableFx){
38436 data.node.ui.highlight();
38446 * Ext JS Library 1.1.1
38447 * Copyright(c) 2006-2007, Ext JS, LLC.
38449 * Originally Released Under LGPL - original licence link has changed is not relivant.
38452 * <script type="text/javascript">
38456 if(Roo.dd.DragZone){
38457 Roo.tree.TreeDragZone = function(tree, config){
38458 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38462 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38463 ddGroup : "TreeDD",
38465 onBeforeDrag : function(data, e){
38467 return n && n.draggable && !n.disabled;
38471 onInitDrag : function(e){
38472 var data = this.dragData;
38473 this.tree.getSelectionModel().select(data.node);
38474 this.proxy.update("");
38475 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38476 this.tree.fireEvent("startdrag", this.tree, data.node, e);
38479 getRepairXY : function(e, data){
38480 return data.node.ui.getDDRepairXY();
38483 onEndDrag : function(data, e){
38484 this.tree.fireEvent("enddrag", this.tree, data.node, e);
38489 onValidDrop : function(dd, e, id){
38490 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38494 beforeInvalidDrop : function(e, id){
38495 // this scrolls the original position back into view
38496 var sm = this.tree.getSelectionModel();
38497 sm.clearSelections();
38498 sm.select(this.dragData.node);
38503 * Ext JS Library 1.1.1
38504 * Copyright(c) 2006-2007, Ext JS, LLC.
38506 * Originally Released Under LGPL - original licence link has changed is not relivant.
38509 * <script type="text/javascript">
38512 * @class Roo.tree.TreeEditor
38513 * @extends Roo.Editor
38514 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
38515 * as the editor field.
38517 * @param {Object} config (used to be the tree panel.)
38518 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38520 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38521 * @cfg {Roo.form.TextField} field [required] The field configuration
38525 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38528 if (oldconfig) { // old style..
38529 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38532 tree = config.tree;
38533 config.field = config.field || {};
38534 config.field.xtype = 'TextField';
38535 field = Roo.factory(config.field, Roo.form);
38537 config = config || {};
38542 * @event beforenodeedit
38543 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
38544 * false from the handler of this event.
38545 * @param {Editor} this
38546 * @param {Roo.tree.Node} node
38548 "beforenodeedit" : true
38552 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38556 tree.on('beforeclick', this.beforeNodeClick, this);
38557 tree.getTreeEl().on('mousedown', this.hide, this);
38558 this.on('complete', this.updateNode, this);
38559 this.on('beforestartedit', this.fitToTree, this);
38560 this.on('startedit', this.bindScroll, this, {delay:10});
38561 this.on('specialkey', this.onSpecialKey, this);
38564 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38566 * @cfg {String} alignment
38567 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38573 * @cfg {Boolean} hideEl
38574 * True to hide the bound element while the editor is displayed (defaults to false)
38578 * @cfg {String} cls
38579 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38581 cls: "x-small-editor x-tree-editor",
38583 * @cfg {Boolean} shim
38584 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38590 * @cfg {Number} maxWidth
38591 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
38592 * the containing tree element's size, it will be automatically limited for you to the container width, taking
38593 * scroll and client offsets into account prior to each edit.
38600 fitToTree : function(ed, el){
38601 var td = this.tree.getTreeEl().dom, nd = el.dom;
38602 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
38603 td.scrollLeft = nd.offsetLeft;
38607 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38608 this.setSize(w, '');
38610 return this.fireEvent('beforenodeedit', this, this.editNode);
38615 triggerEdit : function(node){
38616 this.completeEdit();
38617 this.editNode = node;
38618 this.startEdit(node.ui.textNode, node.text);
38622 bindScroll : function(){
38623 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38627 beforeNodeClick : function(node, e){
38628 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38629 this.lastClick = new Date();
38630 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38632 this.triggerEdit(node);
38639 updateNode : function(ed, value){
38640 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38641 this.editNode.setText(value);
38645 onHide : function(){
38646 Roo.tree.TreeEditor.superclass.onHide.call(this);
38648 this.editNode.ui.focus();
38653 onSpecialKey : function(field, e){
38654 var k = e.getKey();
38658 }else if(k == e.ENTER && !e.hasModifier()){
38660 this.completeEdit();
38663 });//<Script type="text/javascript">
38666 * Ext JS Library 1.1.1
38667 * Copyright(c) 2006-2007, Ext JS, LLC.
38669 * Originally Released Under LGPL - original licence link has changed is not relivant.
38672 * <script type="text/javascript">
38676 * Not documented??? - probably should be...
38679 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38680 //focus: Roo.emptyFn, // prevent odd scrolling behavior
38682 renderElements : function(n, a, targetNode, bulkRender){
38683 //consel.log("renderElements?");
38684 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38686 var t = n.getOwnerTree();
38687 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38689 var cols = t.columns;
38690 var bw = t.borderWidth;
38692 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38693 var cb = typeof a.checked == "boolean";
38694 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38695 var colcls = 'x-t-' + tid + '-c0';
38697 '<li class="x-tree-node">',
38700 '<div class="x-tree-node-el ', a.cls,'">',
38702 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38705 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38706 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
38707 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38708 (a.icon ? ' x-tree-node-inline-icon' : ''),
38709 (a.iconCls ? ' '+a.iconCls : ''),
38710 '" unselectable="on" />',
38711 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
38712 (a.checked ? 'checked="checked" />' : ' />')) : ''),
38714 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38715 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38716 '<span unselectable="on" qtip="' + tx + '">',
38720 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38721 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38723 for(var i = 1, len = cols.length; i < len; i++){
38725 colcls = 'x-t-' + tid + '-c' +i;
38726 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38727 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38728 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38734 '<div class="x-clear"></div></div>',
38735 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38738 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38739 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38740 n.nextSibling.ui.getEl(), buf.join(""));
38742 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38744 var el = this.wrap.firstChild;
38746 this.elNode = el.firstChild;
38747 this.ranchor = el.childNodes[1];
38748 this.ctNode = this.wrap.childNodes[1];
38749 var cs = el.firstChild.childNodes;
38750 this.indentNode = cs[0];
38751 this.ecNode = cs[1];
38752 this.iconNode = cs[2];
38755 this.checkbox = cs[3];
38758 this.anchor = cs[index];
38760 this.textNode = cs[index].firstChild;
38762 //el.on("click", this.onClick, this);
38763 //el.on("dblclick", this.onDblClick, this);
38766 // console.log(this);
38768 initEvents : function(){
38769 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38772 var a = this.ranchor;
38774 var el = Roo.get(a);
38776 if(Roo.isOpera){ // opera render bug ignores the CSS
38777 el.setStyle("text-decoration", "none");
38780 el.on("click", this.onClick, this);
38781 el.on("dblclick", this.onDblClick, this);
38782 el.on("contextmenu", this.onContextMenu, this);
38786 /*onSelectedChange : function(state){
38789 this.addClass("x-tree-selected");
38792 this.removeClass("x-tree-selected");
38795 addClass : function(cls){
38797 Roo.fly(this.elRow).addClass(cls);
38803 removeClass : function(cls){
38805 Roo.fly(this.elRow).removeClass(cls);
38811 });//<Script type="text/javascript">
38815 * Ext JS Library 1.1.1
38816 * Copyright(c) 2006-2007, Ext JS, LLC.
38818 * Originally Released Under LGPL - original licence link has changed is not relivant.
38821 * <script type="text/javascript">
38826 * @class Roo.tree.ColumnTree
38827 * @extends Roo.tree.TreePanel
38828 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
38829 * @cfg {int} borderWidth compined right/left border allowance
38831 * @param {String/HTMLElement/Element} el The container element
38832 * @param {Object} config
38834 Roo.tree.ColumnTree = function(el, config)
38836 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38840 * Fire this event on a container when it resizes
38841 * @param {int} w Width
38842 * @param {int} h Height
38846 this.on('resize', this.onResize, this);
38849 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38853 borderWidth: Roo.isBorderBox ? 0 : 2,
38856 render : function(){
38857 // add the header.....
38859 Roo.tree.ColumnTree.superclass.render.apply(this);
38861 this.el.addClass('x-column-tree');
38863 this.headers = this.el.createChild(
38864 {cls:'x-tree-headers'},this.innerCt.dom);
38866 var cols = this.columns, c;
38867 var totalWidth = 0;
38869 var len = cols.length;
38870 for(var i = 0; i < len; i++){
38872 totalWidth += c.width;
38873 this.headEls.push(this.headers.createChild({
38874 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38876 cls:'x-tree-hd-text',
38879 style:'width:'+(c.width-this.borderWidth)+'px;'
38882 this.headers.createChild({cls:'x-clear'});
38883 // prevent floats from wrapping when clipped
38884 this.headers.setWidth(totalWidth);
38885 //this.innerCt.setWidth(totalWidth);
38886 this.innerCt.setStyle({ overflow: 'auto' });
38887 this.onResize(this.width, this.height);
38891 onResize : function(w,h)
38896 this.innerCt.setWidth(this.width);
38897 this.innerCt.setHeight(this.height-20);
38900 var cols = this.columns, c;
38901 var totalWidth = 0;
38903 var len = cols.length;
38904 for(var i = 0; i < len; i++){
38906 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38907 // it's the expander..
38908 expEl = this.headEls[i];
38911 totalWidth += c.width;
38915 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
38917 this.headers.setWidth(w-20);
38926 * Ext JS Library 1.1.1
38927 * Copyright(c) 2006-2007, Ext JS, LLC.
38929 * Originally Released Under LGPL - original licence link has changed is not relivant.
38932 * <script type="text/javascript">
38936 * @class Roo.menu.Menu
38937 * @extends Roo.util.Observable
38938 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38939 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
38940 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38942 * Creates a new Menu
38943 * @param {Object} config Configuration options
38945 Roo.menu.Menu = function(config){
38947 Roo.menu.Menu.superclass.constructor.call(this, config);
38949 this.id = this.id || Roo.id();
38952 * @event beforeshow
38953 * Fires before this menu is displayed
38954 * @param {Roo.menu.Menu} this
38958 * @event beforehide
38959 * Fires before this menu is hidden
38960 * @param {Roo.menu.Menu} this
38965 * Fires after this menu is displayed
38966 * @param {Roo.menu.Menu} this
38971 * Fires after this menu is hidden
38972 * @param {Roo.menu.Menu} this
38977 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38978 * @param {Roo.menu.Menu} this
38979 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38980 * @param {Roo.EventObject} e
38985 * Fires when the mouse is hovering over this menu
38986 * @param {Roo.menu.Menu} this
38987 * @param {Roo.EventObject} e
38988 * @param {Roo.menu.Item} menuItem The menu item that was clicked
38993 * Fires when the mouse exits this menu
38994 * @param {Roo.menu.Menu} this
38995 * @param {Roo.EventObject} e
38996 * @param {Roo.menu.Item} menuItem The menu item that was clicked
39001 * Fires when a menu item contained in this menu is clicked
39002 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39003 * @param {Roo.EventObject} e
39007 if (this.registerMenu) {
39008 Roo.menu.MenuMgr.register(this);
39011 var mis = this.items;
39012 this.items = new Roo.util.MixedCollection();
39014 this.add.apply(this, mis);
39018 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39020 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39024 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39025 * for bottom-right shadow (defaults to "sides")
39029 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39030 * this menu (defaults to "tl-tr?")
39032 subMenuAlign : "tl-tr?",
39034 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39035 * relative to its element of origin (defaults to "tl-bl?")
39037 defaultAlign : "tl-bl?",
39039 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39041 allowOtherMenus : false,
39043 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39045 registerMenu : true,
39050 render : function(){
39054 var el = this.el = new Roo.Layer({
39056 shadow:this.shadow,
39058 parentEl: this.parentEl || document.body,
39062 this.keyNav = new Roo.menu.MenuNav(this);
39065 el.addClass("x-menu-plain");
39068 el.addClass(this.cls);
39070 // generic focus element
39071 this.focusEl = el.createChild({
39072 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39074 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39075 //disabling touch- as it's causing issues ..
39076 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
39077 ul.on('click' , this.onClick, this);
39080 ul.on("mouseover", this.onMouseOver, this);
39081 ul.on("mouseout", this.onMouseOut, this);
39082 this.items.each(function(item){
39087 var li = document.createElement("li");
39088 li.className = "x-menu-list-item";
39089 ul.dom.appendChild(li);
39090 item.render(li, this);
39097 autoWidth : function(){
39098 var el = this.el, ul = this.ul;
39102 var w = this.width;
39105 }else if(Roo.isIE){
39106 el.setWidth(this.minWidth);
39107 var t = el.dom.offsetWidth; // force recalc
39108 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39113 delayAutoWidth : function(){
39116 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39118 this.awTask.delay(20);
39123 findTargetItem : function(e){
39124 var t = e.getTarget(".x-menu-list-item", this.ul, true);
39125 if(t && t.menuItemId){
39126 return this.items.get(t.menuItemId);
39131 onClick : function(e){
39132 Roo.log("menu.onClick");
39133 var t = this.findTargetItem(e);
39138 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
39139 if(t == this.activeItem && t.shouldDeactivate(e)){
39140 this.activeItem.deactivate();
39141 delete this.activeItem;
39145 this.setActiveItem(t, true);
39153 this.fireEvent("click", this, t, e);
39157 setActiveItem : function(item, autoExpand){
39158 if(item != this.activeItem){
39159 if(this.activeItem){
39160 this.activeItem.deactivate();
39162 this.activeItem = item;
39163 item.activate(autoExpand);
39164 }else if(autoExpand){
39170 tryActivate : function(start, step){
39171 var items = this.items;
39172 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39173 var item = items.get(i);
39174 if(!item.disabled && item.canActivate){
39175 this.setActiveItem(item, false);
39183 onMouseOver : function(e){
39185 if(t = this.findTargetItem(e)){
39186 if(t.canActivate && !t.disabled){
39187 this.setActiveItem(t, true);
39190 this.fireEvent("mouseover", this, e, t);
39194 onMouseOut : function(e){
39196 if(t = this.findTargetItem(e)){
39197 if(t == this.activeItem && t.shouldDeactivate(e)){
39198 this.activeItem.deactivate();
39199 delete this.activeItem;
39202 this.fireEvent("mouseout", this, e, t);
39206 * Read-only. Returns true if the menu is currently displayed, else false.
39209 isVisible : function(){
39210 return this.el && !this.hidden;
39214 * Displays this menu relative to another element
39215 * @param {String/HTMLElement/Roo.Element} element The element to align to
39216 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39217 * the element (defaults to this.defaultAlign)
39218 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39220 show : function(el, pos, parentMenu){
39221 this.parentMenu = parentMenu;
39225 this.fireEvent("beforeshow", this);
39226 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39230 * Displays this menu at a specific xy position
39231 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39232 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39234 showAt : function(xy, parentMenu, /* private: */_e){
39235 this.parentMenu = parentMenu;
39240 this.fireEvent("beforeshow", this);
39241 xy = this.el.adjustForConstraints(xy);
39245 this.hidden = false;
39247 this.fireEvent("show", this);
39250 focus : function(){
39252 this.doFocus.defer(50, this);
39256 doFocus : function(){
39258 this.focusEl.focus();
39263 * Hides this menu and optionally all parent menus
39264 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39266 hide : function(deep){
39267 if(this.el && this.isVisible()){
39268 this.fireEvent("beforehide", this);
39269 if(this.activeItem){
39270 this.activeItem.deactivate();
39271 this.activeItem = null;
39274 this.hidden = true;
39275 this.fireEvent("hide", this);
39277 if(deep === true && this.parentMenu){
39278 this.parentMenu.hide(true);
39283 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39284 * Any of the following are valid:
39286 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39287 * <li>An HTMLElement object which will be converted to a menu item</li>
39288 * <li>A menu item config object that will be created as a new menu item</li>
39289 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39290 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39295 var menu = new Roo.menu.Menu();
39297 // Create a menu item to add by reference
39298 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39300 // Add a bunch of items at once using different methods.
39301 // Only the last item added will be returned.
39302 var item = menu.add(
39303 menuItem, // add existing item by ref
39304 'Dynamic Item', // new TextItem
39305 '-', // new separator
39306 { text: 'Config Item' } // new item by config
39309 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39310 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39313 var a = arguments, l = a.length, item;
39314 for(var i = 0; i < l; i++){
39316 if ((typeof(el) == "object") && el.xtype && el.xns) {
39317 el = Roo.factory(el, Roo.menu);
39320 if(el.render){ // some kind of Item
39321 item = this.addItem(el);
39322 }else if(typeof el == "string"){ // string
39323 if(el == "separator" || el == "-"){
39324 item = this.addSeparator();
39326 item = this.addText(el);
39328 }else if(el.tagName || el.el){ // element
39329 item = this.addElement(el);
39330 }else if(typeof el == "object"){ // must be menu item config?
39331 item = this.addMenuItem(el);
39338 * Returns this menu's underlying {@link Roo.Element} object
39339 * @return {Roo.Element} The element
39341 getEl : function(){
39349 * Adds a separator bar to the menu
39350 * @return {Roo.menu.Item} The menu item that was added
39352 addSeparator : function(){
39353 return this.addItem(new Roo.menu.Separator());
39357 * Adds an {@link Roo.Element} object to the menu
39358 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39359 * @return {Roo.menu.Item} The menu item that was added
39361 addElement : function(el){
39362 return this.addItem(new Roo.menu.BaseItem(el));
39366 * Adds an existing object based on {@link Roo.menu.Item} to the menu
39367 * @param {Roo.menu.Item} item The menu item to add
39368 * @return {Roo.menu.Item} The menu item that was added
39370 addItem : function(item){
39371 this.items.add(item);
39373 var li = document.createElement("li");
39374 li.className = "x-menu-list-item";
39375 this.ul.dom.appendChild(li);
39376 item.render(li, this);
39377 this.delayAutoWidth();
39383 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39384 * @param {Object} config A MenuItem config object
39385 * @return {Roo.menu.Item} The menu item that was added
39387 addMenuItem : function(config){
39388 if(!(config instanceof Roo.menu.Item)){
39389 if(typeof config.checked == "boolean"){ // must be check menu item config?
39390 config = new Roo.menu.CheckItem(config);
39392 config = new Roo.menu.Item(config);
39395 return this.addItem(config);
39399 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39400 * @param {String} text The text to display in the menu item
39401 * @return {Roo.menu.Item} The menu item that was added
39403 addText : function(text){
39404 return this.addItem(new Roo.menu.TextItem({ text : text }));
39408 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39409 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39410 * @param {Roo.menu.Item} item The menu item to add
39411 * @return {Roo.menu.Item} The menu item that was added
39413 insert : function(index, item){
39414 this.items.insert(index, item);
39416 var li = document.createElement("li");
39417 li.className = "x-menu-list-item";
39418 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39419 item.render(li, this);
39420 this.delayAutoWidth();
39426 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39427 * @param {Roo.menu.Item} item The menu item to remove
39429 remove : function(item){
39430 this.items.removeKey(item.id);
39435 * Removes and destroys all items in the menu
39437 removeAll : function(){
39439 while(f = this.items.first()){
39445 // MenuNav is a private utility class used internally by the Menu
39446 Roo.menu.MenuNav = function(menu){
39447 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39448 this.scope = this.menu = menu;
39451 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39452 doRelay : function(e, h){
39453 var k = e.getKey();
39454 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39455 this.menu.tryActivate(0, 1);
39458 return h.call(this.scope || this, e, this.menu);
39461 up : function(e, m){
39462 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39463 m.tryActivate(m.items.length-1, -1);
39467 down : function(e, m){
39468 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39469 m.tryActivate(0, 1);
39473 right : function(e, m){
39475 m.activeItem.expandMenu(true);
39479 left : function(e, m){
39481 if(m.parentMenu && m.parentMenu.activeItem){
39482 m.parentMenu.activeItem.activate();
39486 enter : function(e, m){
39488 e.stopPropagation();
39489 m.activeItem.onClick(e);
39490 m.fireEvent("click", this, m.activeItem);
39496 * Ext JS Library 1.1.1
39497 * Copyright(c) 2006-2007, Ext JS, LLC.
39499 * Originally Released Under LGPL - original licence link has changed is not relivant.
39502 * <script type="text/javascript">
39506 * @class Roo.menu.MenuMgr
39507 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39510 Roo.menu.MenuMgr = function(){
39511 var menus, active, groups = {}, attached = false, lastShow = new Date();
39513 // private - called when first menu is created
39516 active = new Roo.util.MixedCollection();
39517 Roo.get(document).addKeyListener(27, function(){
39518 if(active.length > 0){
39525 function hideAll(){
39526 if(active && active.length > 0){
39527 var c = active.clone();
39528 c.each(function(m){
39535 function onHide(m){
39537 if(active.length < 1){
39538 Roo.get(document).un("mousedown", onMouseDown);
39544 function onShow(m){
39545 var last = active.last();
39546 lastShow = new Date();
39549 Roo.get(document).on("mousedown", onMouseDown);
39553 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39554 m.parentMenu.activeChild = m;
39555 }else if(last && last.isVisible()){
39556 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39561 function onBeforeHide(m){
39563 m.activeChild.hide();
39565 if(m.autoHideTimer){
39566 clearTimeout(m.autoHideTimer);
39567 delete m.autoHideTimer;
39572 function onBeforeShow(m){
39573 var pm = m.parentMenu;
39574 if(!pm && !m.allowOtherMenus){
39576 }else if(pm && pm.activeChild && active != m){
39577 pm.activeChild.hide();
39582 function onMouseDown(e){
39583 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39589 function onBeforeCheck(mi, state){
39591 var g = groups[mi.group];
39592 for(var i = 0, l = g.length; i < l; i++){
39594 g[i].setChecked(false);
39603 * Hides all menus that are currently visible
39605 hideAll : function(){
39610 register : function(menu){
39614 menus[menu.id] = menu;
39615 menu.on("beforehide", onBeforeHide);
39616 menu.on("hide", onHide);
39617 menu.on("beforeshow", onBeforeShow);
39618 menu.on("show", onShow);
39619 var g = menu.group;
39620 if(g && menu.events["checkchange"]){
39624 groups[g].push(menu);
39625 menu.on("checkchange", onCheck);
39630 * Returns a {@link Roo.menu.Menu} object
39631 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39632 * be used to generate and return a new Menu instance.
39634 get : function(menu){
39635 if(typeof menu == "string"){ // menu id
39636 return menus[menu];
39637 }else if(menu.events){ // menu instance
39639 }else if(typeof menu.length == 'number'){ // array of menu items?
39640 return new Roo.menu.Menu({items:menu});
39641 }else{ // otherwise, must be a config
39642 return new Roo.menu.Menu(menu);
39647 unregister : function(menu){
39648 delete menus[menu.id];
39649 menu.un("beforehide", onBeforeHide);
39650 menu.un("hide", onHide);
39651 menu.un("beforeshow", onBeforeShow);
39652 menu.un("show", onShow);
39653 var g = menu.group;
39654 if(g && menu.events["checkchange"]){
39655 groups[g].remove(menu);
39656 menu.un("checkchange", onCheck);
39661 registerCheckable : function(menuItem){
39662 var g = menuItem.group;
39667 groups[g].push(menuItem);
39668 menuItem.on("beforecheckchange", onBeforeCheck);
39673 unregisterCheckable : function(menuItem){
39674 var g = menuItem.group;
39676 groups[g].remove(menuItem);
39677 menuItem.un("beforecheckchange", onBeforeCheck);
39683 * Ext JS Library 1.1.1
39684 * Copyright(c) 2006-2007, Ext JS, LLC.
39686 * Originally Released Under LGPL - original licence link has changed is not relivant.
39689 * <script type="text/javascript">
39694 * @class Roo.menu.BaseItem
39695 * @extends Roo.Component
39697 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
39698 * management and base configuration options shared by all menu components.
39700 * Creates a new BaseItem
39701 * @param {Object} config Configuration options
39703 Roo.menu.BaseItem = function(config){
39704 Roo.menu.BaseItem.superclass.constructor.call(this, config);
39709 * Fires when this item is clicked
39710 * @param {Roo.menu.BaseItem} this
39711 * @param {Roo.EventObject} e
39716 * Fires when this item is activated
39717 * @param {Roo.menu.BaseItem} this
39721 * @event deactivate
39722 * Fires when this item is deactivated
39723 * @param {Roo.menu.BaseItem} this
39729 this.on("click", this.handler, this.scope, true);
39733 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39735 * @cfg {Function} handler
39736 * A function that will handle the click event of this menu item (defaults to undefined)
39739 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39741 canActivate : false,
39744 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39749 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39751 activeClass : "x-menu-item-active",
39753 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39755 hideOnClick : true,
39757 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39762 ctype: "Roo.menu.BaseItem",
39765 actionMode : "container",
39768 render : function(container, parentMenu){
39769 this.parentMenu = parentMenu;
39770 Roo.menu.BaseItem.superclass.render.call(this, container);
39771 this.container.menuItemId = this.id;
39775 onRender : function(container, position){
39776 this.el = Roo.get(this.el);
39777 container.dom.appendChild(this.el.dom);
39781 onClick : function(e){
39782 if(!this.disabled && this.fireEvent("click", this, e) !== false
39783 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39784 this.handleClick(e);
39791 activate : function(){
39795 var li = this.container;
39796 li.addClass(this.activeClass);
39797 this.region = li.getRegion().adjust(2, 2, -2, -2);
39798 this.fireEvent("activate", this);
39803 deactivate : function(){
39804 this.container.removeClass(this.activeClass);
39805 this.fireEvent("deactivate", this);
39809 shouldDeactivate : function(e){
39810 return !this.region || !this.region.contains(e.getPoint());
39814 handleClick : function(e){
39815 if(this.hideOnClick){
39816 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39821 expandMenu : function(autoActivate){
39826 hideMenu : function(){
39831 * Ext JS Library 1.1.1
39832 * Copyright(c) 2006-2007, Ext JS, LLC.
39834 * Originally Released Under LGPL - original licence link has changed is not relivant.
39837 * <script type="text/javascript">
39841 * @class Roo.menu.Adapter
39842 * @extends Roo.menu.BaseItem
39844 * 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.
39845 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39847 * Creates a new Adapter
39848 * @param {Object} config Configuration options
39850 Roo.menu.Adapter = function(component, config){
39851 Roo.menu.Adapter.superclass.constructor.call(this, config);
39852 this.component = component;
39854 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39856 canActivate : true,
39859 onRender : function(container, position){
39860 this.component.render(container);
39861 this.el = this.component.getEl();
39865 activate : function(){
39869 this.component.focus();
39870 this.fireEvent("activate", this);
39875 deactivate : function(){
39876 this.fireEvent("deactivate", this);
39880 disable : function(){
39881 this.component.disable();
39882 Roo.menu.Adapter.superclass.disable.call(this);
39886 enable : function(){
39887 this.component.enable();
39888 Roo.menu.Adapter.superclass.enable.call(this);
39892 * Ext JS Library 1.1.1
39893 * Copyright(c) 2006-2007, Ext JS, LLC.
39895 * Originally Released Under LGPL - original licence link has changed is not relivant.
39898 * <script type="text/javascript">
39902 * @class Roo.menu.TextItem
39903 * @extends Roo.menu.BaseItem
39904 * Adds a static text string to a menu, usually used as either a heading or group separator.
39905 * Note: old style constructor with text is still supported.
39908 * Creates a new TextItem
39909 * @param {Object} cfg Configuration
39911 Roo.menu.TextItem = function(cfg){
39912 if (typeof(cfg) == 'string') {
39915 Roo.apply(this,cfg);
39918 Roo.menu.TextItem.superclass.constructor.call(this);
39921 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39923 * @cfg {String} text Text to show on item.
39928 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39930 hideOnClick : false,
39932 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39934 itemCls : "x-menu-text",
39937 onRender : function(){
39938 var s = document.createElement("span");
39939 s.className = this.itemCls;
39940 s.innerHTML = this.text;
39942 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39946 * Ext JS Library 1.1.1
39947 * Copyright(c) 2006-2007, Ext JS, LLC.
39949 * Originally Released Under LGPL - original licence link has changed is not relivant.
39952 * <script type="text/javascript">
39956 * @class Roo.menu.Separator
39957 * @extends Roo.menu.BaseItem
39958 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39959 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39961 * @param {Object} config Configuration options
39963 Roo.menu.Separator = function(config){
39964 Roo.menu.Separator.superclass.constructor.call(this, config);
39967 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39969 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39971 itemCls : "x-menu-sep",
39973 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39975 hideOnClick : false,
39978 onRender : function(li){
39979 var s = document.createElement("span");
39980 s.className = this.itemCls;
39981 s.innerHTML = " ";
39983 li.addClass("x-menu-sep-li");
39984 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39988 * Ext JS Library 1.1.1
39989 * Copyright(c) 2006-2007, Ext JS, LLC.
39991 * Originally Released Under LGPL - original licence link has changed is not relivant.
39994 * <script type="text/javascript">
39997 * @class Roo.menu.Item
39998 * @extends Roo.menu.BaseItem
39999 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40000 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40001 * activation and click handling.
40003 * Creates a new Item
40004 * @param {Object} config Configuration options
40006 Roo.menu.Item = function(config){
40007 Roo.menu.Item.superclass.constructor.call(this, config);
40009 this.menu = Roo.menu.MenuMgr.get(this.menu);
40012 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40014 * @cfg {Roo.menu.Menu} menu
40018 * @cfg {String} text
40019 * The text to show on the menu item.
40023 * @cfg {String} html to render in menu
40024 * The text to show on the menu item (HTML version).
40028 * @cfg {String} icon
40029 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40033 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40035 itemCls : "x-menu-item",
40037 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40039 canActivate : true,
40041 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40044 // doc'd in BaseItem
40048 ctype: "Roo.menu.Item",
40051 onRender : function(container, position){
40052 var el = document.createElement("a");
40053 el.hideFocus = true;
40054 el.unselectable = "on";
40055 el.href = this.href || "#";
40056 if(this.hrefTarget){
40057 el.target = this.hrefTarget;
40059 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
40061 var html = this.html.length ? this.html : String.format('{0}',this.text);
40063 el.innerHTML = String.format(
40064 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40065 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40067 Roo.menu.Item.superclass.onRender.call(this, container, position);
40071 * Sets the text to display in this menu item
40072 * @param {String} text The text to display
40073 * @param {Boolean} isHTML true to indicate text is pure html.
40075 setText : function(text, isHTML){
40083 var html = this.html.length ? this.html : String.format('{0}',this.text);
40085 this.el.update(String.format(
40086 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40087 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40088 this.parentMenu.autoWidth();
40093 handleClick : function(e){
40094 if(!this.href){ // if no link defined, stop the event automatically
40097 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40101 activate : function(autoExpand){
40102 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40112 shouldDeactivate : function(e){
40113 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40114 if(this.menu && this.menu.isVisible()){
40115 return !this.menu.getEl().getRegion().contains(e.getPoint());
40123 deactivate : function(){
40124 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40129 expandMenu : function(autoActivate){
40130 if(!this.disabled && this.menu){
40131 clearTimeout(this.hideTimer);
40132 delete this.hideTimer;
40133 if(!this.menu.isVisible() && !this.showTimer){
40134 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40135 }else if (this.menu.isVisible() && autoActivate){
40136 this.menu.tryActivate(0, 1);
40142 deferExpand : function(autoActivate){
40143 delete this.showTimer;
40144 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40146 this.menu.tryActivate(0, 1);
40151 hideMenu : function(){
40152 clearTimeout(this.showTimer);
40153 delete this.showTimer;
40154 if(!this.hideTimer && this.menu && this.menu.isVisible()){
40155 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40160 deferHide : function(){
40161 delete this.hideTimer;
40166 * Ext JS Library 1.1.1
40167 * Copyright(c) 2006-2007, Ext JS, LLC.
40169 * Originally Released Under LGPL - original licence link has changed is not relivant.
40172 * <script type="text/javascript">
40176 * @class Roo.menu.CheckItem
40177 * @extends Roo.menu.Item
40178 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40180 * Creates a new CheckItem
40181 * @param {Object} config Configuration options
40183 Roo.menu.CheckItem = function(config){
40184 Roo.menu.CheckItem.superclass.constructor.call(this, config);
40187 * @event beforecheckchange
40188 * Fires before the checked value is set, providing an opportunity to cancel if needed
40189 * @param {Roo.menu.CheckItem} this
40190 * @param {Boolean} checked The new checked value that will be set
40192 "beforecheckchange" : true,
40194 * @event checkchange
40195 * Fires after the checked value has been set
40196 * @param {Roo.menu.CheckItem} this
40197 * @param {Boolean} checked The checked value that was set
40199 "checkchange" : true
40201 if(this.checkHandler){
40202 this.on('checkchange', this.checkHandler, this.scope);
40205 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40207 * @cfg {String} group
40208 * All check items with the same group name will automatically be grouped into a single-select
40209 * radio button group (defaults to '')
40212 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40214 itemCls : "x-menu-item x-menu-check-item",
40216 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40218 groupClass : "x-menu-group-item",
40221 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
40222 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40223 * initialized with checked = true will be rendered as checked.
40228 ctype: "Roo.menu.CheckItem",
40231 onRender : function(c){
40232 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40234 this.el.addClass(this.groupClass);
40236 Roo.menu.MenuMgr.registerCheckable(this);
40238 this.checked = false;
40239 this.setChecked(true, true);
40244 destroy : function(){
40246 Roo.menu.MenuMgr.unregisterCheckable(this);
40248 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40252 * Set the checked state of this item
40253 * @param {Boolean} checked The new checked value
40254 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40256 setChecked : function(state, suppressEvent){
40257 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40258 if(this.container){
40259 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40261 this.checked = state;
40262 if(suppressEvent !== true){
40263 this.fireEvent("checkchange", this, state);
40269 handleClick : function(e){
40270 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40271 this.setChecked(!this.checked);
40273 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40277 * Ext JS Library 1.1.1
40278 * Copyright(c) 2006-2007, Ext JS, LLC.
40280 * Originally Released Under LGPL - original licence link has changed is not relivant.
40283 * <script type="text/javascript">
40287 * @class Roo.menu.DateItem
40288 * @extends Roo.menu.Adapter
40289 * A menu item that wraps the {@link Roo.DatPicker} component.
40291 * Creates a new DateItem
40292 * @param {Object} config Configuration options
40294 Roo.menu.DateItem = function(config){
40295 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40296 /** The Roo.DatePicker object @type Roo.DatePicker */
40297 this.picker = this.component;
40298 this.addEvents({select: true});
40300 this.picker.on("render", function(picker){
40301 picker.getEl().swallowEvent("click");
40302 picker.container.addClass("x-menu-date-item");
40305 this.picker.on("select", this.onSelect, this);
40308 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40310 onSelect : function(picker, date){
40311 this.fireEvent("select", this, date, picker);
40312 Roo.menu.DateItem.superclass.handleClick.call(this);
40316 * Ext JS Library 1.1.1
40317 * Copyright(c) 2006-2007, Ext JS, LLC.
40319 * Originally Released Under LGPL - original licence link has changed is not relivant.
40322 * <script type="text/javascript">
40326 * @class Roo.menu.ColorItem
40327 * @extends Roo.menu.Adapter
40328 * A menu item that wraps the {@link Roo.ColorPalette} component.
40330 * Creates a new ColorItem
40331 * @param {Object} config Configuration options
40333 Roo.menu.ColorItem = function(config){
40334 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40335 /** The Roo.ColorPalette object @type Roo.ColorPalette */
40336 this.palette = this.component;
40337 this.relayEvents(this.palette, ["select"]);
40338 if(this.selectHandler){
40339 this.on('select', this.selectHandler, this.scope);
40342 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40344 * Ext JS Library 1.1.1
40345 * Copyright(c) 2006-2007, Ext JS, LLC.
40347 * Originally Released Under LGPL - original licence link has changed is not relivant.
40350 * <script type="text/javascript">
40355 * @class Roo.menu.DateMenu
40356 * @extends Roo.menu.Menu
40357 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40359 * Creates a new DateMenu
40360 * @param {Object} config Configuration options
40362 Roo.menu.DateMenu = function(config){
40363 Roo.menu.DateMenu.superclass.constructor.call(this, config);
40365 var di = new Roo.menu.DateItem(config);
40368 * The {@link Roo.DatePicker} instance for this DateMenu
40371 this.picker = di.picker;
40374 * @param {DatePicker} picker
40375 * @param {Date} date
40377 this.relayEvents(di, ["select"]);
40378 this.on('beforeshow', function(){
40380 this.picker.hideMonthPicker(false);
40384 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40388 * Ext JS Library 1.1.1
40389 * Copyright(c) 2006-2007, Ext JS, LLC.
40391 * Originally Released Under LGPL - original licence link has changed is not relivant.
40394 * <script type="text/javascript">
40399 * @class Roo.menu.ColorMenu
40400 * @extends Roo.menu.Menu
40401 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40403 * Creates a new ColorMenu
40404 * @param {Object} config Configuration options
40406 Roo.menu.ColorMenu = function(config){
40407 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40409 var ci = new Roo.menu.ColorItem(config);
40412 * The {@link Roo.ColorPalette} instance for this ColorMenu
40413 * @type ColorPalette
40415 this.palette = ci.palette;
40418 * @param {ColorPalette} palette
40419 * @param {String} color
40421 this.relayEvents(ci, ["select"]);
40423 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40425 * Ext JS Library 1.1.1
40426 * Copyright(c) 2006-2007, Ext JS, LLC.
40428 * Originally Released Under LGPL - original licence link has changed is not relivant.
40431 * <script type="text/javascript">
40435 * @class Roo.form.TextItem
40436 * @extends Roo.BoxComponent
40437 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40439 * Creates a new TextItem
40440 * @param {Object} config Configuration options
40442 Roo.form.TextItem = function(config){
40443 Roo.form.TextItem.superclass.constructor.call(this, config);
40446 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
40449 * @cfg {String} tag the tag for this item (default div)
40453 * @cfg {String} html the content for this item
40457 getAutoCreate : function()
40470 onRender : function(ct, position)
40472 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40475 var cfg = this.getAutoCreate();
40477 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40479 if (!cfg.name.length) {
40482 this.el = ct.createChild(cfg, position);
40487 * @param {String} html update the Contents of the element.
40489 setHTML : function(html)
40491 this.fieldEl.dom.innerHTML = html;
40496 * Ext JS Library 1.1.1
40497 * Copyright(c) 2006-2007, Ext JS, LLC.
40499 * Originally Released Under LGPL - original licence link has changed is not relivant.
40502 * <script type="text/javascript">
40506 * @class Roo.form.Field
40507 * @extends Roo.BoxComponent
40508 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40510 * Creates a new Field
40511 * @param {Object} config Configuration options
40513 Roo.form.Field = function(config){
40514 Roo.form.Field.superclass.constructor.call(this, config);
40517 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
40519 * @cfg {String} fieldLabel Label to use when rendering a form.
40522 * @cfg {String} qtip Mouse over tip
40526 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40528 invalidClass : "x-form-invalid",
40530 * @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")
40532 invalidText : "The value in this field is invalid",
40534 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40536 focusClass : "x-form-focus",
40538 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40539 automatic validation (defaults to "keyup").
40541 validationEvent : "keyup",
40543 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40545 validateOnBlur : true,
40547 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40549 validationDelay : 250,
40551 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40552 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40554 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40556 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40558 fieldClass : "x-form-field",
40560 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
40563 ----------- ----------------------------------------------------------------------
40564 qtip Display a quick tip when the user hovers over the field
40565 title Display a default browser title attribute popup
40566 under Add a block div beneath the field containing the error text
40567 side Add an error icon to the right of the field with a popup on hover
40568 [element id] Add the error text directly to the innerHTML of the specified element
40571 msgTarget : 'qtip',
40573 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40578 * @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.
40583 * @cfg {Boolean} disabled True to disable the field (defaults to false).
40588 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40590 inputType : undefined,
40593 * @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).
40595 tabIndex : undefined,
40598 isFormField : true,
40603 * @property {Roo.Element} fieldEl
40604 * Element Containing the rendered Field (with label etc.)
40607 * @cfg {Mixed} value A value to initialize this field with.
40612 * @cfg {String} name The field's HTML name attribute.
40615 * @cfg {String} cls A CSS class to apply to the field's underlying element.
40618 loadedValue : false,
40622 initComponent : function(){
40623 Roo.form.Field.superclass.initComponent.call(this);
40627 * Fires when this field receives input focus.
40628 * @param {Roo.form.Field} this
40633 * Fires when this field loses input focus.
40634 * @param {Roo.form.Field} this
40638 * @event specialkey
40639 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
40640 * {@link Roo.EventObject#getKey} to determine which key was pressed.
40641 * @param {Roo.form.Field} this
40642 * @param {Roo.EventObject} e The event object
40647 * Fires just before the field blurs if the field value has changed.
40648 * @param {Roo.form.Field} this
40649 * @param {Mixed} newValue The new value
40650 * @param {Mixed} oldValue The original value
40655 * Fires after the field has been marked as invalid.
40656 * @param {Roo.form.Field} this
40657 * @param {String} msg The validation message
40662 * Fires after the field has been validated with no errors.
40663 * @param {Roo.form.Field} this
40668 * Fires after the key up
40669 * @param {Roo.form.Field} this
40670 * @param {Roo.EventObject} e The event Object
40677 * Returns the name attribute of the field if available
40678 * @return {String} name The field name
40680 getName: function(){
40681 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40685 onRender : function(ct, position){
40686 Roo.form.Field.superclass.onRender.call(this, ct, position);
40688 var cfg = this.getAutoCreate();
40690 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40692 if (!cfg.name.length) {
40695 if(this.inputType){
40696 cfg.type = this.inputType;
40698 this.el = ct.createChild(cfg, position);
40700 var type = this.el.dom.type;
40702 if(type == 'password'){
40705 this.el.addClass('x-form-'+type);
40708 this.el.dom.readOnly = true;
40710 if(this.tabIndex !== undefined){
40711 this.el.dom.setAttribute('tabIndex', this.tabIndex);
40714 this.el.addClass([this.fieldClass, this.cls]);
40719 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40720 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40721 * @return {Roo.form.Field} this
40723 applyTo : function(target){
40724 this.allowDomMove = false;
40725 this.el = Roo.get(target);
40726 this.render(this.el.dom.parentNode);
40731 initValue : function(){
40732 if(this.value !== undefined){
40733 this.setValue(this.value);
40734 }else if(this.el.dom.value.length > 0){
40735 this.setValue(this.el.dom.value);
40740 * Returns true if this field has been changed since it was originally loaded and is not disabled.
40741 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
40743 isDirty : function() {
40744 if(this.disabled) {
40747 return String(this.getValue()) !== String(this.originalValue);
40751 * stores the current value in loadedValue
40753 resetHasChanged : function()
40755 this.loadedValue = String(this.getValue());
40758 * checks the current value against the 'loaded' value.
40759 * Note - will return false if 'resetHasChanged' has not been called first.
40761 hasChanged : function()
40763 if(this.disabled || this.readOnly) {
40766 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40772 afterRender : function(){
40773 Roo.form.Field.superclass.afterRender.call(this);
40778 fireKey : function(e){
40779 //Roo.log('field ' + e.getKey());
40780 if(e.isNavKeyPress()){
40781 this.fireEvent("specialkey", this, e);
40786 * Resets the current field value to the originally loaded value and clears any validation messages
40788 reset : function(){
40789 this.setValue(this.resetValue);
40790 this.originalValue = this.getValue();
40791 this.clearInvalid();
40795 initEvents : function(){
40796 // safari killled keypress - so keydown is now used..
40797 this.el.on("keydown" , this.fireKey, this);
40798 this.el.on("focus", this.onFocus, this);
40799 this.el.on("blur", this.onBlur, this);
40800 this.el.relayEvent('keyup', this);
40802 // reference to original value for reset
40803 this.originalValue = this.getValue();
40804 this.resetValue = this.getValue();
40808 onFocus : function(){
40809 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40810 this.el.addClass(this.focusClass);
40812 if(!this.hasFocus){
40813 this.hasFocus = true;
40814 this.startValue = this.getValue();
40815 this.fireEvent("focus", this);
40819 beforeBlur : Roo.emptyFn,
40822 onBlur : function(){
40824 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40825 this.el.removeClass(this.focusClass);
40827 this.hasFocus = false;
40828 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40831 var v = this.getValue();
40832 if(String(v) !== String(this.startValue)){
40833 this.fireEvent('change', this, v, this.startValue);
40835 this.fireEvent("blur", this);
40839 * Returns whether or not the field value is currently valid
40840 * @param {Boolean} preventMark True to disable marking the field invalid
40841 * @return {Boolean} True if the value is valid, else false
40843 isValid : function(preventMark){
40847 var restore = this.preventMark;
40848 this.preventMark = preventMark === true;
40849 var v = this.validateValue(this.processValue(this.getRawValue()));
40850 this.preventMark = restore;
40855 * Validates the field value
40856 * @return {Boolean} True if the value is valid, else false
40858 validate : function(){
40859 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40860 this.clearInvalid();
40866 processValue : function(value){
40871 // Subclasses should provide the validation implementation by overriding this
40872 validateValue : function(value){
40877 * Mark this field as invalid
40878 * @param {String} msg The validation message
40880 markInvalid : function(msg){
40881 if(!this.rendered || this.preventMark){ // not rendered
40885 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40887 obj.el.addClass(this.invalidClass);
40888 msg = msg || this.invalidText;
40889 switch(this.msgTarget){
40891 obj.el.dom.qtip = msg;
40892 obj.el.dom.qclass = 'x-form-invalid-tip';
40893 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40894 Roo.QuickTips.enable();
40898 this.el.dom.title = msg;
40902 var elp = this.el.findParent('.x-form-element', 5, true);
40903 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40904 this.errorEl.setWidth(elp.getWidth(true)-20);
40906 this.errorEl.update(msg);
40907 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40910 if(!this.errorIcon){
40911 var elp = this.el.findParent('.x-form-element', 5, true);
40912 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40914 this.alignErrorIcon();
40915 this.errorIcon.dom.qtip = msg;
40916 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40917 this.errorIcon.show();
40918 this.on('resize', this.alignErrorIcon, this);
40921 var t = Roo.getDom(this.msgTarget);
40923 t.style.display = this.msgDisplay;
40926 this.fireEvent('invalid', this, msg);
40930 alignErrorIcon : function(){
40931 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40935 * Clear any invalid styles/messages for this field
40937 clearInvalid : function(){
40938 if(!this.rendered || this.preventMark){ // not rendered
40941 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40943 obj.el.removeClass(this.invalidClass);
40944 switch(this.msgTarget){
40946 obj.el.dom.qtip = '';
40949 this.el.dom.title = '';
40953 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40957 if(this.errorIcon){
40958 this.errorIcon.dom.qtip = '';
40959 this.errorIcon.hide();
40960 this.un('resize', this.alignErrorIcon, this);
40964 var t = Roo.getDom(this.msgTarget);
40966 t.style.display = 'none';
40969 this.fireEvent('valid', this);
40973 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
40974 * @return {Mixed} value The field value
40976 getRawValue : function(){
40977 var v = this.el.getValue();
40983 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
40984 * @return {Mixed} value The field value
40986 getValue : function(){
40987 var v = this.el.getValue();
40993 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
40994 * @param {Mixed} value The value to set
40996 setRawValue : function(v){
40997 return this.el.dom.value = (v === null || v === undefined ? '' : v);
41001 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
41002 * @param {Mixed} value The value to set
41004 setValue : function(v){
41007 this.el.dom.value = (v === null || v === undefined ? '' : v);
41012 adjustSize : function(w, h){
41013 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41014 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41018 adjustWidth : function(tag, w){
41019 tag = tag.toLowerCase();
41020 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41021 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41022 if(tag == 'input'){
41025 if(tag == 'textarea'){
41028 }else if(Roo.isOpera){
41029 if(tag == 'input'){
41032 if(tag == 'textarea'){
41042 // anything other than normal should be considered experimental
41043 Roo.form.Field.msgFx = {
41045 show: function(msgEl, f){
41046 msgEl.setDisplayed('block');
41049 hide : function(msgEl, f){
41050 msgEl.setDisplayed(false).update('');
41055 show: function(msgEl, f){
41056 msgEl.slideIn('t', {stopFx:true});
41059 hide : function(msgEl, f){
41060 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41065 show: function(msgEl, f){
41066 msgEl.fixDisplay();
41067 msgEl.alignTo(f.el, 'tl-tr');
41068 msgEl.slideIn('l', {stopFx:true});
41071 hide : function(msgEl, f){
41072 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41077 * Ext JS Library 1.1.1
41078 * Copyright(c) 2006-2007, Ext JS, LLC.
41080 * Originally Released Under LGPL - original licence link has changed is not relivant.
41083 * <script type="text/javascript">
41088 * @class Roo.form.TextField
41089 * @extends Roo.form.Field
41090 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
41091 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41093 * Creates a new TextField
41094 * @param {Object} config Configuration options
41096 Roo.form.TextField = function(config){
41097 Roo.form.TextField.superclass.constructor.call(this, config);
41101 * Fires when the autosize function is triggered. The field may or may not have actually changed size
41102 * according to the default logic, but this event provides a hook for the developer to apply additional
41103 * logic at runtime to resize the field if needed.
41104 * @param {Roo.form.Field} this This text field
41105 * @param {Number} width The new field width
41111 Roo.extend(Roo.form.TextField, Roo.form.Field, {
41113 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41117 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41121 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41125 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41129 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41133 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41135 disableKeyFilter : false,
41137 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41141 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41145 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41147 maxLength : Number.MAX_VALUE,
41149 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41151 minLengthText : "The minimum length for this field is {0}",
41153 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41155 maxLengthText : "The maximum length for this field is {0}",
41157 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41159 selectOnFocus : false,
41161 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
41163 allowLeadingSpace : false,
41165 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41167 blankText : "This field is required",
41169 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41170 * If available, this function will be called only after the basic validators all return true, and will be passed the
41171 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41175 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41176 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41177 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
41181 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41185 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41191 initEvents : function()
41193 if (this.emptyText) {
41194 this.el.attr('placeholder', this.emptyText);
41197 Roo.form.TextField.superclass.initEvents.call(this);
41198 if(this.validationEvent == 'keyup'){
41199 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41200 this.el.on('keyup', this.filterValidation, this);
41202 else if(this.validationEvent !== false){
41203 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41206 if(this.selectOnFocus){
41207 this.on("focus", this.preFocus, this);
41209 if (!this.allowLeadingSpace) {
41210 this.on('blur', this.cleanLeadingSpace, this);
41213 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41214 this.el.on("keypress", this.filterKeys, this);
41217 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
41218 this.el.on("click", this.autoSize, this);
41220 if(this.el.is('input[type=password]') && Roo.isSafari){
41221 this.el.on('keydown', this.SafariOnKeyDown, this);
41225 processValue : function(value){
41226 if(this.stripCharsRe){
41227 var newValue = value.replace(this.stripCharsRe, '');
41228 if(newValue !== value){
41229 this.setRawValue(newValue);
41236 filterValidation : function(e){
41237 if(!e.isNavKeyPress()){
41238 this.validationTask.delay(this.validationDelay);
41243 onKeyUp : function(e){
41244 if(!e.isNavKeyPress()){
41248 // private - clean the leading white space
41249 cleanLeadingSpace : function(e)
41251 if ( this.inputType == 'file') {
41255 this.setValue((this.getValue() + '').replace(/^\s+/,''));
41258 * Resets the current field value to the originally-loaded value and clears any validation messages.
41261 reset : function(){
41262 Roo.form.TextField.superclass.reset.call(this);
41266 preFocus : function(){
41268 if(this.selectOnFocus){
41269 this.el.dom.select();
41275 filterKeys : function(e){
41276 var k = e.getKey();
41277 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41280 var c = e.getCharCode(), cc = String.fromCharCode(c);
41281 if(Roo.isIE && (e.isSpecialKey() || !cc)){
41284 if(!this.maskRe.test(cc)){
41289 setValue : function(v){
41291 Roo.form.TextField.superclass.setValue.apply(this, arguments);
41297 * Validates a value according to the field's validation rules and marks the field as invalid
41298 * if the validation fails
41299 * @param {Mixed} value The value to validate
41300 * @return {Boolean} True if the value is valid, else false
41302 validateValue : function(value){
41303 if(value.length < 1) { // if it's blank
41304 if(this.allowBlank){
41305 this.clearInvalid();
41308 this.markInvalid(this.blankText);
41312 if(value.length < this.minLength){
41313 this.markInvalid(String.format(this.minLengthText, this.minLength));
41316 if(value.length > this.maxLength){
41317 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41321 var vt = Roo.form.VTypes;
41322 if(!vt[this.vtype](value, this)){
41323 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41327 if(typeof this.validator == "function"){
41328 var msg = this.validator(value);
41330 this.markInvalid(msg);
41334 if(this.regex && !this.regex.test(value)){
41335 this.markInvalid(this.regexText);
41342 * Selects text in this field
41343 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41344 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41346 selectText : function(start, end){
41347 var v = this.getRawValue();
41349 start = start === undefined ? 0 : start;
41350 end = end === undefined ? v.length : end;
41351 var d = this.el.dom;
41352 if(d.setSelectionRange){
41353 d.setSelectionRange(start, end);
41354 }else if(d.createTextRange){
41355 var range = d.createTextRange();
41356 range.moveStart("character", start);
41357 range.moveEnd("character", v.length-end);
41364 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41365 * This only takes effect if grow = true, and fires the autosize event.
41367 autoSize : function(){
41368 if(!this.grow || !this.rendered){
41372 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41375 var v = el.dom.value;
41376 var d = document.createElement('div');
41377 d.appendChild(document.createTextNode(v));
41381 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41382 this.el.setWidth(w);
41383 this.fireEvent("autosize", this, w);
41387 SafariOnKeyDown : function(event)
41389 // this is a workaround for a password hang bug on chrome/ webkit.
41391 var isSelectAll = false;
41393 if(this.el.dom.selectionEnd > 0){
41394 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41396 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41397 event.preventDefault();
41402 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41404 event.preventDefault();
41405 // this is very hacky as keydown always get's upper case.
41407 var cc = String.fromCharCode(event.getCharCode());
41410 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
41418 * Ext JS Library 1.1.1
41419 * Copyright(c) 2006-2007, Ext JS, LLC.
41421 * Originally Released Under LGPL - original licence link has changed is not relivant.
41424 * <script type="text/javascript">
41428 * @class Roo.form.Hidden
41429 * @extends Roo.form.TextField
41430 * Simple Hidden element used on forms
41432 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41435 * Creates a new Hidden form element.
41436 * @param {Object} config Configuration options
41441 // easy hidden field...
41442 Roo.form.Hidden = function(config){
41443 Roo.form.Hidden.superclass.constructor.call(this, config);
41446 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41448 inputType: 'hidden',
41451 labelSeparator: '',
41453 itemCls : 'x-form-item-display-none'
41461 * Ext JS Library 1.1.1
41462 * Copyright(c) 2006-2007, Ext JS, LLC.
41464 * Originally Released Under LGPL - original licence link has changed is not relivant.
41467 * <script type="text/javascript">
41471 * @class Roo.form.TriggerField
41472 * @extends Roo.form.TextField
41473 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41474 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41475 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41476 * for which you can provide a custom implementation. For example:
41478 var trigger = new Roo.form.TriggerField();
41479 trigger.onTriggerClick = myTriggerFn;
41480 trigger.applyTo('my-field');
41483 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41484 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41485 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
41486 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41488 * Create a new TriggerField.
41489 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41490 * to the base TextField)
41492 Roo.form.TriggerField = function(config){
41493 this.mimicing = false;
41494 Roo.form.TriggerField.superclass.constructor.call(this, config);
41497 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
41499 * @cfg {String} triggerClass A CSS class to apply to the trigger
41502 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41503 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41505 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41507 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41511 /** @cfg {Boolean} grow @hide */
41512 /** @cfg {Number} growMin @hide */
41513 /** @cfg {Number} growMax @hide */
41519 autoSize: Roo.emptyFn,
41523 deferHeight : true,
41526 actionMode : 'wrap',
41528 onResize : function(w, h){
41529 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41530 if(typeof w == 'number'){
41531 var x = w - this.trigger.getWidth();
41532 this.el.setWidth(this.adjustWidth('input', x));
41533 this.trigger.setStyle('left', x+'px');
41538 adjustSize : Roo.BoxComponent.prototype.adjustSize,
41541 getResizeEl : function(){
41546 getPositionEl : function(){
41551 alignErrorIcon : function(){
41552 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41556 onRender : function(ct, position){
41557 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41558 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41559 this.trigger = this.wrap.createChild(this.triggerConfig ||
41560 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41561 if(this.hideTrigger){
41562 this.trigger.setDisplayed(false);
41564 this.initTrigger();
41566 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41571 initTrigger : function(){
41572 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41573 this.trigger.addClassOnOver('x-form-trigger-over');
41574 this.trigger.addClassOnClick('x-form-trigger-click');
41578 onDestroy : function(){
41580 this.trigger.removeAllListeners();
41581 this.trigger.remove();
41584 this.wrap.remove();
41586 Roo.form.TriggerField.superclass.onDestroy.call(this);
41590 onFocus : function(){
41591 Roo.form.TriggerField.superclass.onFocus.call(this);
41592 if(!this.mimicing){
41593 this.wrap.addClass('x-trigger-wrap-focus');
41594 this.mimicing = true;
41595 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41596 if(this.monitorTab){
41597 this.el.on("keydown", this.checkTab, this);
41603 checkTab : function(e){
41604 if(e.getKey() == e.TAB){
41605 this.triggerBlur();
41610 onBlur : function(){
41615 mimicBlur : function(e, t){
41616 if(!this.wrap.contains(t) && this.validateBlur()){
41617 this.triggerBlur();
41622 triggerBlur : function(){
41623 this.mimicing = false;
41624 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41625 if(this.monitorTab){
41626 this.el.un("keydown", this.checkTab, this);
41628 this.wrap.removeClass('x-trigger-wrap-focus');
41629 Roo.form.TriggerField.superclass.onBlur.call(this);
41633 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41634 validateBlur : function(e, t){
41639 onDisable : function(){
41640 Roo.form.TriggerField.superclass.onDisable.call(this);
41642 this.wrap.addClass('x-item-disabled');
41647 onEnable : function(){
41648 Roo.form.TriggerField.superclass.onEnable.call(this);
41650 this.wrap.removeClass('x-item-disabled');
41655 onShow : function(){
41656 var ae = this.getActionEl();
41659 ae.dom.style.display = '';
41660 ae.dom.style.visibility = 'visible';
41666 onHide : function(){
41667 var ae = this.getActionEl();
41668 ae.dom.style.display = 'none';
41672 * The function that should handle the trigger's click event. This method does nothing by default until overridden
41673 * by an implementing function.
41675 * @param {EventObject} e
41677 onTriggerClick : Roo.emptyFn
41680 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
41681 // to be extended by an implementing class. For an example of implementing this class, see the custom
41682 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41683 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41684 initComponent : function(){
41685 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41687 this.triggerConfig = {
41688 tag:'span', cls:'x-form-twin-triggers', cn:[
41689 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41690 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41694 getTrigger : function(index){
41695 return this.triggers[index];
41698 initTrigger : function(){
41699 var ts = this.trigger.select('.x-form-trigger', true);
41700 this.wrap.setStyle('overflow', 'hidden');
41701 var triggerField = this;
41702 ts.each(function(t, all, index){
41703 t.hide = function(){
41704 var w = triggerField.wrap.getWidth();
41705 this.dom.style.display = 'none';
41706 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41708 t.show = function(){
41709 var w = triggerField.wrap.getWidth();
41710 this.dom.style.display = '';
41711 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41713 var triggerIndex = 'Trigger'+(index+1);
41715 if(this['hide'+triggerIndex]){
41716 t.dom.style.display = 'none';
41718 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41719 t.addClassOnOver('x-form-trigger-over');
41720 t.addClassOnClick('x-form-trigger-click');
41722 this.triggers = ts.elements;
41725 onTrigger1Click : Roo.emptyFn,
41726 onTrigger2Click : Roo.emptyFn
41729 * Ext JS Library 1.1.1
41730 * Copyright(c) 2006-2007, Ext JS, LLC.
41732 * Originally Released Under LGPL - original licence link has changed is not relivant.
41735 * <script type="text/javascript">
41739 * @class Roo.form.TextArea
41740 * @extends Roo.form.TextField
41741 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
41742 * support for auto-sizing.
41744 * Creates a new TextArea
41745 * @param {Object} config Configuration options
41747 Roo.form.TextArea = function(config){
41748 Roo.form.TextArea.superclass.constructor.call(this, config);
41749 // these are provided exchanges for backwards compat
41750 // minHeight/maxHeight were replaced by growMin/growMax to be
41751 // compatible with TextField growing config values
41752 if(this.minHeight !== undefined){
41753 this.growMin = this.minHeight;
41755 if(this.maxHeight !== undefined){
41756 this.growMax = this.maxHeight;
41760 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
41762 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41766 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41770 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41771 * in the field (equivalent to setting overflow: hidden, defaults to false)
41773 preventScrollbars: false,
41775 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41776 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41780 onRender : function(ct, position){
41782 this.defaultAutoCreate = {
41784 style:"width:300px;height:60px;",
41785 autocomplete: "new-password"
41788 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41790 this.textSizeEl = Roo.DomHelper.append(document.body, {
41791 tag: "pre", cls: "x-form-grow-sizer"
41793 if(this.preventScrollbars){
41794 this.el.setStyle("overflow", "hidden");
41796 this.el.setHeight(this.growMin);
41800 onDestroy : function(){
41801 if(this.textSizeEl){
41802 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41804 Roo.form.TextArea.superclass.onDestroy.call(this);
41808 onKeyUp : function(e){
41809 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41815 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41816 * This only takes effect if grow = true, and fires the autosize event if the height changes.
41818 autoSize : function(){
41819 if(!this.grow || !this.textSizeEl){
41823 var v = el.dom.value;
41824 var ts = this.textSizeEl;
41827 ts.appendChild(document.createTextNode(v));
41830 Roo.fly(ts).setWidth(this.el.getWidth());
41832 v = "  ";
41835 v = v.replace(/\n/g, '<p> </p>');
41837 v += " \n ";
41840 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41841 if(h != this.lastHeight){
41842 this.lastHeight = h;
41843 this.el.setHeight(h);
41844 this.fireEvent("autosize", this, h);
41849 * Ext JS Library 1.1.1
41850 * Copyright(c) 2006-2007, Ext JS, LLC.
41852 * Originally Released Under LGPL - original licence link has changed is not relivant.
41855 * <script type="text/javascript">
41860 * @class Roo.form.NumberField
41861 * @extends Roo.form.TextField
41862 * Numeric text field that provides automatic keystroke filtering and numeric validation.
41864 * Creates a new NumberField
41865 * @param {Object} config Configuration options
41867 Roo.form.NumberField = function(config){
41868 Roo.form.NumberField.superclass.constructor.call(this, config);
41871 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
41873 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41875 fieldClass: "x-form-field x-form-num-field",
41877 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41879 allowDecimals : true,
41881 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41883 decimalSeparator : ".",
41885 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41887 decimalPrecision : 2,
41889 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41891 allowNegative : true,
41893 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41895 minValue : Number.NEGATIVE_INFINITY,
41897 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41899 maxValue : Number.MAX_VALUE,
41901 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41903 minText : "The minimum value for this field is {0}",
41905 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41907 maxText : "The maximum value for this field is {0}",
41909 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41910 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41912 nanText : "{0} is not a valid number",
41915 initEvents : function(){
41916 Roo.form.NumberField.superclass.initEvents.call(this);
41917 var allowed = "0123456789";
41918 if(this.allowDecimals){
41919 allowed += this.decimalSeparator;
41921 if(this.allowNegative){
41924 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41925 var keyPress = function(e){
41926 var k = e.getKey();
41927 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41930 var c = e.getCharCode();
41931 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41935 this.el.on("keypress", keyPress, this);
41939 validateValue : function(value){
41940 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41943 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41946 var num = this.parseValue(value);
41948 this.markInvalid(String.format(this.nanText, value));
41951 if(num < this.minValue){
41952 this.markInvalid(String.format(this.minText, this.minValue));
41955 if(num > this.maxValue){
41956 this.markInvalid(String.format(this.maxText, this.maxValue));
41962 getValue : function(){
41963 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41967 parseValue : function(value){
41968 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41969 return isNaN(value) ? '' : value;
41973 fixPrecision : function(value){
41974 var nan = isNaN(value);
41975 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41976 return nan ? '' : value;
41978 return parseFloat(value).toFixed(this.decimalPrecision);
41981 setValue : function(v){
41982 v = this.fixPrecision(v);
41983 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41987 decimalPrecisionFcn : function(v){
41988 return Math.floor(v);
41991 beforeBlur : function(){
41992 var v = this.parseValue(this.getRawValue());
41999 * Ext JS Library 1.1.1
42000 * Copyright(c) 2006-2007, Ext JS, LLC.
42002 * Originally Released Under LGPL - original licence link has changed is not relivant.
42005 * <script type="text/javascript">
42009 * @class Roo.form.DateField
42010 * @extends Roo.form.TriggerField
42011 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42013 * Create a new DateField
42014 * @param {Object} config
42016 Roo.form.DateField = function(config)
42018 Roo.form.DateField.superclass.constructor.call(this, config);
42024 * Fires when a date is selected
42025 * @param {Roo.form.DateField} combo This combo box
42026 * @param {Date} date The date selected
42033 if(typeof this.minValue == "string") {
42034 this.minValue = this.parseDate(this.minValue);
42036 if(typeof this.maxValue == "string") {
42037 this.maxValue = this.parseDate(this.maxValue);
42039 this.ddMatch = null;
42040 if(this.disabledDates){
42041 var dd = this.disabledDates;
42043 for(var i = 0; i < dd.length; i++){
42045 if(i != dd.length-1) {
42049 this.ddMatch = new RegExp(re + ")");
42053 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
42055 * @cfg {String} format
42056 * The default date format string which can be overriden for localization support. The format must be
42057 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42061 * @cfg {String} altFormats
42062 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42063 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42065 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42067 * @cfg {Array} disabledDays
42068 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42070 disabledDays : null,
42072 * @cfg {String} disabledDaysText
42073 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42075 disabledDaysText : "Disabled",
42077 * @cfg {Array} disabledDates
42078 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42079 * expression so they are very powerful. Some examples:
42081 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42082 * <li>["03/08", "09/16"] would disable those days for every year</li>
42083 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42084 * <li>["03/../2006"] would disable every day in March 2006</li>
42085 * <li>["^03"] would disable every day in every March</li>
42087 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42088 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42090 disabledDates : null,
42092 * @cfg {String} disabledDatesText
42093 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42095 disabledDatesText : "Disabled",
42099 * @cfg {Date/String} zeroValue
42100 * if the date is less that this number, then the field is rendered as empty
42103 zeroValue : '1800-01-01',
42107 * @cfg {Date/String} minValue
42108 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42109 * valid format (defaults to null).
42113 * @cfg {Date/String} maxValue
42114 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42115 * valid format (defaults to null).
42119 * @cfg {String} minText
42120 * The error text to display when the date in the cell is before minValue (defaults to
42121 * 'The date in this field must be after {minValue}').
42123 minText : "The date in this field must be equal to or after {0}",
42125 * @cfg {String} maxText
42126 * The error text to display when the date in the cell is after maxValue (defaults to
42127 * 'The date in this field must be before {maxValue}').
42129 maxText : "The date in this field must be equal to or before {0}",
42131 * @cfg {String} invalidText
42132 * The error text to display when the date in the field is invalid (defaults to
42133 * '{value} is not a valid date - it must be in the format {format}').
42135 invalidText : "{0} is not a valid date - it must be in the format {1}",
42137 * @cfg {String} triggerClass
42138 * An additional CSS class used to style the trigger button. The trigger will always get the
42139 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42140 * which displays a calendar icon).
42142 triggerClass : 'x-form-date-trigger',
42146 * @cfg {Boolean} useIso
42147 * if enabled, then the date field will use a hidden field to store the
42148 * real value as iso formated date. default (false)
42152 * @cfg {String/Object} autoCreate
42153 * A DomHelper element spec, or true for a default element spec (defaults to
42154 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42157 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42160 hiddenField: false,
42162 onRender : function(ct, position)
42164 Roo.form.DateField.superclass.onRender.call(this, ct, position);
42166 //this.el.dom.removeAttribute('name');
42167 Roo.log("Changing name?");
42168 this.el.dom.setAttribute('name', this.name + '____hidden___' );
42169 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42171 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42172 // prevent input submission
42173 this.hiddenName = this.name;
42180 validateValue : function(value)
42182 value = this.formatDate(value);
42183 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42184 Roo.log('super failed');
42187 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42190 var svalue = value;
42191 value = this.parseDate(value);
42193 Roo.log('parse date failed' + svalue);
42194 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42197 var time = value.getTime();
42198 if(this.minValue && time < this.minValue.getTime()){
42199 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42202 if(this.maxValue && time > this.maxValue.getTime()){
42203 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42206 if(this.disabledDays){
42207 var day = value.getDay();
42208 for(var i = 0; i < this.disabledDays.length; i++) {
42209 if(day === this.disabledDays[i]){
42210 this.markInvalid(this.disabledDaysText);
42215 var fvalue = this.formatDate(value);
42216 if(this.ddMatch && this.ddMatch.test(fvalue)){
42217 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42224 // Provides logic to override the default TriggerField.validateBlur which just returns true
42225 validateBlur : function(){
42226 return !this.menu || !this.menu.isVisible();
42229 getName: function()
42231 // returns hidden if it's set..
42232 if (!this.rendered) {return ''};
42233 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42238 * Returns the current date value of the date field.
42239 * @return {Date} The date value
42241 getValue : function(){
42243 return this.hiddenField ?
42244 this.hiddenField.value :
42245 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42249 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42250 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42251 * (the default format used is "m/d/y").
42254 //All of these calls set the same date value (May 4, 2006)
42256 //Pass a date object:
42257 var dt = new Date('5/4/06');
42258 dateField.setValue(dt);
42260 //Pass a date string (default format):
42261 dateField.setValue('5/4/06');
42263 //Pass a date string (custom format):
42264 dateField.format = 'Y-m-d';
42265 dateField.setValue('2006-5-4');
42267 * @param {String/Date} date The date or valid date string
42269 setValue : function(date){
42270 if (this.hiddenField) {
42271 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42273 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42274 // make sure the value field is always stored as a date..
42275 this.value = this.parseDate(date);
42281 parseDate : function(value){
42283 if (value instanceof Date) {
42284 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42291 if(!value || value instanceof Date){
42294 var v = Date.parseDate(value, this.format);
42295 if (!v && this.useIso) {
42296 v = Date.parseDate(value, 'Y-m-d');
42298 if(!v && this.altFormats){
42299 if(!this.altFormatsArray){
42300 this.altFormatsArray = this.altFormats.split("|");
42302 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42303 v = Date.parseDate(value, this.altFormatsArray[i]);
42306 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42313 formatDate : function(date, fmt){
42314 return (!date || !(date instanceof Date)) ?
42315 date : date.dateFormat(fmt || this.format);
42320 select: function(m, d){
42323 this.fireEvent('select', this, d);
42325 show : function(){ // retain focus styling
42329 this.focus.defer(10, this);
42330 var ml = this.menuListeners;
42331 this.menu.un("select", ml.select, this);
42332 this.menu.un("show", ml.show, this);
42333 this.menu.un("hide", ml.hide, this);
42338 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42339 onTriggerClick : function(){
42343 if(this.menu == null){
42344 this.menu = new Roo.menu.DateMenu();
42346 Roo.apply(this.menu.picker, {
42347 showClear: this.allowBlank,
42348 minDate : this.minValue,
42349 maxDate : this.maxValue,
42350 disabledDatesRE : this.ddMatch,
42351 disabledDatesText : this.disabledDatesText,
42352 disabledDays : this.disabledDays,
42353 disabledDaysText : this.disabledDaysText,
42354 format : this.useIso ? 'Y-m-d' : this.format,
42355 minText : String.format(this.minText, this.formatDate(this.minValue)),
42356 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42358 this.menu.on(Roo.apply({}, this.menuListeners, {
42361 this.menu.picker.setValue(this.getValue() || new Date());
42362 this.menu.show(this.el, "tl-bl?");
42365 beforeBlur : function(){
42366 var v = this.parseDate(this.getRawValue());
42376 isDirty : function() {
42377 if(this.disabled) {
42381 if(typeof(this.startValue) === 'undefined'){
42385 return String(this.getValue()) !== String(this.startValue);
42389 cleanLeadingSpace : function(e)
42396 * Ext JS Library 1.1.1
42397 * Copyright(c) 2006-2007, Ext JS, LLC.
42399 * Originally Released Under LGPL - original licence link has changed is not relivant.
42402 * <script type="text/javascript">
42406 * @class Roo.form.MonthField
42407 * @extends Roo.form.TriggerField
42408 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42410 * Create a new MonthField
42411 * @param {Object} config
42413 Roo.form.MonthField = function(config){
42415 Roo.form.MonthField.superclass.constructor.call(this, config);
42421 * Fires when a date is selected
42422 * @param {Roo.form.MonthFieeld} combo This combo box
42423 * @param {Date} date The date selected
42430 if(typeof this.minValue == "string") {
42431 this.minValue = this.parseDate(this.minValue);
42433 if(typeof this.maxValue == "string") {
42434 this.maxValue = this.parseDate(this.maxValue);
42436 this.ddMatch = null;
42437 if(this.disabledDates){
42438 var dd = this.disabledDates;
42440 for(var i = 0; i < dd.length; i++){
42442 if(i != dd.length-1) {
42446 this.ddMatch = new RegExp(re + ")");
42450 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
42452 * @cfg {String} format
42453 * The default date format string which can be overriden for localization support. The format must be
42454 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42458 * @cfg {String} altFormats
42459 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42460 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42462 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42464 * @cfg {Array} disabledDays
42465 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42467 disabledDays : [0,1,2,3,4,5,6],
42469 * @cfg {String} disabledDaysText
42470 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42472 disabledDaysText : "Disabled",
42474 * @cfg {Array} disabledDates
42475 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42476 * expression so they are very powerful. Some examples:
42478 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42479 * <li>["03/08", "09/16"] would disable those days for every year</li>
42480 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42481 * <li>["03/../2006"] would disable every day in March 2006</li>
42482 * <li>["^03"] would disable every day in every March</li>
42484 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42485 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42487 disabledDates : null,
42489 * @cfg {String} disabledDatesText
42490 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42492 disabledDatesText : "Disabled",
42494 * @cfg {Date/String} minValue
42495 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42496 * valid format (defaults to null).
42500 * @cfg {Date/String} maxValue
42501 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42502 * valid format (defaults to null).
42506 * @cfg {String} minText
42507 * The error text to display when the date in the cell is before minValue (defaults to
42508 * 'The date in this field must be after {minValue}').
42510 minText : "The date in this field must be equal to or after {0}",
42512 * @cfg {String} maxTextf
42513 * The error text to display when the date in the cell is after maxValue (defaults to
42514 * 'The date in this field must be before {maxValue}').
42516 maxText : "The date in this field must be equal to or before {0}",
42518 * @cfg {String} invalidText
42519 * The error text to display when the date in the field is invalid (defaults to
42520 * '{value} is not a valid date - it must be in the format {format}').
42522 invalidText : "{0} is not a valid date - it must be in the format {1}",
42524 * @cfg {String} triggerClass
42525 * An additional CSS class used to style the trigger button. The trigger will always get the
42526 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42527 * which displays a calendar icon).
42529 triggerClass : 'x-form-date-trigger',
42533 * @cfg {Boolean} useIso
42534 * if enabled, then the date field will use a hidden field to store the
42535 * real value as iso formated date. default (true)
42539 * @cfg {String/Object} autoCreate
42540 * A DomHelper element spec, or true for a default element spec (defaults to
42541 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42544 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42547 hiddenField: false,
42549 hideMonthPicker : false,
42551 onRender : function(ct, position)
42553 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42555 this.el.dom.removeAttribute('name');
42556 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42558 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42559 // prevent input submission
42560 this.hiddenName = this.name;
42567 validateValue : function(value)
42569 value = this.formatDate(value);
42570 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42573 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42576 var svalue = value;
42577 value = this.parseDate(value);
42579 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42582 var time = value.getTime();
42583 if(this.minValue && time < this.minValue.getTime()){
42584 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42587 if(this.maxValue && time > this.maxValue.getTime()){
42588 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42591 /*if(this.disabledDays){
42592 var day = value.getDay();
42593 for(var i = 0; i < this.disabledDays.length; i++) {
42594 if(day === this.disabledDays[i]){
42595 this.markInvalid(this.disabledDaysText);
42601 var fvalue = this.formatDate(value);
42602 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42603 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42611 // Provides logic to override the default TriggerField.validateBlur which just returns true
42612 validateBlur : function(){
42613 return !this.menu || !this.menu.isVisible();
42617 * Returns the current date value of the date field.
42618 * @return {Date} The date value
42620 getValue : function(){
42624 return this.hiddenField ?
42625 this.hiddenField.value :
42626 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42630 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42631 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42632 * (the default format used is "m/d/y").
42635 //All of these calls set the same date value (May 4, 2006)
42637 //Pass a date object:
42638 var dt = new Date('5/4/06');
42639 monthField.setValue(dt);
42641 //Pass a date string (default format):
42642 monthField.setValue('5/4/06');
42644 //Pass a date string (custom format):
42645 monthField.format = 'Y-m-d';
42646 monthField.setValue('2006-5-4');
42648 * @param {String/Date} date The date or valid date string
42650 setValue : function(date){
42651 Roo.log('month setValue' + date);
42652 // can only be first of month..
42654 var val = this.parseDate(date);
42656 if (this.hiddenField) {
42657 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42659 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42660 this.value = this.parseDate(date);
42664 parseDate : function(value){
42665 if(!value || value instanceof Date){
42666 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42669 var v = Date.parseDate(value, this.format);
42670 if (!v && this.useIso) {
42671 v = Date.parseDate(value, 'Y-m-d');
42675 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42679 if(!v && this.altFormats){
42680 if(!this.altFormatsArray){
42681 this.altFormatsArray = this.altFormats.split("|");
42683 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42684 v = Date.parseDate(value, this.altFormatsArray[i]);
42691 formatDate : function(date, fmt){
42692 return (!date || !(date instanceof Date)) ?
42693 date : date.dateFormat(fmt || this.format);
42698 select: function(m, d){
42700 this.fireEvent('select', this, d);
42702 show : function(){ // retain focus styling
42706 this.focus.defer(10, this);
42707 var ml = this.menuListeners;
42708 this.menu.un("select", ml.select, this);
42709 this.menu.un("show", ml.show, this);
42710 this.menu.un("hide", ml.hide, this);
42714 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42715 onTriggerClick : function(){
42719 if(this.menu == null){
42720 this.menu = new Roo.menu.DateMenu();
42724 Roo.apply(this.menu.picker, {
42726 showClear: this.allowBlank,
42727 minDate : this.minValue,
42728 maxDate : this.maxValue,
42729 disabledDatesRE : this.ddMatch,
42730 disabledDatesText : this.disabledDatesText,
42732 format : this.useIso ? 'Y-m-d' : this.format,
42733 minText : String.format(this.minText, this.formatDate(this.minValue)),
42734 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42737 this.menu.on(Roo.apply({}, this.menuListeners, {
42745 // hide month picker get's called when we called by 'before hide';
42747 var ignorehide = true;
42748 p.hideMonthPicker = function(disableAnim){
42752 if(this.monthPicker){
42753 Roo.log("hideMonthPicker called");
42754 if(disableAnim === true){
42755 this.monthPicker.hide();
42757 this.monthPicker.slideOut('t', {duration:.2});
42758 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42759 p.fireEvent("select", this, this.value);
42765 Roo.log('picker set value');
42766 Roo.log(this.getValue());
42767 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42768 m.show(this.el, 'tl-bl?');
42769 ignorehide = false;
42770 // this will trigger hideMonthPicker..
42773 // hidden the day picker
42774 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42780 p.showMonthPicker.defer(100, p);
42786 beforeBlur : function(){
42787 var v = this.parseDate(this.getRawValue());
42793 /** @cfg {Boolean} grow @hide */
42794 /** @cfg {Number} growMin @hide */
42795 /** @cfg {Number} growMax @hide */
42802 * Ext JS Library 1.1.1
42803 * Copyright(c) 2006-2007, Ext JS, LLC.
42805 * Originally Released Under LGPL - original licence link has changed is not relivant.
42808 * <script type="text/javascript">
42813 * @class Roo.form.ComboBox
42814 * @extends Roo.form.TriggerField
42815 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42817 * Create a new ComboBox.
42818 * @param {Object} config Configuration options
42820 Roo.form.ComboBox = function(config){
42821 Roo.form.ComboBox.superclass.constructor.call(this, config);
42825 * Fires when the dropdown list is expanded
42826 * @param {Roo.form.ComboBox} combo This combo box
42831 * Fires when the dropdown list is collapsed
42832 * @param {Roo.form.ComboBox} combo This combo box
42836 * @event beforeselect
42837 * Fires before a list item is selected. Return false to cancel the selection.
42838 * @param {Roo.form.ComboBox} combo This combo box
42839 * @param {Roo.data.Record} record The data record returned from the underlying store
42840 * @param {Number} index The index of the selected item in the dropdown list
42842 'beforeselect' : true,
42845 * Fires when a list item is selected
42846 * @param {Roo.form.ComboBox} combo This combo box
42847 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42848 * @param {Number} index The index of the selected item in the dropdown list
42852 * @event beforequery
42853 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42854 * The event object passed has these properties:
42855 * @param {Roo.form.ComboBox} combo This combo box
42856 * @param {String} query The query
42857 * @param {Boolean} forceAll true to force "all" query
42858 * @param {Boolean} cancel true to cancel the query
42859 * @param {Object} e The query event object
42861 'beforequery': true,
42864 * Fires when the 'add' icon is pressed (add a listener to enable add button)
42865 * @param {Roo.form.ComboBox} combo This combo box
42870 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42871 * @param {Roo.form.ComboBox} combo This combo box
42872 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42878 if(this.transform){
42879 this.allowDomMove = false;
42880 var s = Roo.getDom(this.transform);
42881 if(!this.hiddenName){
42882 this.hiddenName = s.name;
42885 this.mode = 'local';
42886 var d = [], opts = s.options;
42887 for(var i = 0, len = opts.length;i < len; i++){
42889 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42891 this.value = value;
42893 d.push([value, o.text]);
42895 this.store = new Roo.data.SimpleStore({
42897 fields: ['value', 'text'],
42900 this.valueField = 'value';
42901 this.displayField = 'text';
42903 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42904 if(!this.lazyRender){
42905 this.target = true;
42906 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42907 s.parentNode.removeChild(s); // remove it
42908 this.render(this.el.parentNode);
42910 s.parentNode.removeChild(s); // remove it
42915 this.store = Roo.factory(this.store, Roo.data);
42918 this.selectedIndex = -1;
42919 if(this.mode == 'local'){
42920 if(config.queryDelay === undefined){
42921 this.queryDelay = 10;
42923 if(config.minChars === undefined){
42929 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42931 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42934 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42935 * rendering into an Roo.Editor, defaults to false)
42938 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42939 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42942 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42945 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42946 * the dropdown list (defaults to undefined, with no header element)
42950 * @cfg {String/Roo.Template} tpl The template to use to render the output
42954 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42956 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42958 listWidth: undefined,
42960 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42961 * mode = 'remote' or 'text' if mode = 'local')
42963 displayField: undefined,
42965 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42966 * mode = 'remote' or 'value' if mode = 'local').
42967 * Note: use of a valueField requires the user make a selection
42968 * in order for a value to be mapped.
42970 valueField: undefined,
42974 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42975 * field's data value (defaults to the underlying DOM element's name)
42977 hiddenName: undefined,
42979 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42983 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42985 selectedClass: 'x-combo-selected',
42987 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
42988 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42989 * which displays a downward arrow icon).
42991 triggerClass : 'x-form-arrow-trigger',
42993 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42997 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42998 * anchor positions (defaults to 'tl-bl')
43000 listAlign: 'tl-bl?',
43002 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43006 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
43007 * query specified by the allQuery config option (defaults to 'query')
43009 triggerAction: 'query',
43011 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43012 * (defaults to 4, does not apply if editable = false)
43016 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43017 * delay (typeAheadDelay) if it matches a known value (defaults to false)
43021 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43022 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43026 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43027 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
43031 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
43032 * when editable = true (defaults to false)
43034 selectOnFocus:false,
43036 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43038 queryParam: 'query',
43040 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
43041 * when mode = 'remote' (defaults to 'Loading...')
43043 loadingText: 'Loading...',
43045 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43049 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43053 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43054 * traditional select (defaults to true)
43058 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43062 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43066 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43067 * listWidth has a higher value)
43071 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43072 * allow the user to set arbitrary text into the field (defaults to false)
43074 forceSelection:false,
43076 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43077 * if typeAhead = true (defaults to 250)
43079 typeAheadDelay : 250,
43081 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43082 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43084 valueNotFoundText : undefined,
43086 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43088 blockFocus : false,
43091 * @cfg {Boolean} disableClear Disable showing of clear button.
43093 disableClear : false,
43095 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
43097 alwaysQuery : false,
43103 // element that contains real text value.. (when hidden is used..)
43106 onRender : function(ct, position)
43108 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43110 if(this.hiddenName){
43111 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
43113 this.hiddenField.value =
43114 this.hiddenValue !== undefined ? this.hiddenValue :
43115 this.value !== undefined ? this.value : '';
43117 // prevent input submission
43118 this.el.dom.removeAttribute('name');
43124 this.el.dom.setAttribute('autocomplete', 'off');
43127 var cls = 'x-combo-list';
43129 this.list = new Roo.Layer({
43130 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43133 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43134 this.list.setWidth(lw);
43135 this.list.swallowEvent('mousewheel');
43136 this.assetHeight = 0;
43139 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43140 this.assetHeight += this.header.getHeight();
43143 this.innerList = this.list.createChild({cls:cls+'-inner'});
43144 this.innerList.on('mouseover', this.onViewOver, this);
43145 this.innerList.on('mousemove', this.onViewMove, this);
43146 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43148 if(this.allowBlank && !this.pageSize && !this.disableClear){
43149 this.footer = this.list.createChild({cls:cls+'-ft'});
43150 this.pageTb = new Roo.Toolbar(this.footer);
43154 this.footer = this.list.createChild({cls:cls+'-ft'});
43155 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43156 {pageSize: this.pageSize});
43160 if (this.pageTb && this.allowBlank && !this.disableClear) {
43162 this.pageTb.add(new Roo.Toolbar.Fill(), {
43163 cls: 'x-btn-icon x-btn-clear',
43165 handler: function()
43168 _this.clearValue();
43169 _this.onSelect(false, -1);
43174 this.assetHeight += this.footer.getHeight();
43179 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43182 this.view = new Roo.View(this.innerList, this.tpl, {
43185 selectedClass: this.selectedClass
43188 this.view.on('click', this.onViewClick, this);
43190 this.store.on('beforeload', this.onBeforeLoad, this);
43191 this.store.on('load', this.onLoad, this);
43192 this.store.on('loadexception', this.onLoadException, this);
43194 if(this.resizable){
43195 this.resizer = new Roo.Resizable(this.list, {
43196 pinned:true, handles:'se'
43198 this.resizer.on('resize', function(r, w, h){
43199 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43200 this.listWidth = w;
43201 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43202 this.restrictHeight();
43204 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43206 if(!this.editable){
43207 this.editable = true;
43208 this.setEditable(false);
43212 if (typeof(this.events.add.listeners) != 'undefined') {
43214 this.addicon = this.wrap.createChild(
43215 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
43217 this.addicon.on('click', function(e) {
43218 this.fireEvent('add', this);
43221 if (typeof(this.events.edit.listeners) != 'undefined') {
43223 this.editicon = this.wrap.createChild(
43224 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
43225 if (this.addicon) {
43226 this.editicon.setStyle('margin-left', '40px');
43228 this.editicon.on('click', function(e) {
43230 // we fire even if inothing is selected..
43231 this.fireEvent('edit', this, this.lastData );
43241 initEvents : function(){
43242 Roo.form.ComboBox.superclass.initEvents.call(this);
43244 this.keyNav = new Roo.KeyNav(this.el, {
43245 "up" : function(e){
43246 this.inKeyMode = true;
43250 "down" : function(e){
43251 if(!this.isExpanded()){
43252 this.onTriggerClick();
43254 this.inKeyMode = true;
43259 "enter" : function(e){
43260 this.onViewClick();
43264 "esc" : function(e){
43268 "tab" : function(e){
43269 this.onViewClick(false);
43270 this.fireEvent("specialkey", this, e);
43276 doRelay : function(foo, bar, hname){
43277 if(hname == 'down' || this.scope.isExpanded()){
43278 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43285 this.queryDelay = Math.max(this.queryDelay || 10,
43286 this.mode == 'local' ? 10 : 250);
43287 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43288 if(this.typeAhead){
43289 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43291 if(this.editable !== false){
43292 this.el.on("keyup", this.onKeyUp, this);
43294 if(this.forceSelection){
43295 this.on('blur', this.doForce, this);
43299 onDestroy : function(){
43301 this.view.setStore(null);
43302 this.view.el.removeAllListeners();
43303 this.view.el.remove();
43304 this.view.purgeListeners();
43307 this.list.destroy();
43310 this.store.un('beforeload', this.onBeforeLoad, this);
43311 this.store.un('load', this.onLoad, this);
43312 this.store.un('loadexception', this.onLoadException, this);
43314 Roo.form.ComboBox.superclass.onDestroy.call(this);
43318 fireKey : function(e){
43319 if(e.isNavKeyPress() && !this.list.isVisible()){
43320 this.fireEvent("specialkey", this, e);
43325 onResize: function(w, h){
43326 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43328 if(typeof w != 'number'){
43329 // we do not handle it!?!?
43332 var tw = this.trigger.getWidth();
43333 tw += this.addicon ? this.addicon.getWidth() : 0;
43334 tw += this.editicon ? this.editicon.getWidth() : 0;
43336 this.el.setWidth( this.adjustWidth('input', x));
43338 this.trigger.setStyle('left', x+'px');
43340 if(this.list && this.listWidth === undefined){
43341 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43342 this.list.setWidth(lw);
43343 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43351 * Allow or prevent the user from directly editing the field text. If false is passed,
43352 * the user will only be able to select from the items defined in the dropdown list. This method
43353 * is the runtime equivalent of setting the 'editable' config option at config time.
43354 * @param {Boolean} value True to allow the user to directly edit the field text
43356 setEditable : function(value){
43357 if(value == this.editable){
43360 this.editable = value;
43362 this.el.dom.setAttribute('readOnly', true);
43363 this.el.on('mousedown', this.onTriggerClick, this);
43364 this.el.addClass('x-combo-noedit');
43366 this.el.dom.setAttribute('readOnly', false);
43367 this.el.un('mousedown', this.onTriggerClick, this);
43368 this.el.removeClass('x-combo-noedit');
43373 onBeforeLoad : function(){
43374 if(!this.hasFocus){
43377 this.innerList.update(this.loadingText ?
43378 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43379 this.restrictHeight();
43380 this.selectedIndex = -1;
43384 onLoad : function(){
43385 if(!this.hasFocus){
43388 if(this.store.getCount() > 0){
43390 this.restrictHeight();
43391 if(this.lastQuery == this.allQuery){
43393 this.el.dom.select();
43395 if(!this.selectByValue(this.value, true)){
43396 this.select(0, true);
43400 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43401 this.taTask.delay(this.typeAheadDelay);
43405 this.onEmptyResults();
43410 onLoadException : function()
43413 Roo.log(this.store.reader.jsonData);
43414 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43415 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43421 onTypeAhead : function(){
43422 if(this.store.getCount() > 0){
43423 var r = this.store.getAt(0);
43424 var newValue = r.data[this.displayField];
43425 var len = newValue.length;
43426 var selStart = this.getRawValue().length;
43427 if(selStart != len){
43428 this.setRawValue(newValue);
43429 this.selectText(selStart, newValue.length);
43435 onSelect : function(record, index){
43436 if(this.fireEvent('beforeselect', this, record, index) !== false){
43437 this.setFromData(index > -1 ? record.data : false);
43439 this.fireEvent('select', this, record, index);
43444 * Returns the currently selected field value or empty string if no value is set.
43445 * @return {String} value The selected value
43447 getValue : function(){
43448 if(this.valueField){
43449 return typeof this.value != 'undefined' ? this.value : '';
43451 return Roo.form.ComboBox.superclass.getValue.call(this);
43455 * Clears any text/value currently set in the field
43457 clearValue : function(){
43458 if(this.hiddenField){
43459 this.hiddenField.value = '';
43462 this.setRawValue('');
43463 this.lastSelectionText = '';
43468 * Sets the specified value into the field. If the value finds a match, the corresponding record text
43469 * will be displayed in the field. If the value does not match the data value of an existing item,
43470 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43471 * Otherwise the field will be blank (although the value will still be set).
43472 * @param {String} value The value to match
43474 setValue : function(v){
43476 if(this.valueField){
43477 var r = this.findRecord(this.valueField, v);
43479 text = r.data[this.displayField];
43480 }else if(this.valueNotFoundText !== undefined){
43481 text = this.valueNotFoundText;
43484 this.lastSelectionText = text;
43485 if(this.hiddenField){
43486 this.hiddenField.value = v;
43488 Roo.form.ComboBox.superclass.setValue.call(this, text);
43492 * @property {Object} the last set data for the element
43497 * Sets the value of the field based on a object which is related to the record format for the store.
43498 * @param {Object} value the value to set as. or false on reset?
43500 setFromData : function(o){
43501 var dv = ''; // display value
43502 var vv = ''; // value value..
43504 if (this.displayField) {
43505 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43507 // this is an error condition!!!
43508 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
43511 if(this.valueField){
43512 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43514 if(this.hiddenField){
43515 this.hiddenField.value = vv;
43517 this.lastSelectionText = dv;
43518 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43522 // no hidden field.. - we store the value in 'value', but still display
43523 // display field!!!!
43524 this.lastSelectionText = dv;
43525 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43531 reset : function(){
43532 // overridden so that last data is reset..
43533 this.setValue(this.resetValue);
43534 this.originalValue = this.getValue();
43535 this.clearInvalid();
43536 this.lastData = false;
43538 this.view.clearSelections();
43542 findRecord : function(prop, value){
43544 if(this.store.getCount() > 0){
43545 this.store.each(function(r){
43546 if(r.data[prop] == value){
43556 getName: function()
43558 // returns hidden if it's set..
43559 if (!this.rendered) {return ''};
43560 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
43564 onViewMove : function(e, t){
43565 this.inKeyMode = false;
43569 onViewOver : function(e, t){
43570 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43573 var item = this.view.findItemFromChild(t);
43575 var index = this.view.indexOf(item);
43576 this.select(index, false);
43581 onViewClick : function(doFocus)
43583 var index = this.view.getSelectedIndexes()[0];
43584 var r = this.store.getAt(index);
43586 this.onSelect(r, index);
43588 if(doFocus !== false && !this.blockFocus){
43594 restrictHeight : function(){
43595 this.innerList.dom.style.height = '';
43596 var inner = this.innerList.dom;
43597 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43598 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43599 this.list.beginUpdate();
43600 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43601 this.list.alignTo(this.el, this.listAlign);
43602 this.list.endUpdate();
43606 onEmptyResults : function(){
43611 * Returns true if the dropdown list is expanded, else false.
43613 isExpanded : function(){
43614 return this.list.isVisible();
43618 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43619 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43620 * @param {String} value The data value of the item to select
43621 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43622 * selected item if it is not currently in view (defaults to true)
43623 * @return {Boolean} True if the value matched an item in the list, else false
43625 selectByValue : function(v, scrollIntoView){
43626 if(v !== undefined && v !== null){
43627 var r = this.findRecord(this.valueField || this.displayField, v);
43629 this.select(this.store.indexOf(r), scrollIntoView);
43637 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43638 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43639 * @param {Number} index The zero-based index of the list item to select
43640 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43641 * selected item if it is not currently in view (defaults to true)
43643 select : function(index, scrollIntoView){
43644 this.selectedIndex = index;
43645 this.view.select(index);
43646 if(scrollIntoView !== false){
43647 var el = this.view.getNode(index);
43649 this.innerList.scrollChildIntoView(el, false);
43655 selectNext : function(){
43656 var ct = this.store.getCount();
43658 if(this.selectedIndex == -1){
43660 }else if(this.selectedIndex < ct-1){
43661 this.select(this.selectedIndex+1);
43667 selectPrev : function(){
43668 var ct = this.store.getCount();
43670 if(this.selectedIndex == -1){
43672 }else if(this.selectedIndex != 0){
43673 this.select(this.selectedIndex-1);
43679 onKeyUp : function(e){
43680 if(this.editable !== false && !e.isSpecialKey()){
43681 this.lastKey = e.getKey();
43682 this.dqTask.delay(this.queryDelay);
43687 validateBlur : function(){
43688 return !this.list || !this.list.isVisible();
43692 initQuery : function(){
43693 this.doQuery(this.getRawValue());
43697 doForce : function(){
43698 if(this.el.dom.value.length > 0){
43699 this.el.dom.value =
43700 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43706 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
43707 * query allowing the query action to be canceled if needed.
43708 * @param {String} query The SQL query to execute
43709 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43710 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
43711 * saved in the current store (defaults to false)
43713 doQuery : function(q, forceAll){
43714 if(q === undefined || q === null){
43719 forceAll: forceAll,
43723 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43727 forceAll = qe.forceAll;
43728 if(forceAll === true || (q.length >= this.minChars)){
43729 if(this.lastQuery != q || this.alwaysQuery){
43730 this.lastQuery = q;
43731 if(this.mode == 'local'){
43732 this.selectedIndex = -1;
43734 this.store.clearFilter();
43736 this.store.filter(this.displayField, q);
43740 this.store.baseParams[this.queryParam] = q;
43742 params: this.getParams(q)
43747 this.selectedIndex = -1;
43754 getParams : function(q){
43756 //p[this.queryParam] = q;
43759 p.limit = this.pageSize;
43765 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43767 collapse : function(){
43768 if(!this.isExpanded()){
43772 Roo.get(document).un('mousedown', this.collapseIf, this);
43773 Roo.get(document).un('mousewheel', this.collapseIf, this);
43774 if (!this.editable) {
43775 Roo.get(document).un('keydown', this.listKeyPress, this);
43777 this.fireEvent('collapse', this);
43781 collapseIf : function(e){
43782 if(!e.within(this.wrap) && !e.within(this.list)){
43788 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43790 expand : function(){
43791 if(this.isExpanded() || !this.hasFocus){
43794 this.list.alignTo(this.el, this.listAlign);
43796 Roo.get(document).on('mousedown', this.collapseIf, this);
43797 Roo.get(document).on('mousewheel', this.collapseIf, this);
43798 if (!this.editable) {
43799 Roo.get(document).on('keydown', this.listKeyPress, this);
43802 this.fireEvent('expand', this);
43806 // Implements the default empty TriggerField.onTriggerClick function
43807 onTriggerClick : function(){
43811 if(this.isExpanded()){
43813 if (!this.blockFocus) {
43818 this.hasFocus = true;
43819 if(this.triggerAction == 'all') {
43820 this.doQuery(this.allQuery, true);
43822 this.doQuery(this.getRawValue());
43824 if (!this.blockFocus) {
43829 listKeyPress : function(e)
43831 //Roo.log('listkeypress');
43832 // scroll to first matching element based on key pres..
43833 if (e.isSpecialKey()) {
43836 var k = String.fromCharCode(e.getKey()).toUpperCase();
43839 var csel = this.view.getSelectedNodes();
43840 var cselitem = false;
43842 var ix = this.view.indexOf(csel[0]);
43843 cselitem = this.store.getAt(ix);
43844 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43850 this.store.each(function(v) {
43852 // start at existing selection.
43853 if (cselitem.id == v.id) {
43859 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43860 match = this.store.indexOf(v);
43865 if (match === false) {
43866 return true; // no more action?
43869 this.view.select(match);
43870 var sn = Roo.get(this.view.getSelectedNodes()[0]);
43871 sn.scrollIntoView(sn.dom.parentNode, false);
43875 * @cfg {Boolean} grow
43879 * @cfg {Number} growMin
43883 * @cfg {Number} growMax
43891 * Copyright(c) 2010-2012, Roo J Solutions Limited
43898 * @class Roo.form.ComboBoxArray
43899 * @extends Roo.form.TextField
43900 * A facebook style adder... for lists of email / people / countries etc...
43901 * pick multiple items from a combo box, and shows each one.
43903 * Fred [x] Brian [x] [Pick another |v]
43906 * For this to work: it needs various extra information
43907 * - normal combo problay has
43909 * + displayField, valueField
43911 * For our purpose...
43914 * If we change from 'extends' to wrapping...
43921 * Create a new ComboBoxArray.
43922 * @param {Object} config Configuration options
43926 Roo.form.ComboBoxArray = function(config)
43930 * @event beforeremove
43931 * Fires before remove the value from the list
43932 * @param {Roo.form.ComboBoxArray} _self This combo box array
43933 * @param {Roo.form.ComboBoxArray.Item} item removed item
43935 'beforeremove' : true,
43938 * Fires when remove the value from the list
43939 * @param {Roo.form.ComboBoxArray} _self This combo box array
43940 * @param {Roo.form.ComboBoxArray.Item} item removed item
43947 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43949 this.items = new Roo.util.MixedCollection(false);
43951 // construct the child combo...
43961 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43964 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43969 // behavies liek a hiddne field
43970 inputType: 'hidden',
43972 * @cfg {Number} width The width of the box that displays the selected element
43979 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
43983 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
43985 hiddenName : false,
43987 * @cfg {String} seperator The value seperator normally ','
43991 // private the array of items that are displayed..
43993 // private - the hidden field el.
43995 // private - the filed el..
43998 //validateValue : function() { return true; }, // all values are ok!
43999 //onAddClick: function() { },
44001 onRender : function(ct, position)
44004 // create the standard hidden element
44005 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44008 // give fake names to child combo;
44009 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44010 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44012 this.combo = Roo.factory(this.combo, Roo.form);
44013 this.combo.onRender(ct, position);
44014 if (typeof(this.combo.width) != 'undefined') {
44015 this.combo.onResize(this.combo.width,0);
44018 this.combo.initEvents();
44020 // assigned so form know we need to do this..
44021 this.store = this.combo.store;
44022 this.valueField = this.combo.valueField;
44023 this.displayField = this.combo.displayField ;
44026 this.combo.wrap.addClass('x-cbarray-grp');
44028 var cbwrap = this.combo.wrap.createChild(
44029 {tag: 'div', cls: 'x-cbarray-cb'},
44034 this.hiddenEl = this.combo.wrap.createChild({
44035 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
44037 this.el = this.combo.wrap.createChild({
44038 tag: 'input', type:'hidden' , name: this.name, value : ''
44040 // this.el.dom.removeAttribute("name");
44043 this.outerWrap = this.combo.wrap;
44044 this.wrap = cbwrap;
44046 this.outerWrap.setWidth(this.width);
44047 this.outerWrap.dom.removeChild(this.el.dom);
44049 this.wrap.dom.appendChild(this.el.dom);
44050 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44051 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44053 this.combo.trigger.setStyle('position','relative');
44054 this.combo.trigger.setStyle('left', '0px');
44055 this.combo.trigger.setStyle('top', '2px');
44057 this.combo.el.setStyle('vertical-align', 'text-bottom');
44059 //this.trigger.setStyle('vertical-align', 'top');
44061 // this should use the code from combo really... on('add' ....)
44065 this.adder = this.outerWrap.createChild(
44066 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
44068 this.adder.on('click', function(e) {
44069 _t.fireEvent('adderclick', this, e);
44073 //this.adder.on('click', this.onAddClick, _t);
44076 this.combo.on('select', function(cb, rec, ix) {
44077 this.addItem(rec.data);
44080 cb.el.dom.value = '';
44081 //cb.lastData = rec.data;
44090 getName: function()
44092 // returns hidden if it's set..
44093 if (!this.rendered) {return ''};
44094 return this.hiddenName ? this.hiddenName : this.name;
44099 onResize: function(w, h){
44102 // not sure if this is needed..
44103 //this.combo.onResize(w,h);
44105 if(typeof w != 'number'){
44106 // we do not handle it!?!?
44109 var tw = this.combo.trigger.getWidth();
44110 tw += this.addicon ? this.addicon.getWidth() : 0;
44111 tw += this.editicon ? this.editicon.getWidth() : 0;
44113 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44115 this.combo.trigger.setStyle('left', '0px');
44117 if(this.list && this.listWidth === undefined){
44118 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44119 this.list.setWidth(lw);
44120 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44127 addItem: function(rec)
44129 var valueField = this.combo.valueField;
44130 var displayField = this.combo.displayField;
44132 if (this.items.indexOfKey(rec[valueField]) > -1) {
44133 //console.log("GOT " + rec.data.id);
44137 var x = new Roo.form.ComboBoxArray.Item({
44138 //id : rec[this.idField],
44140 displayField : displayField ,
44141 tipField : displayField ,
44145 this.items.add(rec[valueField],x);
44146 // add it before the element..
44147 this.updateHiddenEl();
44148 x.render(this.outerWrap, this.wrap.dom);
44149 // add the image handler..
44152 updateHiddenEl : function()
44155 if (!this.hiddenEl) {
44159 var idField = this.combo.valueField;
44161 this.items.each(function(f) {
44162 ar.push(f.data[idField]);
44164 this.hiddenEl.dom.value = ar.join(this.seperator);
44170 this.items.clear();
44172 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44176 this.el.dom.value = '';
44177 if (this.hiddenEl) {
44178 this.hiddenEl.dom.value = '';
44182 getValue: function()
44184 return this.hiddenEl ? this.hiddenEl.dom.value : '';
44186 setValue: function(v) // not a valid action - must use addItems..
44191 if (this.store.isLocal && (typeof(v) == 'string')) {
44192 // then we can use the store to find the values..
44193 // comma seperated at present.. this needs to allow JSON based encoding..
44194 this.hiddenEl.value = v;
44196 Roo.each(v.split(this.seperator), function(k) {
44197 Roo.log("CHECK " + this.valueField + ',' + k);
44198 var li = this.store.query(this.valueField, k);
44203 add[this.valueField] = k;
44204 add[this.displayField] = li.item(0).data[this.displayField];
44210 if (typeof(v) == 'object' ) {
44211 // then let's assume it's an array of objects..
44212 Roo.each(v, function(l) {
44214 if (typeof(l) == 'string') {
44216 add[this.valueField] = l;
44217 add[this.displayField] = l
44226 setFromData: function(v)
44228 // this recieves an object, if setValues is called.
44230 this.el.dom.value = v[this.displayField];
44231 this.hiddenEl.dom.value = v[this.valueField];
44232 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44235 var kv = v[this.valueField];
44236 var dv = v[this.displayField];
44237 kv = typeof(kv) != 'string' ? '' : kv;
44238 dv = typeof(dv) != 'string' ? '' : dv;
44241 var keys = kv.split(this.seperator);
44242 var display = dv.split(this.seperator);
44243 for (var i = 0 ; i < keys.length; i++) {
44245 add[this.valueField] = keys[i];
44246 add[this.displayField] = display[i];
44254 * Validates the combox array value
44255 * @return {Boolean} True if the value is valid, else false
44257 validate : function(){
44258 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44259 this.clearInvalid();
44265 validateValue : function(value){
44266 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44274 isDirty : function() {
44275 if(this.disabled) {
44280 var d = Roo.decode(String(this.originalValue));
44282 return String(this.getValue()) !== String(this.originalValue);
44285 var originalValue = [];
44287 for (var i = 0; i < d.length; i++){
44288 originalValue.push(d[i][this.valueField]);
44291 return String(this.getValue()) !== String(originalValue.join(this.seperator));
44300 * @class Roo.form.ComboBoxArray.Item
44301 * @extends Roo.BoxComponent
44302 * A selected item in the list
44303 * Fred [x] Brian [x] [Pick another |v]
44306 * Create a new item.
44307 * @param {Object} config Configuration options
44310 Roo.form.ComboBoxArray.Item = function(config) {
44311 config.id = Roo.id();
44312 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44315 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44318 displayField : false,
44322 defaultAutoCreate : {
44324 cls: 'x-cbarray-item',
44331 src : Roo.BLANK_IMAGE_URL ,
44339 onRender : function(ct, position)
44341 Roo.form.Field.superclass.onRender.call(this, ct, position);
44344 var cfg = this.getAutoCreate();
44345 this.el = ct.createChild(cfg, position);
44348 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44350 this.el.child('div').dom.innerHTML = this.cb.renderer ?
44351 this.cb.renderer(this.data) :
44352 String.format('{0}',this.data[this.displayField]);
44355 this.el.child('div').dom.setAttribute('qtip',
44356 String.format('{0}',this.data[this.tipField])
44359 this.el.child('img').on('click', this.remove, this);
44363 remove : function()
44365 if(this.cb.disabled){
44369 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44370 this.cb.items.remove(this);
44371 this.el.child('img').un('click', this.remove, this);
44373 this.cb.updateHiddenEl();
44375 this.cb.fireEvent('remove', this.cb, this);
44380 * RooJS Library 1.1.1
44381 * Copyright(c) 2008-2011 Alan Knowles
44388 * @class Roo.form.ComboNested
44389 * @extends Roo.form.ComboBox
44390 * A combobox for that allows selection of nested items in a list,
44405 * Create a new ComboNested
44406 * @param {Object} config Configuration options
44408 Roo.form.ComboNested = function(config){
44409 Roo.form.ComboCheck.superclass.constructor.call(this, config);
44410 // should verify some data...
44412 // hiddenName = required..
44413 // displayField = required
44414 // valudField == required
44415 var req= [ 'hiddenName', 'displayField', 'valueField' ];
44417 Roo.each(req, function(e) {
44418 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44419 throw "Roo.form.ComboNested : missing value for: " + e;
44426 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44429 * @config {Number} max Number of columns to show
44434 list : null, // the outermost div..
44435 innerLists : null, // the
44439 loadingChildren : false,
44441 onRender : function(ct, position)
44443 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44445 if(this.hiddenName){
44446 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
44448 this.hiddenField.value =
44449 this.hiddenValue !== undefined ? this.hiddenValue :
44450 this.value !== undefined ? this.value : '';
44452 // prevent input submission
44453 this.el.dom.removeAttribute('name');
44459 this.el.dom.setAttribute('autocomplete', 'off');
44462 var cls = 'x-combo-list';
44464 this.list = new Roo.Layer({
44465 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44468 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44469 this.list.setWidth(lw);
44470 this.list.swallowEvent('mousewheel');
44471 this.assetHeight = 0;
44474 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44475 this.assetHeight += this.header.getHeight();
44477 this.innerLists = [];
44480 for (var i =0 ; i < this.maxColumns; i++) {
44481 this.onRenderList( cls, i);
44484 // always needs footer, as we are going to have an 'OK' button.
44485 this.footer = this.list.createChild({cls:cls+'-ft'});
44486 this.pageTb = new Roo.Toolbar(this.footer);
44491 handler: function()
44497 if ( this.allowBlank && !this.disableClear) {
44499 this.pageTb.add(new Roo.Toolbar.Fill(), {
44500 cls: 'x-btn-icon x-btn-clear',
44502 handler: function()
44505 _this.clearValue();
44506 _this.onSelect(false, -1);
44511 this.assetHeight += this.footer.getHeight();
44515 onRenderList : function ( cls, i)
44518 var lw = Math.floor(
44519 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44522 this.list.setWidth(lw); // default to '1'
44524 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44525 //il.on('mouseover', this.onViewOver, this, { list: i });
44526 //il.on('mousemove', this.onViewMove, this, { list: i });
44528 il.setStyle({ 'overflow-x' : 'hidden'});
44531 this.tpl = new Roo.Template({
44532 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44533 isEmpty: function (value, allValues) {
44535 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44536 return dl ? 'has-children' : 'no-children'
44541 var store = this.store;
44543 store = new Roo.data.SimpleStore({
44544 //fields : this.store.reader.meta.fields,
44545 reader : this.store.reader,
44549 this.stores[i] = store;
44551 var view = this.views[i] = new Roo.View(
44557 selectedClass: this.selectedClass
44560 view.getEl().setWidth(lw);
44561 view.getEl().setStyle({
44562 position: i < 1 ? 'relative' : 'absolute',
44564 left: (i * lw ) + 'px',
44565 display : i > 0 ? 'none' : 'block'
44567 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44568 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44569 //view.on('click', this.onViewClick, this, { list : i });
44571 store.on('beforeload', this.onBeforeLoad, this);
44572 store.on('load', this.onLoad, this, { list : i});
44573 store.on('loadexception', this.onLoadException, this);
44575 // hide the other vies..
44581 restrictHeight : function()
44584 Roo.each(this.innerLists, function(il,i) {
44585 var el = this.views[i].getEl();
44586 el.dom.style.height = '';
44587 var inner = el.dom;
44588 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44589 // only adjust heights on other ones..
44590 mh = Math.max(h, mh);
44593 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44594 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44601 this.list.beginUpdate();
44602 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44603 this.list.alignTo(this.el, this.listAlign);
44604 this.list.endUpdate();
44609 // -- store handlers..
44611 onBeforeLoad : function()
44613 if(!this.hasFocus){
44616 this.innerLists[0].update(this.loadingText ?
44617 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44618 this.restrictHeight();
44619 this.selectedIndex = -1;
44622 onLoad : function(a,b,c,d)
44624 if (!this.loadingChildren) {
44625 // then we are loading the top level. - hide the children
44626 for (var i = 1;i < this.views.length; i++) {
44627 this.views[i].getEl().setStyle({ display : 'none' });
44629 var lw = Math.floor(
44630 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44633 this.list.setWidth(lw); // default to '1'
44637 if(!this.hasFocus){
44641 if(this.store.getCount() > 0) {
44643 this.restrictHeight();
44645 this.onEmptyResults();
44648 if (!this.loadingChildren) {
44649 this.selectActive();
44652 this.stores[1].loadData([]);
44653 this.stores[2].loadData([]);
44662 onLoadException : function()
44665 Roo.log(this.store.reader.jsonData);
44666 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44667 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44672 // no cleaning of leading spaces on blur here.
44673 cleanLeadingSpace : function(e) { },
44676 onSelectChange : function (view, sels, opts )
44678 var ix = view.getSelectedIndexes();
44680 if (opts.list > this.maxColumns - 2) {
44681 if (view.store.getCount()< 1) {
44682 this.views[opts.list ].getEl().setStyle({ display : 'none' });
44686 // used to clear ?? but if we are loading unselected
44687 this.setFromData(view.store.getAt(ix[0]).data);
44696 // this get's fired when trigger opens..
44697 // this.setFromData({});
44698 var str = this.stores[opts.list+1];
44699 str.data.clear(); // removeall wihtout the fire events..
44703 var rec = view.store.getAt(ix[0]);
44705 this.setFromData(rec.data);
44706 this.fireEvent('select', this, rec, ix[0]);
44708 var lw = Math.floor(
44710 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44711 ) / this.maxColumns
44713 this.loadingChildren = true;
44714 this.stores[opts.list+1].loadDataFromChildren( rec );
44715 this.loadingChildren = false;
44716 var dl = this.stores[opts.list+1]. getTotalCount();
44718 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44720 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44721 for (var i = opts.list+2; i < this.views.length;i++) {
44722 this.views[i].getEl().setStyle({ display : 'none' });
44725 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44726 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44728 if (this.isLoading) {
44729 // this.selectActive(opts.list);
44737 onDoubleClick : function()
44739 this.collapse(); //??
44747 recordToStack : function(store, prop, value, stack)
44749 var cstore = new Roo.data.SimpleStore({
44750 //fields : this.store.reader.meta.fields, // we need array reader.. for
44751 reader : this.store.reader,
44755 var record = false;
44757 if(store.getCount() < 1){
44760 store.each(function(r){
44761 if(r.data[prop] == value){
44766 if (r.data.cn && r.data.cn.length) {
44767 cstore.loadDataFromChildren( r);
44768 var cret = _this.recordToStack(cstore, prop, value, stack);
44769 if (cret !== false) {
44778 if (record == false) {
44781 stack.unshift(srec);
44786 * find the stack of stores that match our value.
44791 selectActive : function ()
44793 // if store is not loaded, then we will need to wait for that to happen first.
44795 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44796 for (var i = 0; i < stack.length; i++ ) {
44797 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44809 * Ext JS Library 1.1.1
44810 * Copyright(c) 2006-2007, Ext JS, LLC.
44812 * Originally Released Under LGPL - original licence link has changed is not relivant.
44815 * <script type="text/javascript">
44818 * @class Roo.form.Checkbox
44819 * @extends Roo.form.Field
44820 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
44822 * Creates a new Checkbox
44823 * @param {Object} config Configuration options
44825 Roo.form.Checkbox = function(config){
44826 Roo.form.Checkbox.superclass.constructor.call(this, config);
44830 * Fires when the checkbox is checked or unchecked.
44831 * @param {Roo.form.Checkbox} this This checkbox
44832 * @param {Boolean} checked The new checked value
44838 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
44840 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44842 focusClass : undefined,
44844 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44846 fieldClass: "x-form-field",
44848 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44852 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44853 * {tag: "input", type: "checkbox", autocomplete: "off"})
44855 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44857 * @cfg {String} boxLabel The text that appears beside the checkbox
44861 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44865 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44867 valueOff: '0', // value when not checked..
44869 actionMode : 'viewEl',
44872 itemCls : 'x-menu-check-item x-form-item',
44873 groupClass : 'x-menu-group-item',
44874 inputType : 'hidden',
44877 inSetChecked: false, // check that we are not calling self...
44879 inputElement: false, // real input element?
44880 basedOn: false, // ????
44882 isFormField: true, // not sure where this is needed!!!!
44884 onResize : function(){
44885 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44886 if(!this.boxLabel){
44887 this.el.alignTo(this.wrap, 'c-c');
44891 initEvents : function(){
44892 Roo.form.Checkbox.superclass.initEvents.call(this);
44893 this.el.on("click", this.onClick, this);
44894 this.el.on("change", this.onClick, this);
44898 getResizeEl : function(){
44902 getPositionEl : function(){
44907 onRender : function(ct, position){
44908 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44910 if(this.inputValue !== undefined){
44911 this.el.dom.value = this.inputValue;
44914 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44915 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44916 var viewEl = this.wrap.createChild({
44917 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44918 this.viewEl = viewEl;
44919 this.wrap.on('click', this.onClick, this);
44921 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
44922 this.el.on('propertychange', this.setFromHidden, this); //ie
44927 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44928 // viewEl.on('click', this.onClick, this);
44930 //if(this.checked){
44931 this.setChecked(this.checked);
44933 //this.checked = this.el.dom;
44939 initValue : Roo.emptyFn,
44942 * Returns the checked state of the checkbox.
44943 * @return {Boolean} True if checked, else false
44945 getValue : function(){
44947 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44949 return this.valueOff;
44954 onClick : function(){
44955 if (this.disabled) {
44958 this.setChecked(!this.checked);
44960 //if(this.el.dom.checked != this.checked){
44961 // this.setValue(this.el.dom.checked);
44966 * Sets the checked state of the checkbox.
44967 * On is always based on a string comparison between inputValue and the param.
44968 * @param {Boolean/String} value - the value to set
44969 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44971 setValue : function(v,suppressEvent){
44974 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44975 //if(this.el && this.el.dom){
44976 // this.el.dom.checked = this.checked;
44977 // this.el.dom.defaultChecked = this.checked;
44979 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44980 //this.fireEvent("check", this, this.checked);
44983 setChecked : function(state,suppressEvent)
44985 if (this.inSetChecked) {
44986 this.checked = state;
44992 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44994 this.checked = state;
44995 if(suppressEvent !== true){
44996 this.fireEvent('check', this, state);
44998 this.inSetChecked = true;
44999 this.el.dom.value = state ? this.inputValue : this.valueOff;
45000 this.inSetChecked = false;
45003 // handle setting of hidden value by some other method!!?!?
45004 setFromHidden: function()
45009 //console.log("SET FROM HIDDEN");
45010 //alert('setFrom hidden');
45011 this.setValue(this.el.dom.value);
45014 onDestroy : function()
45017 Roo.get(this.viewEl).remove();
45020 Roo.form.Checkbox.superclass.onDestroy.call(this);
45023 setBoxLabel : function(str)
45025 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45030 * Ext JS Library 1.1.1
45031 * Copyright(c) 2006-2007, Ext JS, LLC.
45033 * Originally Released Under LGPL - original licence link has changed is not relivant.
45036 * <script type="text/javascript">
45040 * @class Roo.form.Radio
45041 * @extends Roo.form.Checkbox
45042 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
45043 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45045 * Creates a new Radio
45046 * @param {Object} config Configuration options
45048 Roo.form.Radio = function(){
45049 Roo.form.Radio.superclass.constructor.apply(this, arguments);
45051 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45052 inputType: 'radio',
45055 * If this radio is part of a group, it will return the selected value
45058 getGroupValue : function(){
45059 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45063 onRender : function(ct, position){
45064 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45066 if(this.inputValue !== undefined){
45067 this.el.dom.value = this.inputValue;
45070 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45071 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45072 //var viewEl = this.wrap.createChild({
45073 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45074 //this.viewEl = viewEl;
45075 //this.wrap.on('click', this.onClick, this);
45077 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
45078 //this.el.on('propertychange', this.setFromHidden, this); //ie
45083 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45084 // viewEl.on('click', this.onClick, this);
45087 this.el.dom.checked = 'checked' ;
45093 });Roo.rtf = {}; // namespace
45094 Roo.rtf.Hex = function(hex)
45098 Roo.rtf.Paragraph = function(opts)
45100 this.content = []; ///??? is that used?
45101 };Roo.rtf.Span = function(opts)
45103 this.value = opts.value;
45106 Roo.rtf.Group = function(parent)
45108 // we dont want to acutally store parent - it will make debug a nightmare..
45116 Roo.rtf.Group.prototype = {
45120 addContent : function(node) {
45121 // could set styles...
45122 this.content.push(node);
45124 addChild : function(cn)
45128 // only for images really...
45129 toDataURL : function()
45131 var mimetype = false;
45133 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
45134 mimetype = "image/png";
45136 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45137 mimetype = "image/jpeg";
45140 return 'about:blank'; // ?? error?
45144 var hexstring = this.content[this.content.length-1].value;
45146 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45147 return String.fromCharCode(parseInt(a, 16));
45152 // this looks like it's normally the {rtf{ .... }}
45153 Roo.rtf.Document = function()
45155 // we dont want to acutally store parent - it will make debug a nightmare..
45161 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
45162 addChild : function(cn)
45166 case 'rtlch': // most content seems to be inside this??
45169 this.rtlch.push(cn);
45172 this[cn.type] = cn;
45177 getElementsByType : function(type)
45180 this._getElementsByType(type, ret, this.cn, 'rtf');
45183 _getElementsByType : function (type, ret, search_array, path)
45185 search_array.forEach(function(n,i) {
45186 if (n.type == type) {
45187 n.path = path + '/' + n.type + ':' + i;
45190 if (n.cn.length > 0) {
45191 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45198 Roo.rtf.Ctrl = function(opts)
45200 this.value = opts.value;
45201 this.param = opts.param;
45206 * based on this https://github.com/iarna/rtf-parser
45207 * it's really only designed to extract pict from pasted RTF
45211 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45220 Roo.rtf.Parser = function(text) {
45221 //super({objectMode: true})
45223 this.parserState = this.parseText;
45225 // these are for interpeter...
45227 ///this.parserState = this.parseTop
45228 this.groupStack = [];
45229 this.hexStore = [];
45232 this.groups = []; // where we put the return.
45234 for (var ii = 0; ii < text.length; ++ii) {
45237 if (text[ii] === '\n') {
45243 this.parserState(text[ii]);
45249 Roo.rtf.Parser.prototype = {
45250 text : '', // string being parsed..
45252 controlWordParam : '',
45256 groupStack : false,
45261 row : 1, // reportin?
45265 push : function (el)
45267 var m = 'cmd'+ el.type;
45268 if (typeof(this[m]) == 'undefined') {
45269 Roo.log('invalid cmd:' + el.type);
45275 flushHexStore : function()
45277 if (this.hexStore.length < 1) {
45280 var hexstr = this.hexStore.map(
45285 this.group.addContent( new Roo.rtf.Hex( hexstr ));
45288 this.hexStore.splice(0)
45292 cmdgroupstart : function()
45294 this.flushHexStore();
45296 this.groupStack.push(this.group);
45299 if (this.doc === false) {
45300 this.group = this.doc = new Roo.rtf.Document();
45304 this.group = new Roo.rtf.Group(this.group);
45306 cmdignorable : function()
45308 this.flushHexStore();
45309 this.group.ignorable = true;
45311 cmdendparagraph : function()
45313 this.flushHexStore();
45314 this.group.addContent(new Roo.rtf.Paragraph());
45316 cmdgroupend : function ()
45318 this.flushHexStore();
45319 var endingGroup = this.group;
45322 this.group = this.groupStack.pop();
45324 this.group.addChild(endingGroup);
45329 var doc = this.group || this.doc;
45330 //if (endingGroup instanceof FontTable) {
45331 // doc.fonts = endingGroup.table
45332 //} else if (endingGroup instanceof ColorTable) {
45333 // doc.colors = endingGroup.table
45334 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45335 if (endingGroup.ignorable === false) {
45337 this.groups.push(endingGroup);
45338 // Roo.log( endingGroup );
45340 //Roo.each(endingGroup.content, function(item)) {
45341 // doc.addContent(item);
45343 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45346 cmdtext : function (cmd)
45348 this.flushHexStore();
45349 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45350 //this.group = this.doc
45351 return; // we really don't care about stray text...
45353 this.group.addContent(new Roo.rtf.Span(cmd));
45355 cmdcontrolword : function (cmd)
45357 this.flushHexStore();
45358 if (!this.group.type) {
45359 this.group.type = cmd.value;
45362 this.group.addContent(new Roo.rtf.Ctrl(cmd));
45363 // we actually don't care about ctrl words...
45366 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45367 if (this[method]) {
45368 this[method](cmd.param)
45370 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45374 cmdhexchar : function(cmd) {
45375 this.hexStore.push(cmd);
45377 cmderror : function(cmd) {
45378 throw new Exception (cmd.value);
45383 if (this.text !== '\u0000') this.emitText()
45389 parseText : function(c)
45392 this.parserState = this.parseEscapes;
45393 } else if (c === '{') {
45394 this.emitStartGroup();
45395 } else if (c === '}') {
45396 this.emitEndGroup();
45397 } else if (c === '\x0A' || c === '\x0D') {
45398 // cr/lf are noise chars
45404 parseEscapes: function (c)
45406 if (c === '\\' || c === '{' || c === '}') {
45408 this.parserState = this.parseText;
45410 this.parserState = this.parseControlSymbol;
45411 this.parseControlSymbol(c);
45414 parseControlSymbol: function(c)
45417 this.text += '\u00a0'; // nbsp
45418 this.parserState = this.parseText
45419 } else if (c === '-') {
45420 this.text += '\u00ad'; // soft hyphen
45421 } else if (c === '_') {
45422 this.text += '\u2011'; // non-breaking hyphen
45423 } else if (c === '*') {
45424 this.emitIgnorable();
45425 this.parserState = this.parseText;
45426 } else if (c === "'") {
45427 this.parserState = this.parseHexChar;
45428 } else if (c === '|') { // formula cacter
45429 this.emitFormula();
45430 this.parserState = this.parseText;
45431 } else if (c === ':') { // subentry in an index entry
45432 this.emitIndexSubEntry();
45433 this.parserState = this.parseText;
45434 } else if (c === '\x0a') {
45435 this.emitEndParagraph();
45436 this.parserState = this.parseText;
45437 } else if (c === '\x0d') {
45438 this.emitEndParagraph();
45439 this.parserState = this.parseText;
45441 this.parserState = this.parseControlWord;
45442 this.parseControlWord(c);
45445 parseHexChar: function (c)
45447 if (/^[A-Fa-f0-9]$/.test(c)) {
45449 if (this.hexChar.length >= 2) {
45450 this.emitHexChar();
45451 this.parserState = this.parseText;
45455 this.emitError("Invalid character \"" + c + "\" in hex literal.");
45456 this.parserState = this.parseText;
45459 parseControlWord : function(c)
45462 this.emitControlWord();
45463 this.parserState = this.parseText;
45464 } else if (/^[-\d]$/.test(c)) {
45465 this.parserState = this.parseControlWordParam;
45466 this.controlWordParam += c;
45467 } else if (/^[A-Za-z]$/.test(c)) {
45468 this.controlWord += c;
45470 this.emitControlWord();
45471 this.parserState = this.parseText;
45475 parseControlWordParam : function (c) {
45476 if (/^\d$/.test(c)) {
45477 this.controlWordParam += c;
45478 } else if (c === ' ') {
45479 this.emitControlWord();
45480 this.parserState = this.parseText;
45482 this.emitControlWord();
45483 this.parserState = this.parseText;
45491 emitText : function () {
45492 if (this.text === '') {
45504 emitControlWord : function ()
45507 if (this.controlWord === '') {
45508 this.emitError('empty control word');
45511 type: 'controlword',
45512 value: this.controlWord,
45513 param: this.controlWordParam !== '' && Number(this.controlWordParam),
45519 this.controlWord = '';
45520 this.controlWordParam = '';
45522 emitStartGroup : function ()
45526 type: 'groupstart',
45532 emitEndGroup : function ()
45542 emitIgnorable : function ()
45552 emitHexChar : function ()
45557 value: this.hexChar,
45564 emitError : function (message)
45572 char: this.cpos //,
45573 //stack: new Error().stack
45576 emitEndParagraph : function () {
45579 type: 'endparagraph',
45587 Roo.htmleditor = {};
45590 * @class Roo.htmleditor.Filter
45591 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45592 * @cfg {DomElement} node The node to iterate and filter
45593 * @cfg {boolean|String|Array} tag Tags to replace
45595 * Create a new Filter.
45596 * @param {Object} config Configuration options
45601 Roo.htmleditor.Filter = function(cfg) {
45602 Roo.apply(this.cfg);
45603 // this does not actually call walk as it's really just a abstract class
45607 Roo.htmleditor.Filter.prototype = {
45613 // overrride to do replace comments.
45614 replaceComment : false,
45616 // overrride to do replace or do stuff with tags..
45617 replaceTag : false,
45619 walk : function(dom)
45621 Roo.each( Array.from(dom.childNodes), function( e ) {
45624 case e.nodeType == 8 && this.replaceComment !== false: // comment
45625 this.replaceComment(e);
45628 case e.nodeType != 1: //not a node.
45631 case this.tag === true: // everything
45632 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45633 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45634 if (this.replaceTag && false === this.replaceTag(e)) {
45637 if (e.hasChildNodes()) {
45642 default: // tags .. that do not match.
45643 if (e.hasChildNodes()) {
45654 * @class Roo.htmleditor.FilterAttributes
45655 * clean attributes and styles including http:// etc.. in attribute
45657 * Run a new Attribute Filter
45658 * @param {Object} config Configuration options
45660 Roo.htmleditor.FilterAttributes = function(cfg)
45662 Roo.apply(this, cfg);
45663 this.attrib_black = this.attrib_black || [];
45664 this.attrib_white = this.attrib_white || [];
45666 this.attrib_clean = this.attrib_clean || [];
45667 this.style_white = this.style_white || [];
45668 this.style_black = this.style_black || [];
45669 this.walk(cfg.node);
45672 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45674 tag: true, // all tags
45676 attrib_black : false, // array
45677 attrib_clean : false,
45678 attrib_white : false,
45680 style_white : false,
45681 style_black : false,
45684 replaceTag : function(node)
45686 if (!node.attributes || !node.attributes.length) {
45690 for (var i = node.attributes.length-1; i > -1 ; i--) {
45691 var a = node.attributes[i];
45693 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45694 node.removeAttribute(a.name);
45700 if (a.name.toLowerCase().substr(0,2)=='on') {
45701 node.removeAttribute(a.name);
45706 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45707 node.removeAttribute(a.name);
45710 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45711 this.cleanAttr(node,a.name,a.value); // fixme..
45714 if (a.name == 'style') {
45715 this.cleanStyle(node,a.name,a.value);
45718 /// clean up MS crap..
45719 // tecnically this should be a list of valid class'es..
45722 if (a.name == 'class') {
45723 if (a.value.match(/^Mso/)) {
45724 node.removeAttribute('class');
45727 if (a.value.match(/^body$/)) {
45728 node.removeAttribute('class');
45738 return true; // clean children
45741 cleanAttr: function(node, n,v)
45744 if (v.match(/^\./) || v.match(/^\//)) {
45747 if (v.match(/^(http|https):\/\//)
45748 || v.match(/^mailto:/)
45749 || v.match(/^ftp:/)
45750 || v.match(/^data:/)
45754 if (v.match(/^#/)) {
45757 if (v.match(/^\{/)) { // allow template editing.
45760 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45761 node.removeAttribute(n);
45764 cleanStyle : function(node, n,v)
45766 if (v.match(/expression/)) { //XSS?? should we even bother..
45767 node.removeAttribute(n);
45771 var parts = v.split(/;/);
45774 Roo.each(parts, function(p) {
45775 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45779 var l = p.split(':').shift().replace(/\s+/g,'');
45780 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45782 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45786 // only allow 'c whitelisted system attributes'
45787 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45795 if (clean.length) {
45796 node.setAttribute(n, clean.join(';'));
45798 node.removeAttribute(n);
45807 * @class Roo.htmleditor.FilterBlack
45808 * remove blacklisted elements.
45810 * Run a new Blacklisted Filter
45811 * @param {Object} config Configuration options
45814 Roo.htmleditor.FilterBlack = function(cfg)
45816 Roo.apply(this, cfg);
45817 this.walk(cfg.node);
45820 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45822 tag : true, // all elements.
45824 replaceTag : function(n)
45826 n.parentNode.removeChild(n);
45830 * @class Roo.htmleditor.FilterComment
45833 * Run a new Comments Filter
45834 * @param {Object} config Configuration options
45836 Roo.htmleditor.FilterComment = function(cfg)
45838 this.walk(cfg.node);
45841 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45844 replaceComment : function(n)
45846 n.parentNode.removeChild(n);
45849 * @class Roo.htmleditor.FilterKeepChildren
45850 * remove tags but keep children
45852 * Run a new Keep Children Filter
45853 * @param {Object} config Configuration options
45856 Roo.htmleditor.FilterKeepChildren = function(cfg)
45858 Roo.apply(this, cfg);
45859 if (this.tag === false) {
45860 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45862 this.walk(cfg.node);
45865 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45869 replaceTag : function(node)
45871 // walk children...
45873 var ar = Array.from(node.childNodes);
45875 for (var i = 0; i < ar.length; i++) {
45876 if (ar[i].nodeType == 1) {
45878 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45879 || // array and it matches
45880 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45882 this.replaceTag(ar[i]); // child is blacklisted as well...
45887 ar = Array.from(node.childNodes);
45888 for (var i = 0; i < ar.length; i++) {
45890 node.removeChild(ar[i]);
45891 // what if we need to walk these???
45892 node.parentNode.insertBefore(ar[i], node);
45893 if (this.tag !== false) {
45898 node.parentNode.removeChild(node);
45899 return false; // don't walk children
45904 * @class Roo.htmleditor.FilterParagraph
45905 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45906 * like on 'push' to remove the <p> tags and replace them with line breaks.
45908 * Run a new Paragraph Filter
45909 * @param {Object} config Configuration options
45912 Roo.htmleditor.FilterParagraph = function(cfg)
45914 // no need to apply config.
45915 this.walk(cfg.node);
45918 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45925 replaceTag : function(node)
45928 if (node.childNodes.length == 1 &&
45929 node.childNodes[0].nodeType == 3 &&
45930 node.childNodes[0].textContent.trim().length < 1
45932 // remove and replace with '<BR>';
45933 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45934 return false; // no need to walk..
45936 var ar = Array.from(node.childNodes);
45937 for (var i = 0; i < ar.length; i++) {
45938 node.removeChild(ar[i]);
45939 // what if we need to walk these???
45940 node.parentNode.insertBefore(ar[i], node);
45942 // now what about this?
45946 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45947 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45948 node.parentNode.removeChild(node);
45955 * @class Roo.htmleditor.FilterSpan
45956 * filter span's with no attributes out..
45958 * Run a new Span Filter
45959 * @param {Object} config Configuration options
45962 Roo.htmleditor.FilterSpan = function(cfg)
45964 // no need to apply config.
45965 this.walk(cfg.node);
45968 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45974 replaceTag : function(node)
45976 if (node.attributes && node.attributes.length > 0) {
45977 return true; // walk if there are any.
45979 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45985 * @class Roo.htmleditor.FilterTableWidth
45986 try and remove table width data - as that frequently messes up other stuff.
45988 * was cleanTableWidths.
45990 * Quite often pasting from word etc.. results in tables with column and widths.
45991 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45994 * Run a new Table Filter
45995 * @param {Object} config Configuration options
45998 Roo.htmleditor.FilterTableWidth = function(cfg)
46000 // no need to apply config.
46001 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46002 this.walk(cfg.node);
46005 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46010 replaceTag: function(node) {
46014 if (node.hasAttribute('width')) {
46015 node.removeAttribute('width');
46019 if (node.hasAttribute("style")) {
46022 var styles = node.getAttribute("style").split(";");
46024 Roo.each(styles, function(s) {
46025 if (!s.match(/:/)) {
46028 var kv = s.split(":");
46029 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46032 // what ever is left... we allow.
46035 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46036 if (!nstyle.length) {
46037 node.removeAttribute('style');
46041 return true; // continue doing children..
46044 * @class Roo.htmleditor.FilterWord
46045 * try and clean up all the mess that Word generates.
46047 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
46050 * Run a new Span Filter
46051 * @param {Object} config Configuration options
46054 Roo.htmleditor.FilterWord = function(cfg)
46056 // no need to apply config.
46057 this.walk(cfg.node);
46060 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46066 * Clean up MS wordisms...
46068 replaceTag : function(node)
46071 // no idea what this does - span with text, replaceds with just text.
46073 node.nodeName == 'SPAN' &&
46074 !node.hasAttributes() &&
46075 node.childNodes.length == 1 &&
46076 node.firstChild.nodeName == "#text"
46078 var textNode = node.firstChild;
46079 node.removeChild(textNode);
46080 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46081 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46083 node.parentNode.insertBefore(textNode, node);
46084 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46085 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46088 node.parentNode.removeChild(node);
46089 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46094 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46095 node.parentNode.removeChild(node);
46096 return false; // dont do chidlren
46098 //Roo.log(node.tagName);
46099 // remove - but keep children..
46100 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46101 //Roo.log('-- removed');
46102 while (node.childNodes.length) {
46103 var cn = node.childNodes[0];
46104 node.removeChild(cn);
46105 node.parentNode.insertBefore(cn, node);
46106 // move node to parent - and clean it..
46107 this.replaceTag(cn);
46109 node.parentNode.removeChild(node);
46110 /// no need to iterate chidlren = it's got none..
46111 //this.iterateChildren(node, this.cleanWord);
46112 return false; // no need to iterate children.
46115 if (node.className.length) {
46117 var cn = node.className.split(/\W+/);
46119 Roo.each(cn, function(cls) {
46120 if (cls.match(/Mso[a-zA-Z]+/)) {
46125 node.className = cna.length ? cna.join(' ') : '';
46127 node.removeAttribute("class");
46131 if (node.hasAttribute("lang")) {
46132 node.removeAttribute("lang");
46135 if (node.hasAttribute("style")) {
46137 var styles = node.getAttribute("style").split(";");
46139 Roo.each(styles, function(s) {
46140 if (!s.match(/:/)) {
46143 var kv = s.split(":");
46144 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46147 // what ever is left... we allow.
46150 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46151 if (!nstyle.length) {
46152 node.removeAttribute('style');
46155 return true; // do children
46162 * @class Roo.htmleditor.FilterStyleToTag
46163 * part of the word stuff... - certain 'styles' should be converted to tags.
46165 * font-weight: bold -> bold
46166 * ?? super / subscrit etc..
46169 * Run a new style to tag filter.
46170 * @param {Object} config Configuration options
46172 Roo.htmleditor.FilterStyleToTag = function(cfg)
46176 B : [ 'fontWeight' , 'bold'],
46177 I : [ 'fontStyle' , 'italic'],
46178 //pre : [ 'font-style' , 'italic'],
46179 // h1.. h6 ?? font-size?
46180 SUP : [ 'verticalAlign' , 'super' ],
46181 SUB : [ 'verticalAlign' , 'sub' ]
46186 Roo.apply(this, cfg);
46189 this.walk(cfg.node);
46196 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46198 tag: true, // all tags
46203 replaceTag : function(node)
46207 if (node.getAttribute("style") === null) {
46211 for (var k in this.tags) {
46212 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46214 node.style.removeProperty(this.tags[k][0]);
46217 if (!inject.length) {
46220 var cn = Array.from(node.childNodes);
46222 Roo.each(inject, function(t) {
46223 var nc = node.ownerDocument.createElement(t);
46224 nn.appendChild(nc);
46227 for(var i = 0;i < cn.length;cn++) {
46228 node.removeChild(cn[i]);
46229 nn.appendChild(cn[i]);
46231 return true /// iterate thru
46235 * @class Roo.htmleditor.FilterLongBr
46236 * BR/BR/BR - keep a maximum of 2...
46238 * Run a new Long BR Filter
46239 * @param {Object} config Configuration options
46242 Roo.htmleditor.FilterLongBr = function(cfg)
46244 // no need to apply config.
46245 this.walk(cfg.node);
46248 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46255 replaceTag : function(node)
46258 var ps = node.nextSibling;
46259 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46260 ps = ps.nextSibling;
46263 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
46264 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46268 if (!ps || ps.nodeType != 1) {
46272 if (!ps || ps.tagName != 'BR') {
46281 if (!node.previousSibling) {
46284 var ps = node.previousSibling;
46286 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46287 ps = ps.previousSibling;
46289 if (!ps || ps.nodeType != 1) {
46292 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46293 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46297 node.parentNode.removeChild(node); // remove me...
46299 return false; // no need to do children
46306 * @class Roo.htmleditor.FilterBlock
46307 * removes id / data-block and contenteditable that are associated with blocks
46308 * usage should be done on a cloned copy of the dom
46310 * Run a new Attribute Filter { node : xxxx }}
46311 * @param {Object} config Configuration options
46313 Roo.htmleditor.FilterBlock = function(cfg)
46315 Roo.apply(this, cfg);
46316 var qa = cfg.node.querySelectorAll;
46317 this.removeAttributes('data-block');
46318 this.removeAttributes('contenteditable');
46319 this.removeAttributes('id');
46323 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46325 node: true, // all tags
46328 removeAttributes : function(attr)
46330 var ar = this.node.querySelectorAll('*[' + attr + ']');
46331 for (var i =0;i<ar.length;i++) {
46332 ar[i].removeAttribute(attr);
46341 * This is based loosely on tinymce
46342 * @class Roo.htmleditor.TidySerializer
46343 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46345 * @method Serializer
46346 * @param {Object} settings Name/value settings object.
46350 Roo.htmleditor.TidySerializer = function(settings)
46352 Roo.apply(this, settings);
46354 this.writer = new Roo.htmleditor.TidyWriter(settings);
46359 Roo.htmleditor.TidySerializer.prototype = {
46362 * @param {boolean} inner do the inner of the node.
46369 * Serializes the specified node into a string.
46372 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46373 * @method serialize
46374 * @param {DomElement} node Node instance to serialize.
46375 * @return {String} String with HTML based on DOM tree.
46377 serialize : function(node) {
46379 // = settings.validate;
46380 var writer = this.writer;
46384 3: function(node) {
46386 writer.text(node.nodeValue, node);
46389 8: function(node) {
46390 writer.comment(node.nodeValue);
46392 // Processing instruction
46393 7: function(node) {
46394 writer.pi(node.name, node.nodeValue);
46397 10: function(node) {
46398 writer.doctype(node.nodeValue);
46401 4: function(node) {
46402 writer.cdata(node.nodeValue);
46404 // Document fragment
46405 11: function(node) {
46406 node = node.firstChild;
46412 node = node.nextSibling
46417 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46418 return writer.getContent();
46421 walk: function(node)
46423 var attrName, attrValue, sortedAttrs, i, l, elementRule,
46424 handler = this.handlers[node.nodeType];
46431 var name = node.nodeName;
46432 var isEmpty = node.childNodes.length < 1;
46434 var writer = this.writer;
46435 var attrs = node.attributes;
46438 writer.start(node.nodeName, attrs, isEmpty, node);
46442 node = node.firstChild;
46449 node = node.nextSibling;
46455 // Serialize element and treat all non elements as fragments
46460 * This is based loosely on tinymce
46461 * @class Roo.htmleditor.TidyWriter
46462 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46465 * - not tested much with 'PRE' formated elements.
46471 Roo.htmleditor.TidyWriter = function(settings)
46474 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46475 Roo.apply(this, settings);
46479 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46482 Roo.htmleditor.TidyWriter.prototype = {
46489 // part of state...
46493 last_inline : false,
46498 * Writes the a start element such as <p id="a">.
46501 * @param {String} name Name of the element.
46502 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46503 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46505 start: function(name, attrs, empty, node)
46507 var i, l, attr, value;
46509 // there are some situations where adding line break && indentation will not work. will not work.
46510 // <span / b / i ... formating?
46512 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46513 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46515 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46517 var add_lb = name == 'BR' ? false : in_inline;
46519 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46523 var indentstr = this.indentstr;
46525 // e_inline = elements that can be inline, but still allow \n before and after?
46526 // only 'BR' ??? any others?
46528 // ADD LINE BEFORE tage
46529 if (!this.in_pre) {
46532 if (name == 'BR') {
46534 } else if (this.lastElementEndsWS()) {
46537 // otherwise - no new line. (and dont indent.)
46548 this.html.push(indentstr + '<', name.toLowerCase());
46551 for (i = 0, l = attrs.length; i < l; i++) {
46553 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46559 this.html[this.html.length] = '/>';
46561 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46563 var e_inline = name == 'BR' ? false : this.in_inline;
46565 if (!e_inline && !this.in_pre) {
46572 this.html[this.html.length] = '>';
46574 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46576 if (!in_inline && !in_pre) {
46577 var cn = node.firstChild;
46579 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46583 cn = cn.nextSibling;
46591 indentstr : in_pre ? '' : (this.indentstr + this.indent),
46593 in_inline : in_inline
46595 // add a line after if we are not in a
46597 if (!in_inline && !in_pre) {
46606 lastElementEndsWS : function()
46608 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46609 if (value === false) {
46612 return value.match(/\s+$/);
46617 * Writes the a end element such as </p>.
46620 * @param {String} name Name of the element.
46622 end: function(name) {
46625 var indentstr = '';
46626 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46628 if (!this.in_pre && !in_inline) {
46630 indentstr = this.indentstr;
46632 this.html.push(indentstr + '</', name.toLowerCase(), '>');
46633 this.last_inline = in_inline;
46635 // pop the indent state..
46638 * Writes a text node.
46640 * In pre - we should not mess with the contents.
46644 * @param {String} text String to write out.
46645 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46647 text: function(text, node)
46649 // if not in whitespace critical
46650 if (text.length < 1) {
46654 this.html[this.html.length] = text;
46658 if (this.in_inline) {
46659 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
46661 text = text.replace(/\s+/,' '); // all white space to single white space
46664 // if next tag is '<BR>', then we can trim right..
46665 if (node.nextSibling &&
46666 node.nextSibling.nodeType == 1 &&
46667 node.nextSibling.nodeName == 'BR' )
46669 text = text.replace(/\s+$/g,'');
46671 // if previous tag was a BR, we can also trim..
46672 if (node.previousSibling &&
46673 node.previousSibling.nodeType == 1 &&
46674 node.previousSibling.nodeName == 'BR' )
46676 text = this.indentstr + text.replace(/^\s+/g,'');
46678 if (text.match(/\n/)) {
46679 text = text.replace(
46680 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46682 // remoeve the last whitespace / line break.
46683 text = text.replace(/\n\s+$/,'');
46685 // repace long lines
46689 this.html[this.html.length] = text;
46692 // see if previous element was a inline element.
46693 var indentstr = this.indentstr;
46695 text = text.replace(/\s+/g," "); // all whitespace into single white space.
46697 // should trim left?
46698 if (node.previousSibling &&
46699 node.previousSibling.nodeType == 1 &&
46700 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
46706 text = text.replace(/^\s+/,''); // trim left
46709 // should trim right?
46710 if (node.nextSibling &&
46711 node.nextSibling.nodeType == 1 &&
46712 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
46717 text = text.replace(/\s+$/,''); // trim right
46724 if (text.length < 1) {
46727 if (!text.match(/\n/)) {
46728 this.html.push(indentstr + text);
46732 text = this.indentstr + text.replace(
46733 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46735 // remoeve the last whitespace / line break.
46736 text = text.replace(/\s+$/,'');
46738 this.html.push(text);
46740 // split and indent..
46745 * Writes a cdata node such as <![CDATA[data]]>.
46748 * @param {String} text String to write out inside the cdata.
46750 cdata: function(text) {
46751 this.html.push('<![CDATA[', text, ']]>');
46754 * Writes a comment node such as <!-- Comment -->.
46757 * @param {String} text String to write out inside the comment.
46759 comment: function(text) {
46760 this.html.push('<!--', text, '-->');
46763 * Writes a PI node such as <?xml attr="value" ?>.
46766 * @param {String} name Name of the pi.
46767 * @param {String} text String to write out inside the pi.
46769 pi: function(name, text) {
46770 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
46771 this.indent != '' && this.html.push('\n');
46774 * Writes a doctype node such as <!DOCTYPE data>.
46777 * @param {String} text String to write out inside the doctype.
46779 doctype: function(text) {
46780 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
46783 * Resets the internal buffer if one wants to reuse the writer.
46787 reset: function() {
46788 this.html.length = 0;
46797 * Returns the contents that got serialized.
46799 * @method getContent
46800 * @return {String} HTML contents that got written down.
46802 getContent: function() {
46803 return this.html.join('').replace(/\n$/, '');
46806 pushState : function(cfg)
46808 this.state.push(cfg);
46809 Roo.apply(this, cfg);
46812 popState : function()
46814 if (this.state.length < 1) {
46815 return; // nothing to push
46822 if (this.state.length > 0) {
46823 cfg = this.state[this.state.length-1];
46825 Roo.apply(this, cfg);
46828 addLine: function()
46830 if (this.html.length < 1) {
46835 var value = this.html[this.html.length - 1];
46836 if (value.length > 0 && '\n' !== value) {
46837 this.html.push('\n');
46842 //'pre script noscript style textarea video audio iframe object code'
46843 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
46847 Roo.htmleditor.TidyWriter.inline_elements = [
46848 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
46849 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
46851 Roo.htmleditor.TidyWriter.shortend_elements = [
46852 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
46853 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
46856 Roo.htmleditor.TidyWriter.whitespace_elements = [
46857 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
46859 * This is based loosely on tinymce
46860 * @class Roo.htmleditor.TidyEntities
46862 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46864 * Not 100% sure this is actually used or needed.
46867 Roo.htmleditor.TidyEntities = {
46870 * initialize data..
46872 init : function (){
46874 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
46879 buildEntitiesLookup: function(items, radix) {
46880 var i, chr, entity, lookup = {};
46884 items = typeof(items) == 'string' ? items.split(',') : items;
46885 radix = radix || 10;
46886 // Build entities lookup table
46887 for (i = 0; i < items.length; i += 2) {
46888 chr = String.fromCharCode(parseInt(items[i], radix));
46889 // Only add non base entities
46890 if (!this.baseEntities[chr]) {
46891 entity = '&' + items[i + 1] + ';';
46892 lookup[chr] = entity;
46893 lookup[entity] = chr;
46932 // Needs to be escaped since the YUI compressor would otherwise break the code
46939 // Reverse lookup table for raw entities
46940 reverseEntities : {
46948 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
46949 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
46950 rawCharsRegExp : /[<>&\"\']/g,
46951 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
46952 namedEntities : false,
46953 namedEntitiesData : [
47454 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47456 * @method encodeRaw
47457 * @param {String} text Text to encode.
47458 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47459 * @return {String} Entity encoded text.
47461 encodeRaw: function(text, attr)
47464 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47465 return t.baseEntities[chr] || chr;
47469 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47470 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47471 * and is exposed as the DOMUtils.encode function.
47473 * @method encodeAllRaw
47474 * @param {String} text Text to encode.
47475 * @return {String} Entity encoded text.
47477 encodeAllRaw: function(text) {
47479 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47480 return t.baseEntities[chr] || chr;
47484 * Encodes the specified string using numeric entities. The core entities will be
47485 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47487 * @method encodeNumeric
47488 * @param {String} text Text to encode.
47489 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47490 * @return {String} Entity encoded text.
47492 encodeNumeric: function(text, attr) {
47494 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47495 // Multi byte sequence convert it to a single entity
47496 if (chr.length > 1) {
47497 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47499 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47503 * Encodes the specified string using named entities. The core entities will be encoded
47504 * as named ones but all non lower ascii characters will be encoded into named entities.
47506 * @method encodeNamed
47507 * @param {String} text Text to encode.
47508 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47509 * @param {Object} entities Optional parameter with entities to use.
47510 * @return {String} Entity encoded text.
47512 encodeNamed: function(text, attr, entities) {
47514 entities = entities || this.namedEntities;
47515 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47516 return t.baseEntities[chr] || entities[chr] || chr;
47520 * Returns an encode function based on the name(s) and it's optional entities.
47522 * @method getEncodeFunc
47523 * @param {String} name Comma separated list of encoders for example named,numeric.
47524 * @param {String} entities Optional parameter with entities to use instead of the built in set.
47525 * @return {function} Encode function to be used.
47527 getEncodeFunc: function(name, entities) {
47528 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47530 function encodeNamedAndNumeric(text, attr) {
47531 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47532 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47536 function encodeCustomNamed(text, attr) {
47537 return t.encodeNamed(text, attr, entities);
47539 // Replace + with , to be compatible with previous TinyMCE versions
47540 name = this.makeMap(name.replace(/\+/g, ','));
47541 // Named and numeric encoder
47542 if (name.named && name.numeric) {
47543 return this.encodeNamedAndNumeric;
47549 return encodeCustomNamed;
47551 return this.encodeNamed;
47554 if (name.numeric) {
47555 return this.encodeNumeric;
47558 return this.encodeRaw;
47561 * Decodes the specified string, this will replace entities with raw UTF characters.
47564 * @param {String} text Text to entity decode.
47565 * @return {String} Entity decoded string.
47567 decode: function(text)
47570 return text.replace(this.entityRegExp, function(all, numeric) {
47572 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47573 // Support upper UTF
47574 if (numeric > 65535) {
47576 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47578 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47580 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47583 nativeDecode : function (text) {
47586 makeMap : function (items, delim, map) {
47588 items = items || [];
47589 delim = delim || ',';
47590 if (typeof items == "string") {
47591 items = items.split(delim);
47596 map[items[i]] = {};
47604 Roo.htmleditor.TidyEntities.init();
47606 * @class Roo.htmleditor.KeyEnter
47607 * Handle Enter press..
47608 * @cfg {Roo.HtmlEditorCore} core the editor.
47610 * Create a new Filter.
47611 * @param {Object} config Configuration options
47618 Roo.htmleditor.KeyEnter = function(cfg) {
47619 Roo.apply(this, cfg);
47620 // this does not actually call walk as it's really just a abstract class
47622 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47625 //Roo.htmleditor.KeyEnter.i = 0;
47628 Roo.htmleditor.KeyEnter.prototype = {
47632 keypress : function(e)
47634 if (e.charCode != 13 && e.charCode != 10) {
47635 Roo.log([e.charCode,e]);
47638 e.preventDefault();
47639 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47640 var doc = this.core.doc;
47644 var sel = this.core.getSelection();
47645 var range = sel.getRangeAt(0);
47646 var n = range.commonAncestorContainer;
47647 var pc = range.closest([ 'ol', 'ul']);
47648 var pli = range.closest('li');
47649 if (!pc || e.ctrlKey) {
47650 sel.insertNode('br', 'after');
47652 this.core.undoManager.addEvent();
47653 this.core.fireEditorEvent(e);
47657 // deal with <li> insetion
47658 if (pli.innerText.trim() == '' &&
47659 pli.previousSibling &&
47660 pli.previousSibling.nodeName == 'LI' &&
47661 pli.previousSibling.innerText.trim() == '') {
47662 pli.parentNode.removeChild(pli.previousSibling);
47663 sel.cursorAfter(pc);
47664 this.core.undoManager.addEvent();
47665 this.core.fireEditorEvent(e);
47669 var li = doc.createElement('LI');
47670 li.innerHTML = ' ';
47671 if (!pli || !pli.firstSibling) {
47672 pc.appendChild(li);
47674 pli.parentNode.insertBefore(li, pli.firstSibling);
47676 sel.cursorText (li.firstChild);
47678 this.core.undoManager.addEvent();
47679 this.core.fireEditorEvent(e);
47691 * @class Roo.htmleditor.Block
47692 * Base class for html editor blocks - do not use it directly .. extend it..
47693 * @cfg {DomElement} node The node to apply stuff to.
47694 * @cfg {String} friendly_name the name that appears in the context bar about this block
47695 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
47698 * Create a new Filter.
47699 * @param {Object} config Configuration options
47702 Roo.htmleditor.Block = function(cfg)
47704 // do nothing .. should not be called really.
47707 * factory method to get the block from an element (using cache if necessary)
47709 * @param {HtmlElement} the dom element
47711 Roo.htmleditor.Block.factory = function(node)
47713 var cc = Roo.htmleditor.Block.cache;
47714 var id = Roo.get(node).id;
47715 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
47716 Roo.htmleditor.Block.cache[id].readElement(node);
47717 return Roo.htmleditor.Block.cache[id];
47719 var db = node.getAttribute('data-block');
47721 db = node.nodeName.toLowerCase().toUpperCaseFirst();
47723 var cls = Roo.htmleditor['Block' + db];
47724 if (typeof(cls) == 'undefined') {
47725 //Roo.log(node.getAttribute('data-block'));
47726 Roo.log("OOps missing block : " + 'Block' + db);
47729 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
47730 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
47734 * initalize all Elements from content that are 'blockable'
47736 * @param the body element
47738 Roo.htmleditor.Block.initAll = function(body, type)
47740 if (typeof(type) == 'undefined') {
47741 var ia = Roo.htmleditor.Block.initAll;
47747 Roo.each(Roo.get(body).query(type), function(e) {
47748 Roo.htmleditor.Block.factory(e);
47751 // question goes here... do we need to clear out this cache sometimes?
47752 // or show we make it relivant to the htmleditor.
47753 Roo.htmleditor.Block.cache = {};
47755 Roo.htmleditor.Block.prototype = {
47759 // used by context menu
47760 friendly_name : 'Based Block',
47762 // text for button to delete this element
47763 deleteTitle : false,
47767 * Update a node with values from this object
47768 * @param {DomElement} node
47770 updateElement : function(node)
47772 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
47775 * convert to plain HTML for calling insertAtCursor..
47777 toHTML : function()
47779 return Roo.DomHelper.markup(this.toObject());
47782 * used by readEleemnt to extract data from a node
47783 * may need improving as it's pretty basic
47785 * @param {DomElement} node
47786 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
47787 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
47788 * @param {String} style the style property - eg. text-align
47790 getVal : function(node, tag, attr, style)
47793 if (tag !== true && n.tagName != tag.toUpperCase()) {
47794 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
47795 // but kiss for now.
47796 n = node.getElementsByTagName(tag).item(0);
47801 if (attr === false) {
47804 if (attr == 'html') {
47805 return n.innerHTML;
47807 if (attr == 'style') {
47808 return n.style[style];
47811 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
47815 * create a DomHelper friendly object - for use with
47816 * Roo.DomHelper.markup / overwrite / etc..
47819 toObject : function()
47824 * Read a node that has a 'data-block' property - and extract the values from it.
47825 * @param {DomElement} node - the node
47827 readElement : function(node)
47838 * @class Roo.htmleditor.BlockFigure
47839 * Block that has an image and a figcaption
47840 * @cfg {String} image_src the url for the image
47841 * @cfg {String} align (left|right) alignment for the block default left
47842 * @cfg {String} caption the text to appear below (and in the alt tag)
47843 * @cfg {String} caption_display (block|none) display or not the caption
47844 * @cfg {String|number} image_width the width of the image number or %?
47845 * @cfg {String|number} image_height the height of the image number or %?
47848 * Create a new Filter.
47849 * @param {Object} config Configuration options
47852 Roo.htmleditor.BlockFigure = function(cfg)
47855 this.readElement(cfg.node);
47856 this.updateElement(cfg.node);
47858 Roo.apply(this, cfg);
47860 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
47867 caption_display : 'block',
47873 // margin: '2%', not used
47875 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
47878 // used by context menu
47879 friendly_name : 'Image with caption',
47880 deleteTitle : "Delete Image and Caption",
47882 contextMenu : function(toolbar)
47885 var block = function() {
47886 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
47890 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
47892 var syncValue = toolbar.editorcore.syncValue;
47898 xtype : 'TextItem',
47900 xns : rooui.Toolbar //Boostrap?
47904 text: 'Change Image URL',
47907 click: function (btn, state)
47911 Roo.MessageBox.show({
47912 title : "Image Source URL",
47913 msg : "Enter the url for the image",
47914 buttons: Roo.MessageBox.OKCANCEL,
47915 fn: function(btn, val){
47922 toolbar.editorcore.onEditorEvent();
47926 //multiline: multiline,
47928 value : b.image_src
47932 xns : rooui.Toolbar
47937 text: 'Change Link URL',
47940 click: function (btn, state)
47944 Roo.MessageBox.show({
47945 title : "Link URL",
47946 msg : "Enter the url for the link - leave blank to have no link",
47947 buttons: Roo.MessageBox.OKCANCEL,
47948 fn: function(btn, val){
47955 toolbar.editorcore.onEditorEvent();
47959 //multiline: multiline,
47965 xns : rooui.Toolbar
47969 text: 'Show Video URL',
47972 click: function (btn, state)
47974 Roo.MessageBox.alert("Video URL",
47975 block().video_url == '' ? 'This image is not linked ot a video' :
47976 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
47979 xns : rooui.Toolbar
47984 xtype : 'TextItem',
47986 xns : rooui.Toolbar //Boostrap?
47989 xtype : 'ComboBox',
47990 allowBlank : false,
47991 displayField : 'val',
47994 triggerAction : 'all',
47996 valueField : 'val',
48000 select : function (combo, r, index)
48002 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48004 b.width = r.get('val');
48007 toolbar.editorcore.onEditorEvent();
48012 xtype : 'SimpleStore',
48024 xtype : 'TextItem',
48026 xns : rooui.Toolbar //Boostrap?
48029 xtype : 'ComboBox',
48030 allowBlank : false,
48031 displayField : 'val',
48034 triggerAction : 'all',
48036 valueField : 'val',
48040 select : function (combo, r, index)
48042 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48044 b.align = r.get('val');
48047 toolbar.editorcore.onEditorEvent();
48052 xtype : 'SimpleStore',
48066 text: 'Hide Caption',
48067 name : 'caption_display',
48069 enableToggle : true,
48070 setValue : function(v) {
48071 this.toggle(v == 'block' ? false : true);
48074 toggle: function (btn, state)
48077 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48078 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48081 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48082 toolbar.editorcore.onEditorEvent();
48085 xns : rooui.Toolbar
48091 * create a DomHelper friendly object - for use with
48092 * Roo.DomHelper.markup / overwrite / etc..
48094 toObject : function()
48096 var d = document.createElement('div');
48097 d.innerHTML = this.caption;
48099 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
48101 var iw = this.align == 'center' ? this.width : '100%';
48104 contenteditable : 'false',
48105 src : this.image_src,
48106 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48109 maxWidth : iw + ' !important', // this is not getting rendered?
48116 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48118 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
48123 if (this.href.length > 0) {
48127 contenteditable : 'true',
48135 if (this.video_url.length > 0) {
48140 allowfullscreen : true,
48141 width : 420, // these are for video tricks - that we replace the outer
48143 src : this.video_url,
48149 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48150 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48154 'data-block' : 'Figure',
48156 contenteditable : 'false',
48160 float : this.align ,
48161 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48162 width : this.align == 'center' ? '100%' : this.width,
48165 textAlign : this.align // seems to work for email..
48170 align : this.align,
48176 'data-display' : this.caption_display,
48178 textAlign : 'left',
48180 lineHeight : '24px',
48181 display : this.caption_display,
48182 maxWidth : this.width + ' !important',
48188 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
48193 marginTop : '16px',
48199 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
48201 contenteditable : true,
48216 readElement : function(node)
48218 // this should not really come from the link...
48219 this.video_url = this.getVal(node, 'div', 'src');
48220 this.cls = this.getVal(node, 'div', 'class');
48221 this.href = this.getVal(node, 'a', 'href');
48224 this.image_src = this.getVal(node, 'img', 'src');
48226 this.align = this.getVal(node, 'figure', 'align');
48227 var figcaption = this.getVal(node, 'figcaption', false);
48228 this.caption = this.getVal(figcaption, 'i', 'html');
48230 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48231 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48232 this.width = this.getVal(node, 'figcaption', 'style', 'width');
48233 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48236 removeNode : function()
48253 * @class Roo.htmleditor.BlockTable
48254 * Block that manages a table
48257 * Create a new Filter.
48258 * @param {Object} config Configuration options
48261 Roo.htmleditor.BlockTable = function(cfg)
48264 this.readElement(cfg.node);
48265 this.updateElement(cfg.node);
48267 Roo.apply(this, cfg);
48270 for(var r = 0; r < this.no_row; r++) {
48272 for(var c = 0; c < this.no_col; c++) {
48273 this.rows[r][c] = this.emptyCell();
48280 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48289 // used by context menu
48290 friendly_name : 'Table',
48291 deleteTitle : 'Delete Table',
48292 // context menu is drawn once..
48294 contextMenu : function(toolbar)
48297 var block = function() {
48298 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48302 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48304 var syncValue = toolbar.editorcore.syncValue;
48310 xtype : 'TextItem',
48312 xns : rooui.Toolbar //Boostrap?
48315 xtype : 'ComboBox',
48316 allowBlank : false,
48317 displayField : 'val',
48320 triggerAction : 'all',
48322 valueField : 'val',
48326 select : function (combo, r, index)
48328 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48330 b.width = r.get('val');
48333 toolbar.editorcore.onEditorEvent();
48338 xtype : 'SimpleStore',
48350 xtype : 'TextItem',
48351 text : "Columns: ",
48352 xns : rooui.Toolbar //Boostrap?
48359 click : function (_self, e)
48361 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48362 block().removeColumn();
48364 toolbar.editorcore.onEditorEvent();
48367 xns : rooui.Toolbar
48373 click : function (_self, e)
48375 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48376 block().addColumn();
48378 toolbar.editorcore.onEditorEvent();
48381 xns : rooui.Toolbar
48385 xtype : 'TextItem',
48387 xns : rooui.Toolbar //Boostrap?
48394 click : function (_self, e)
48396 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48397 block().removeRow();
48399 toolbar.editorcore.onEditorEvent();
48402 xns : rooui.Toolbar
48408 click : function (_self, e)
48412 toolbar.editorcore.onEditorEvent();
48415 xns : rooui.Toolbar
48420 text: 'Reset Column Widths',
48423 click : function (_self, e)
48425 block().resetWidths();
48427 toolbar.editorcore.onEditorEvent();
48430 xns : rooui.Toolbar
48441 * create a DomHelper friendly object - for use with
48442 * Roo.DomHelper.markup / overwrite / etc..
48443 * ?? should it be called with option to hide all editing features?
48445 toObject : function()
48450 contenteditable : 'false', // this stops cell selection from picking the table.
48451 'data-block' : 'Table',
48454 border : 'solid 1px #000', // ??? hard coded?
48455 'border-collapse' : 'collapse'
48458 { tag : 'tbody' , cn : [] }
48462 // do we have a head = not really
48464 Roo.each(this.rows, function( row ) {
48469 border : 'solid 1px #000',
48475 ret.cn[0].cn.push(tr);
48476 // does the row have any properties? ?? height?
48478 Roo.each(row, function( cell ) {
48482 contenteditable : 'true',
48483 'data-block' : 'Td',
48487 if (cell.colspan > 1) {
48488 td.colspan = cell.colspan ;
48489 nc += cell.colspan;
48493 if (cell.rowspan > 1) {
48494 td.rowspan = cell.rowspan ;
48503 ncols = Math.max(nc, ncols);
48507 // add the header row..
48516 readElement : function(node)
48518 node = node ? node : this.node ;
48519 this.width = this.getVal(node, true, 'style', 'width') || '100%';
48523 var trs = Array.from(node.rows);
48524 trs.forEach(function(tr) {
48526 this.rows.push(row);
48530 Array.from(tr.cells).forEach(function(td) {
48533 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48534 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48535 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48536 html : td.innerHTML
48538 no_column += add.colspan;
48545 this.no_col = Math.max(this.no_col, no_column);
48552 normalizeRows: function()
48556 this.rows.forEach(function(row) {
48559 row = this.normalizeRow(row);
48561 row.forEach(function(c) {
48562 while (typeof(ret[rid][cid]) != 'undefined') {
48565 if (typeof(ret[rid]) == 'undefined') {
48571 if (c.rowspan < 2) {
48575 for(var i = 1 ;i < c.rowspan; i++) {
48576 if (typeof(ret[rid+i]) == 'undefined') {
48579 ret[rid+i][cid] = c;
48587 normalizeRow: function(row)
48590 row.forEach(function(c) {
48591 if (c.colspan < 2) {
48595 for(var i =0 ;i < c.colspan; i++) {
48603 deleteColumn : function(sel)
48605 if (!sel || sel.type != 'col') {
48608 if (this.no_col < 2) {
48612 this.rows.forEach(function(row) {
48613 var cols = this.normalizeRow(row);
48614 var col = cols[sel.col];
48615 if (col.colspan > 1) {
48625 removeColumn : function()
48627 this.deleteColumn({
48629 col : this.no_col-1
48631 this.updateElement();
48635 addColumn : function()
48638 this.rows.forEach(function(row) {
48639 row.push(this.emptyCell());
48642 this.updateElement();
48645 deleteRow : function(sel)
48647 if (!sel || sel.type != 'row') {
48651 if (this.no_row < 2) {
48655 var rows = this.normalizeRows();
48658 rows[sel.row].forEach(function(col) {
48659 if (col.rowspan > 1) {
48662 col.remove = 1; // flage it as removed.
48667 this.rows.forEach(function(row) {
48669 row.forEach(function(c) {
48670 if (typeof(c.remove) == 'undefined') {
48675 if (newrow.length > 0) {
48679 this.rows = newrows;
48684 this.updateElement();
48687 removeRow : function()
48691 row : this.no_row-1
48697 addRow : function()
48701 for (var i = 0; i < this.no_col; i++ ) {
48703 row.push(this.emptyCell());
48706 this.rows.push(row);
48707 this.updateElement();
48711 // the default cell object... at present...
48712 emptyCell : function() {
48713 return (new Roo.htmleditor.BlockTd({})).toObject();
48718 removeNode : function()
48725 resetWidths : function()
48727 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
48728 var nn = Roo.htmleditor.Block.factory(n);
48730 nn.updateElement(n);
48743 * since selections really work on the table cell, then editing really should work from there
48745 * The original plan was to support merging etc... - but that may not be needed yet..
48747 * So this simple version will support:
48749 * adjust the width +/-
48750 * reset the width...
48759 * @class Roo.htmleditor.BlockTable
48760 * Block that manages a table
48763 * Create a new Filter.
48764 * @param {Object} config Configuration options
48767 Roo.htmleditor.BlockTd = function(cfg)
48770 this.readElement(cfg.node);
48771 this.updateElement(cfg.node);
48773 Roo.apply(this, cfg);
48778 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
48783 textAlign : 'left',
48790 // used by context menu
48791 friendly_name : 'Table Cell',
48792 deleteTitle : false, // use our customer delete
48794 // context menu is drawn once..
48796 contextMenu : function(toolbar)
48799 var cell = function() {
48800 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48803 var table = function() {
48804 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
48808 var saveSel = function()
48810 lr = toolbar.editorcore.getSelection().getRangeAt(0);
48812 var restoreSel = function()
48816 toolbar.editorcore.focus();
48817 var cr = toolbar.editorcore.getSelection();
48818 cr.removeAllRanges();
48820 toolbar.editorcore.onEditorEvent();
48821 }).defer(10, this);
48827 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48829 var syncValue = toolbar.editorcore.syncValue;
48836 text : 'Edit Table',
48838 click : function() {
48839 var t = toolbar.tb.selectedNode.closest('table');
48840 toolbar.editorcore.selectNode(t);
48841 toolbar.editorcore.onEditorEvent();
48850 xtype : 'TextItem',
48851 text : "Column Width: ",
48852 xns : rooui.Toolbar
48859 click : function (_self, e)
48861 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48862 cell().shrinkColumn();
48864 toolbar.editorcore.onEditorEvent();
48867 xns : rooui.Toolbar
48873 click : function (_self, e)
48875 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48876 cell().growColumn();
48878 toolbar.editorcore.onEditorEvent();
48881 xns : rooui.Toolbar
48885 xtype : 'TextItem',
48886 text : "Vertical Align: ",
48887 xns : rooui.Toolbar //Boostrap?
48890 xtype : 'ComboBox',
48891 allowBlank : false,
48892 displayField : 'val',
48895 triggerAction : 'all',
48897 valueField : 'val',
48901 select : function (combo, r, index)
48903 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48905 b.valign = r.get('val');
48908 toolbar.editorcore.onEditorEvent();
48913 xtype : 'SimpleStore',
48917 ['bottom'] // there are afew more...
48925 xtype : 'TextItem',
48926 text : "Merge Cells: ",
48927 xns : rooui.Toolbar
48936 click : function (_self, e)
48938 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48939 cell().mergeRight();
48940 //block().growColumn();
48942 toolbar.editorcore.onEditorEvent();
48945 xns : rooui.Toolbar
48952 click : function (_self, e)
48954 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48955 cell().mergeBelow();
48956 //block().growColumn();
48958 toolbar.editorcore.onEditorEvent();
48961 xns : rooui.Toolbar
48964 xtype : 'TextItem',
48966 xns : rooui.Toolbar
48974 click : function (_self, e)
48976 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48979 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48980 toolbar.editorcore.onEditorEvent();
48984 xns : rooui.Toolbar
48988 xns : rooui.Toolbar
48997 xns : rooui.Toolbar,
49006 click : function (_self, e)
49010 cell().deleteColumn();
49012 toolbar.editorcore.selectNode(t.node);
49013 toolbar.editorcore.onEditorEvent();
49022 click : function (_self, e)
49025 cell().deleteRow();
49028 toolbar.editorcore.selectNode(t.node);
49029 toolbar.editorcore.onEditorEvent();
49036 xtype : 'Separator',
49043 click : function (_self, e)
49046 var nn = t.node.nextSibling || t.node.previousSibling;
49047 t.node.parentNode.removeChild(t.node);
49049 toolbar.editorcore.selectNode(nn, true);
49051 toolbar.editorcore.onEditorEvent();
49061 // align... << fixme
49069 * create a DomHelper friendly object - for use with
49070 * Roo.DomHelper.markup / overwrite / etc..
49071 * ?? should it be called with option to hide all editing features?
49074 * create a DomHelper friendly object - for use with
49075 * Roo.DomHelper.markup / overwrite / etc..
49076 * ?? should it be called with option to hide all editing features?
49078 toObject : function()
49083 contenteditable : 'true', // this stops cell selection from picking the table.
49084 'data-block' : 'Td',
49085 valign : this.valign,
49087 'text-align' : this.textAlign,
49088 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49089 'border-collapse' : 'collapse',
49090 padding : '6px', // 8 for desktop / 4 for mobile
49091 'vertical-align': this.valign
49095 if (this.width != '') {
49096 ret.width = this.width;
49097 ret.style.width = this.width;
49101 if (this.colspan > 1) {
49102 ret.colspan = this.colspan ;
49104 if (this.rowspan > 1) {
49105 ret.rowspan = this.rowspan ;
49114 readElement : function(node)
49116 node = node ? node : this.node ;
49117 this.width = node.style.width;
49118 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49119 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49120 this.html = node.innerHTML;
49125 // the default cell object... at present...
49126 emptyCell : function() {
49130 textAlign : 'left',
49131 html : " " // is this going to be editable now?
49136 removeNode : function()
49138 return this.node.closest('table');
49146 toTableArray : function()
49149 var tab = this.node.closest('tr').closest('table');
49150 Array.from(tab.rows).forEach(function(r, ri){
49154 this.colWidths = [];
49155 var all_auto = true;
49156 Array.from(tab.rows).forEach(function(r, ri){
49159 Array.from(r.cells).forEach(function(ce, ci){
49164 colspan : ce.colSpan,
49165 rowspan : ce.rowSpan
49167 if (ce.isEqualNode(this.node)) {
49170 // if we have been filled up by a row?
49171 if (typeof(ret[rn][cn]) != 'undefined') {
49172 while(typeof(ret[rn][cn]) != 'undefined') {
49178 if (typeof(this.colWidths[cn]) == 'undefined') {
49179 this.colWidths[cn] = ce.style.width;
49180 if (this.colWidths[cn] != '') {
49186 if (c.colspan < 2 && c.rowspan < 2 ) {
49191 for(var j = 0; j < c.rowspan; j++) {
49192 if (typeof(ret[rn+j]) == 'undefined') {
49193 continue; // we have a problem..
49196 for(var i = 0; i < c.colspan; i++) {
49197 ret[rn+j][cn+i] = c;
49206 // initalize widths.?
49207 // either all widths or no widths..
49209 this.colWidths[0] = false; // no widths flag.
49220 mergeRight: function()
49223 // get the contents of the next cell along..
49224 var tr = this.node.closest('tr');
49225 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49226 if (i >= tr.childNodes.length - 1) {
49227 return; // no cells on right to merge with.
49229 var table = this.toTableArray();
49231 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49232 return; // nothing right?
49234 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49235 // right cell - must be same rowspan and on the same row.
49236 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49237 return; // right hand side is not same rowspan.
49242 this.node.innerHTML += ' ' + rc.cell.innerHTML;
49243 tr.removeChild(rc.cell);
49244 this.colspan += rc.colspan;
49245 this.node.setAttribute('colspan', this.colspan);
49250 mergeBelow : function()
49252 var table = this.toTableArray();
49253 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49254 return; // no row below
49256 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49257 return; // nothing right?
49259 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49261 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49262 return; // right hand side is not same rowspan.
49264 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
49265 rc.cell.parentNode.removeChild(rc.cell);
49266 this.rowspan += rc.rowspan;
49267 this.node.setAttribute('rowspan', this.rowspan);
49272 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49275 var table = this.toTableArray();
49276 var cd = this.cellData;
49280 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49284 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49285 if (r == cd.row && c == cd.col) {
49286 this.node.removeAttribute('rowspan');
49287 this.node.removeAttribute('colspan');
49291 var ntd = this.node.cloneNode(); // which col/row should be 0..
49292 ntd.removeAttribute('id'); //
49293 //ntd.style.width = '';
49294 ntd.innerHTML = '';
49295 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
49299 this.redrawAllCells(table);
49307 redrawAllCells: function(table)
49311 var tab = this.node.closest('tr').closest('table');
49312 var ctr = tab.rows[0].parentNode;
49313 Array.from(tab.rows).forEach(function(r, ri){
49315 Array.from(r.cells).forEach(function(ce, ci){
49316 ce.parentNode.removeChild(ce);
49318 r.parentNode.removeChild(r);
49320 for(var r = 0 ; r < table.length; r++) {
49321 var re = tab.rows[r];
49323 var re = tab.ownerDocument.createElement('tr');
49324 ctr.appendChild(re);
49325 for(var c = 0 ; c < table[r].length; c++) {
49326 if (table[r][c].cell === false) {
49330 re.appendChild(table[r][c].cell);
49332 table[r][c].cell = false;
49337 updateWidths : function(table)
49339 for(var r = 0 ; r < table.length; r++) {
49341 for(var c = 0 ; c < table[r].length; c++) {
49342 if (table[r][c].cell === false) {
49346 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49347 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49348 el.width = Math.floor(this.colWidths[c]) +'%';
49349 el.updateElement(el.node);
49351 table[r][c].cell = false; // done
49355 normalizeWidths : function(table)
49358 if (this.colWidths[0] === false) {
49359 var nw = 100.0 / this.colWidths.length;
49360 this.colWidths.forEach(function(w,i) {
49361 this.colWidths[i] = nw;
49366 var t = 0, missing = [];
49368 this.colWidths.forEach(function(w,i) {
49370 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49371 var add = this.colWidths[i];
49380 var nc = this.colWidths.length;
49381 if (missing.length) {
49382 var mult = (nc - missing.length) / (1.0 * nc);
49384 var ew = (100 -t) / (1.0 * missing.length);
49385 this.colWidths.forEach(function(w,i) {
49387 this.colWidths[i] = w * mult;
49391 this.colWidths[i] = ew;
49393 // have to make up numbers..
49396 // now we should have all the widths..
49401 shrinkColumn : function()
49403 var table = this.toTableArray();
49404 this.normalizeWidths(table);
49405 var col = this.cellData.col;
49406 var nw = this.colWidths[col] * 0.8;
49410 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49411 this.colWidths.forEach(function(w,i) {
49413 this.colWidths[i] = nw;
49416 this.colWidths[i] += otherAdd
49418 this.updateWidths(table);
49421 growColumn : function()
49423 var table = this.toTableArray();
49424 this.normalizeWidths(table);
49425 var col = this.cellData.col;
49426 var nw = this.colWidths[col] * 1.2;
49430 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49431 this.colWidths.forEach(function(w,i) {
49433 this.colWidths[i] = nw;
49436 this.colWidths[i] -= otherSub
49438 this.updateWidths(table);
49441 deleteRow : function()
49443 // delete this rows 'tr'
49444 // if any of the cells in this row have a rowspan > 1 && row!= this row..
49445 // then reduce the rowspan.
49446 var table = this.toTableArray();
49447 // this.cellData.row;
49448 for (var i =0;i< table[this.cellData.row].length ; i++) {
49449 var c = table[this.cellData.row][i];
49450 if (c.row != this.cellData.row) {
49453 c.cell.setAttribute('rowspan', c.rowspan);
49456 if (c.rowspan > 1) {
49458 c.cell.setAttribute('rowspan', c.rowspan);
49461 table.splice(this.cellData.row,1);
49462 this.redrawAllCells(table);
49465 deleteColumn : function()
49467 var table = this.toTableArray();
49469 for (var i =0;i< table.length ; i++) {
49470 var c = table[i][this.cellData.col];
49471 if (c.col != this.cellData.col) {
49472 table[i][this.cellData.col].colspan--;
49473 } else if (c.colspan > 1) {
49475 c.cell.setAttribute('colspan', c.colspan);
49477 table[i].splice(this.cellData.col,1);
49480 this.redrawAllCells(table);
49488 //<script type="text/javascript">
49491 * Based Ext JS Library 1.1.1
49492 * Copyright(c) 2006-2007, Ext JS, LLC.
49498 * @class Roo.HtmlEditorCore
49499 * @extends Roo.Component
49500 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49502 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49505 Roo.HtmlEditorCore = function(config){
49508 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49513 * @event initialize
49514 * Fires when the editor is fully initialized (including the iframe)
49515 * @param {Roo.HtmlEditorCore} this
49520 * Fires when the editor is first receives the focus. Any insertion must wait
49521 * until after this event.
49522 * @param {Roo.HtmlEditorCore} this
49526 * @event beforesync
49527 * Fires before the textarea is updated with content from the editor iframe. Return false
49528 * to cancel the sync.
49529 * @param {Roo.HtmlEditorCore} this
49530 * @param {String} html
49534 * @event beforepush
49535 * Fires before the iframe editor is updated with content from the textarea. Return false
49536 * to cancel the push.
49537 * @param {Roo.HtmlEditorCore} this
49538 * @param {String} html
49543 * Fires when the textarea is updated with content from the editor iframe.
49544 * @param {Roo.HtmlEditorCore} this
49545 * @param {String} html
49550 * Fires when the iframe editor is updated with content from the textarea.
49551 * @param {Roo.HtmlEditorCore} this
49552 * @param {String} html
49557 * @event editorevent
49558 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49559 * @param {Roo.HtmlEditorCore} this
49566 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49568 // defaults : white / black...
49569 this.applyBlacklists();
49576 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
49580 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
49586 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
49591 * @cfg {Number} height (in pixels)
49595 * @cfg {Number} width (in pixels)
49599 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49600 * if you are doing an email editor, this probably needs disabling, it's designed
49605 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49607 enableBlocks : true,
49609 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49612 stylesheets: false,
49614 * @cfg {String} language default en - language of text (usefull for rtl languages)
49620 * @cfg {boolean} allowComments - default false - allow comments in HTML source
49621 * - by default they are stripped - if you are editing email you may need this.
49623 allowComments: false,
49627 // private properties
49628 validationEvent : false,
49630 initialized : false,
49632 sourceEditMode : false,
49633 onFocus : Roo.emptyFn,
49635 hideMode:'offsets',
49639 // blacklist + whitelisted elements..
49646 undoManager : false,
49648 * Protected method that will not generally be called directly. It
49649 * is called when the editor initializes the iframe with HTML contents. Override this method if you
49650 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
49652 getDocMarkup : function(){
49656 // inherit styels from page...??
49657 if (this.stylesheets === false) {
49659 Roo.get(document.head).select('style').each(function(node) {
49660 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49663 Roo.get(document.head).select('link').each(function(node) {
49664 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49667 } else if (!this.stylesheets.length) {
49669 st = '<style type="text/css">' +
49670 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49673 for (var i in this.stylesheets) {
49674 if (typeof(this.stylesheets[i]) != 'string') {
49677 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
49682 st += '<style type="text/css">' +
49683 'IMG { cursor: pointer } ' +
49686 st += '<meta name="google" content="notranslate">';
49688 var cls = 'notranslate roo-htmleditor-body';
49690 if(this.bodyCls.length){
49691 cls += ' ' + this.bodyCls;
49694 return '<html class="notranslate" translate="no"><head>' + st +
49695 //<style type="text/css">' +
49696 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49698 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
49702 onRender : function(ct, position)
49705 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
49706 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
49709 this.el.dom.style.border = '0 none';
49710 this.el.dom.setAttribute('tabIndex', -1);
49711 this.el.addClass('x-hidden hide');
49715 if(Roo.isIE){ // fix IE 1px bogus margin
49716 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
49720 this.frameId = Roo.id();
49724 var iframe = this.owner.wrap.createChild({
49726 cls: 'form-control', // bootstrap..
49728 name: this.frameId,
49729 frameBorder : 'no',
49730 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
49735 this.iframe = iframe.dom;
49737 this.assignDocWin();
49739 this.doc.designMode = 'on';
49742 this.doc.write(this.getDocMarkup());
49746 var task = { // must defer to wait for browser to be ready
49748 //console.log("run task?" + this.doc.readyState);
49749 this.assignDocWin();
49750 if(this.doc.body || this.doc.readyState == 'complete'){
49752 this.doc.designMode="on";
49757 Roo.TaskMgr.stop(task);
49758 this.initEditor.defer(10, this);
49765 Roo.TaskMgr.start(task);
49770 onResize : function(w, h)
49772 Roo.log('resize: ' +w + ',' + h );
49773 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
49777 if(typeof w == 'number'){
49779 this.iframe.style.width = w + 'px';
49781 if(typeof h == 'number'){
49783 this.iframe.style.height = h + 'px';
49785 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
49792 * Toggles the editor between standard and source edit mode.
49793 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49795 toggleSourceEdit : function(sourceEditMode){
49797 this.sourceEditMode = sourceEditMode === true;
49799 if(this.sourceEditMode){
49801 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
49804 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
49805 //this.iframe.className = '';
49808 //this.setSize(this.owner.wrap.getSize());
49809 //this.fireEvent('editmodechange', this, this.sourceEditMode);
49816 * Protected method that will not generally be called directly. If you need/want
49817 * custom HTML cleanup, this is the method you should override.
49818 * @param {String} html The HTML to be cleaned
49819 * return {String} The cleaned HTML
49821 cleanHtml : function(html)
49823 html = String(html);
49824 if(html.length > 5){
49825 if(Roo.isSafari){ // strip safari nonsense
49826 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
49829 if(html == ' '){
49836 * HTML Editor -> Textarea
49837 * Protected method that will not generally be called directly. Syncs the contents
49838 * of the editor iframe with the textarea.
49840 syncValue : function()
49842 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
49843 if(this.initialized){
49845 this.undoManager.addEvent();
49848 var bd = (this.doc.body || this.doc.documentElement);
49851 var sel = this.win.getSelection();
49853 var div = document.createElement('div');
49854 div.innerHTML = bd.innerHTML;
49855 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
49856 if (gtx.length > 0) {
49857 var rm = gtx.item(0).parentNode;
49858 rm.parentNode.removeChild(rm);
49862 if (this.enableBlocks) {
49863 new Roo.htmleditor.FilterBlock({ node : div });
49866 var tidy = new Roo.htmleditor.TidySerializer({
49869 var html = tidy.serialize(div);
49873 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
49874 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
49876 html = '<div style="'+m[0]+'">' + html + '</div>';
49879 html = this.cleanHtml(html);
49880 // fix up the special chars.. normaly like back quotes in word...
49881 // however we do not want to do this with chinese..
49882 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
49884 var cc = match.charCodeAt();
49886 // Get the character value, handling surrogate pairs
49887 if (match.length == 2) {
49888 // It's a surrogate pair, calculate the Unicode code point
49889 var high = match.charCodeAt(0) - 0xD800;
49890 var low = match.charCodeAt(1) - 0xDC00;
49891 cc = (high * 0x400) + low + 0x10000;
49893 (cc >= 0x4E00 && cc < 0xA000 ) ||
49894 (cc >= 0x3400 && cc < 0x4E00 ) ||
49895 (cc >= 0xf900 && cc < 0xfb00 )
49900 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
49901 return "&#" + cc + ";";
49908 if(this.owner.fireEvent('beforesync', this, html) !== false){
49909 this.el.dom.value = html;
49910 this.owner.fireEvent('sync', this, html);
49916 * TEXTAREA -> EDITABLE
49917 * Protected method that will not generally be called directly. Pushes the value of the textarea
49918 * into the iframe editor.
49920 pushValue : function()
49922 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
49923 if(this.initialized){
49924 var v = this.el.dom.value.trim();
49927 if(this.owner.fireEvent('beforepush', this, v) !== false){
49928 var d = (this.doc.body || this.doc.documentElement);
49931 this.el.dom.value = d.innerHTML;
49932 this.owner.fireEvent('push', this, v);
49934 if (this.autoClean) {
49935 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
49936 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
49938 if (this.enableBlocks) {
49939 Roo.htmleditor.Block.initAll(this.doc.body);
49942 this.updateLanguage();
49944 var lc = this.doc.body.lastChild;
49945 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
49946 // add an extra line at the end.
49947 this.doc.body.appendChild(this.doc.createElement('br'));
49955 deferFocus : function(){
49956 this.focus.defer(10, this);
49960 focus : function(){
49961 if(this.win && !this.sourceEditMode){
49968 assignDocWin: function()
49970 var iframe = this.iframe;
49973 this.doc = iframe.contentWindow.document;
49974 this.win = iframe.contentWindow;
49976 // if (!Roo.get(this.frameId)) {
49979 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
49980 // this.win = Roo.get(this.frameId).dom.contentWindow;
49982 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
49986 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
49987 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
49992 initEditor : function(){
49993 //console.log("INIT EDITOR");
49994 this.assignDocWin();
49998 this.doc.designMode="on";
50000 this.doc.write(this.getDocMarkup());
50003 var dbody = (this.doc.body || this.doc.documentElement);
50004 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50005 // this copies styles from the containing element into thsi one..
50006 // not sure why we need all of this..
50007 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50009 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50010 //ss['background-attachment'] = 'fixed'; // w3c
50011 dbody.bgProperties = 'fixed'; // ie
50012 dbody.setAttribute("translate", "no");
50014 //Roo.DomHelper.applyStyles(dbody, ss);
50015 Roo.EventManager.on(this.doc, {
50017 'mouseup': this.onEditorEvent,
50018 'dblclick': this.onEditorEvent,
50019 'click': this.onEditorEvent,
50020 'keyup': this.onEditorEvent,
50025 Roo.EventManager.on(this.doc, {
50026 'paste': this.onPasteEvent,
50030 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50033 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50034 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50036 this.initialized = true;
50039 // initialize special key events - enter
50040 new Roo.htmleditor.KeyEnter({core : this});
50044 this.owner.fireEvent('initialize', this);
50047 // this is to prevent a href clicks resulting in a redirect?
50049 onPasteEvent : function(e,v)
50051 // I think we better assume paste is going to be a dirty load of rubish from word..
50053 // even pasting into a 'email version' of this widget will have to clean up that mess.
50054 var cd = (e.browserEvent.clipboardData || window.clipboardData);
50056 // check what type of paste - if it's an image, then handle it differently.
50057 if (cd.files && cd.files.length > 0) {
50059 var urlAPI = (window.createObjectURL && window) ||
50060 (window.URL && URL.revokeObjectURL && URL) ||
50061 (window.webkitURL && webkitURL);
50063 var url = urlAPI.createObjectURL( cd.files[0]);
50064 this.insertAtCursor('<img src=" + url + ">');
50067 if (cd.types.indexOf('text/html') < 0 ) {
50071 var html = cd.getData('text/html'); // clipboard event
50072 if (cd.types.indexOf('text/rtf') > -1) {
50073 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50074 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50079 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50080 .map(function(g) { return g.toDataURL(); })
50081 .filter(function(g) { return g != 'about:blank'; });
50084 html = this.cleanWordChars(html);
50086 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50089 var sn = this.getParentElement();
50090 // check if d contains a table, and prevent nesting??
50091 //Roo.log(d.getElementsByTagName('table'));
50093 //Roo.log(sn.closest('table'));
50094 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50095 e.preventDefault();
50096 this.insertAtCursor("You can not nest tables");
50097 //Roo.log("prevent?"); // fixme -
50101 if (images.length > 0) {
50102 Roo.each(d.getElementsByTagName('img'), function(img, i) {
50103 img.setAttribute('src', images[i]);
50106 if (this.autoClean) {
50107 new Roo.htmleditor.FilterStyleToTag({ node : d });
50108 new Roo.htmleditor.FilterAttributes({
50110 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan'],
50111 attrib_clean : ['href', 'src' ]
50113 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50114 // should be fonts..
50115 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
50116 new Roo.htmleditor.FilterParagraph({ node : d });
50117 new Roo.htmleditor.FilterSpan({ node : d });
50118 new Roo.htmleditor.FilterLongBr({ node : d });
50120 if (this.enableBlocks) {
50122 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50123 if (img.closest('figure')) { // assume!! that it's aready
50126 var fig = new Roo.htmleditor.BlockFigure({
50127 image_src : img.src
50129 fig.updateElement(img); // replace it..
50135 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
50136 if (this.enableBlocks) {
50137 Roo.htmleditor.Block.initAll(this.doc.body);
50141 e.preventDefault();
50143 // default behaveiour should be our local cleanup paste? (optional?)
50144 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50145 //this.owner.fireEvent('paste', e, v);
50148 onDestroy : function(){
50154 //for (var i =0; i < this.toolbars.length;i++) {
50155 // // fixme - ask toolbars for heights?
50156 // this.toolbars[i].onDestroy();
50159 //this.wrap.dom.innerHTML = '';
50160 //this.wrap.remove();
50165 onFirstFocus : function(){
50167 this.assignDocWin();
50168 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50170 this.activated = true;
50173 if(Roo.isGecko){ // prevent silly gecko errors
50175 var s = this.win.getSelection();
50176 if(!s.focusNode || s.focusNode.nodeType != 3){
50177 var r = s.getRangeAt(0);
50178 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50183 this.execCmd('useCSS', true);
50184 this.execCmd('styleWithCSS', false);
50187 this.owner.fireEvent('activate', this);
50191 adjustFont: function(btn){
50192 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50193 //if(Roo.isSafari){ // safari
50196 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50197 if(Roo.isSafari){ // safari
50198 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50199 v = (v < 10) ? 10 : v;
50200 v = (v > 48) ? 48 : v;
50201 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50206 v = Math.max(1, v+adjust);
50208 this.execCmd('FontSize', v );
50211 onEditorEvent : function(e)
50215 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50216 return; // we do not handle this.. (undo manager does..)
50218 // in theory this detects if the last element is not a br, then we try and do that.
50219 // its so clicking in space at bottom triggers adding a br and moving the cursor.
50221 e.target.nodeName == 'BODY' &&
50222 e.type == "mouseup" &&
50223 this.doc.body.lastChild
50225 var lc = this.doc.body.lastChild;
50226 // gtx-trans is google translate plugin adding crap.
50227 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50228 lc = lc.previousSibling;
50230 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50231 // if last element is <BR> - then dont do anything.
50233 var ns = this.doc.createElement('br');
50234 this.doc.body.appendChild(ns);
50235 range = this.doc.createRange();
50236 range.setStartAfter(ns);
50237 range.collapse(true);
50238 var sel = this.win.getSelection();
50239 sel.removeAllRanges();
50240 sel.addRange(range);
50246 this.fireEditorEvent(e);
50247 // this.updateToolbar();
50248 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50251 fireEditorEvent: function(e)
50253 this.owner.fireEvent('editorevent', this, e);
50256 insertTag : function(tg)
50258 // could be a bit smarter... -> wrap the current selected tRoo..
50259 if (tg.toLowerCase() == 'span' ||
50260 tg.toLowerCase() == 'code' ||
50261 tg.toLowerCase() == 'sup' ||
50262 tg.toLowerCase() == 'sub'
50265 range = this.createRange(this.getSelection());
50266 var wrappingNode = this.doc.createElement(tg.toLowerCase());
50267 wrappingNode.appendChild(range.extractContents());
50268 range.insertNode(wrappingNode);
50275 this.execCmd("formatblock", tg);
50276 this.undoManager.addEvent();
50279 insertText : function(txt)
50283 var range = this.createRange();
50284 range.deleteContents();
50285 //alert(Sender.getAttribute('label'));
50287 range.insertNode(this.doc.createTextNode(txt));
50288 this.undoManager.addEvent();
50294 * Executes a Midas editor command on the editor document and performs necessary focus and
50295 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50296 * @param {String} cmd The Midas command
50297 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50299 relayCmd : function(cmd, value)
50303 case 'justifyleft':
50304 case 'justifyright':
50305 case 'justifycenter':
50306 // if we are in a cell, then we will adjust the
50307 var n = this.getParentElement();
50308 var td = n.closest('td');
50310 var bl = Roo.htmleditor.Block.factory(td);
50311 bl.textAlign = cmd.replace('justify','');
50312 bl.updateElement();
50313 this.owner.fireEvent('editorevent', this);
50316 this.execCmd('styleWithCSS', true); //
50320 // if there is no selection, then we insert, and set the curson inside it..
50321 this.execCmd('styleWithCSS', false);
50331 this.execCmd(cmd, value);
50332 this.owner.fireEvent('editorevent', this);
50333 //this.updateToolbar();
50334 this.owner.deferFocus();
50338 * Executes a Midas editor command directly on the editor document.
50339 * For visual commands, you should use {@link #relayCmd} instead.
50340 * <b>This should only be called after the editor is initialized.</b>
50341 * @param {String} cmd The Midas command
50342 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50344 execCmd : function(cmd, value){
50345 this.doc.execCommand(cmd, false, value === undefined ? null : value);
50352 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50354 * @param {String} text | dom node..
50356 insertAtCursor : function(text)
50359 if(!this.activated){
50363 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50367 // from jquery ui (MIT licenced)
50369 var win = this.win;
50371 if (win.getSelection && win.getSelection().getRangeAt) {
50373 // delete the existing?
50375 this.createRange(this.getSelection()).deleteContents();
50376 range = win.getSelection().getRangeAt(0);
50377 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50378 range.insertNode(node);
50379 range = range.cloneRange();
50380 range.collapse(false);
50382 win.getSelection().removeAllRanges();
50383 win.getSelection().addRange(range);
50387 } else if (win.document.selection && win.document.selection.createRange) {
50388 // no firefox support
50389 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50390 win.document.selection.createRange().pasteHTML(txt);
50393 // no firefox support
50394 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50395 this.execCmd('InsertHTML', txt);
50403 mozKeyPress : function(e){
50405 var c = e.getCharCode(), cmd;
50408 c = String.fromCharCode(c).toLowerCase();
50422 // this.cleanUpPaste.defer(100, this);
50428 this.relayCmd(cmd);
50429 //this.win.focus();
50430 //this.execCmd(cmd);
50431 //this.deferFocus();
50432 e.preventDefault();
50440 fixKeys : function(){ // load time branching for fastest keydown performance
50444 return function(e){
50445 var k = e.getKey(), r;
50448 r = this.doc.selection.createRange();
50451 r.pasteHTML('    ');
50456 /// this is handled by Roo.htmleditor.KeyEnter
50459 r = this.doc.selection.createRange();
50461 var target = r.parentElement();
50462 if(!target || target.tagName.toLowerCase() != 'li'){
50464 r.pasteHTML('<br/>');
50471 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50472 // this.cleanUpPaste.defer(100, this);
50478 }else if(Roo.isOpera){
50479 return function(e){
50480 var k = e.getKey();
50484 this.execCmd('InsertHTML','    ');
50488 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50489 // this.cleanUpPaste.defer(100, this);
50494 }else if(Roo.isSafari){
50495 return function(e){
50496 var k = e.getKey();
50500 this.execCmd('InsertText','\t');
50504 this.mozKeyPress(e);
50506 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50507 // this.cleanUpPaste.defer(100, this);
50515 getAllAncestors: function()
50517 var p = this.getSelectedNode();
50520 a.push(p); // push blank onto stack..
50521 p = this.getParentElement();
50525 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50529 a.push(this.doc.body);
50533 lastSelNode : false,
50536 getSelection : function()
50538 this.assignDocWin();
50539 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50542 * Select a dom node
50543 * @param {DomElement} node the node to select
50545 selectNode : function(node, collapse)
50547 var nodeRange = node.ownerDocument.createRange();
50549 nodeRange.selectNode(node);
50551 nodeRange.selectNodeContents(node);
50553 if (collapse === true) {
50554 nodeRange.collapse(true);
50557 var s = this.win.getSelection();
50558 s.removeAllRanges();
50559 s.addRange(nodeRange);
50562 getSelectedNode: function()
50564 // this may only work on Gecko!!!
50566 // should we cache this!!!!
50570 var range = this.createRange(this.getSelection()).cloneRange();
50573 var parent = range.parentElement();
50575 var testRange = range.duplicate();
50576 testRange.moveToElementText(parent);
50577 if (testRange.inRange(range)) {
50580 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
50583 parent = parent.parentElement;
50588 // is ancestor a text element.
50589 var ac = range.commonAncestorContainer;
50590 if (ac.nodeType == 3) {
50591 ac = ac.parentNode;
50594 var ar = ac.childNodes;
50597 var other_nodes = [];
50598 var has_other_nodes = false;
50599 for (var i=0;i<ar.length;i++) {
50600 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
50603 // fullly contained node.
50605 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
50610 // probably selected..
50611 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
50612 other_nodes.push(ar[i]);
50616 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
50621 has_other_nodes = true;
50623 if (!nodes.length && other_nodes.length) {
50624 nodes= other_nodes;
50626 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
50634 createRange: function(sel)
50636 // this has strange effects when using with
50637 // top toolbar - not sure if it's a great idea.
50638 //this.editor.contentWindow.focus();
50639 if (typeof sel != "undefined") {
50641 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
50643 return this.doc.createRange();
50646 return this.doc.createRange();
50649 getParentElement: function()
50652 this.assignDocWin();
50653 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
50655 var range = this.createRange(sel);
50658 var p = range.commonAncestorContainer;
50659 while (p.nodeType == 3) { // text node
50670 * Range intersection.. the hard stuff...
50674 * [ -- selected range --- ]
50678 * if end is before start or hits it. fail.
50679 * if start is after end or hits it fail.
50681 * if either hits (but other is outside. - then it's not
50687 // @see http://www.thismuchiknow.co.uk/?p=64.
50688 rangeIntersectsNode : function(range, node)
50690 var nodeRange = node.ownerDocument.createRange();
50692 nodeRange.selectNode(node);
50694 nodeRange.selectNodeContents(node);
50697 var rangeStartRange = range.cloneRange();
50698 rangeStartRange.collapse(true);
50700 var rangeEndRange = range.cloneRange();
50701 rangeEndRange.collapse(false);
50703 var nodeStartRange = nodeRange.cloneRange();
50704 nodeStartRange.collapse(true);
50706 var nodeEndRange = nodeRange.cloneRange();
50707 nodeEndRange.collapse(false);
50709 return rangeStartRange.compareBoundaryPoints(
50710 Range.START_TO_START, nodeEndRange) == -1 &&
50711 rangeEndRange.compareBoundaryPoints(
50712 Range.START_TO_START, nodeStartRange) == 1;
50716 rangeCompareNode : function(range, node)
50718 var nodeRange = node.ownerDocument.createRange();
50720 nodeRange.selectNode(node);
50722 nodeRange.selectNodeContents(node);
50726 range.collapse(true);
50728 nodeRange.collapse(true);
50730 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
50731 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
50733 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
50735 var nodeIsBefore = ss == 1;
50736 var nodeIsAfter = ee == -1;
50738 if (nodeIsBefore && nodeIsAfter) {
50741 if (!nodeIsBefore && nodeIsAfter) {
50742 return 1; //right trailed.
50745 if (nodeIsBefore && !nodeIsAfter) {
50746 return 2; // left trailed.
50752 cleanWordChars : function(input) {// change the chars to hex code
50755 [ 8211, "–" ],
50756 [ 8212, "—" ],
50764 var output = input;
50765 Roo.each(swapCodes, function(sw) {
50766 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
50768 output = output.replace(swapper, sw[1]);
50778 cleanUpChild : function (node)
50781 new Roo.htmleditor.FilterComment({node : node});
50782 new Roo.htmleditor.FilterAttributes({
50784 attrib_black : this.ablack,
50785 attrib_clean : this.aclean,
50786 style_white : this.cwhite,
50787 style_black : this.cblack
50789 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
50790 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
50796 * Clean up MS wordisms...
50797 * @deprecated - use filter directly
50799 cleanWord : function(node)
50801 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
50808 * @deprecated - use filters
50810 cleanTableWidths : function(node)
50812 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
50819 applyBlacklists : function()
50821 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
50822 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
50824 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
50825 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
50826 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
50830 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
50831 if (b.indexOf(tag) > -1) {
50834 this.white.push(tag);
50838 Roo.each(w, function(tag) {
50839 if (b.indexOf(tag) > -1) {
50842 if (this.white.indexOf(tag) > -1) {
50845 this.white.push(tag);
50850 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
50851 if (w.indexOf(tag) > -1) {
50854 this.black.push(tag);
50858 Roo.each(b, function(tag) {
50859 if (w.indexOf(tag) > -1) {
50862 if (this.black.indexOf(tag) > -1) {
50865 this.black.push(tag);
50870 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
50871 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
50875 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
50876 if (b.indexOf(tag) > -1) {
50879 this.cwhite.push(tag);
50883 Roo.each(w, function(tag) {
50884 if (b.indexOf(tag) > -1) {
50887 if (this.cwhite.indexOf(tag) > -1) {
50890 this.cwhite.push(tag);
50895 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
50896 if (w.indexOf(tag) > -1) {
50899 this.cblack.push(tag);
50903 Roo.each(b, function(tag) {
50904 if (w.indexOf(tag) > -1) {
50907 if (this.cblack.indexOf(tag) > -1) {
50910 this.cblack.push(tag);
50915 setStylesheets : function(stylesheets)
50917 if(typeof(stylesheets) == 'string'){
50918 Roo.get(this.iframe.contentDocument.head).createChild({
50920 rel : 'stylesheet',
50929 Roo.each(stylesheets, function(s) {
50934 Roo.get(_this.iframe.contentDocument.head).createChild({
50936 rel : 'stylesheet',
50946 updateLanguage : function()
50948 if (!this.iframe || !this.iframe.contentDocument) {
50951 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
50955 removeStylesheets : function()
50959 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
50964 setStyle : function(style)
50966 Roo.get(this.iframe.contentDocument.head).createChild({
50975 // hide stuff that is not compatible
50989 * @event specialkey
50993 * @cfg {String} fieldClass @hide
50996 * @cfg {String} focusClass @hide
50999 * @cfg {String} autoCreate @hide
51002 * @cfg {String} inputType @hide
51005 * @cfg {String} invalidClass @hide
51008 * @cfg {String} invalidText @hide
51011 * @cfg {String} msgFx @hide
51014 * @cfg {String} validateOnBlur @hide
51018 Roo.HtmlEditorCore.white = [
51019 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51021 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
51022 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
51023 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
51024 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
51025 'TABLE', 'UL', 'XMP',
51027 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
51030 'DIR', 'MENU', 'OL', 'UL', 'DL',
51036 Roo.HtmlEditorCore.black = [
51037 // 'embed', 'object', // enable - backend responsiblity to clean thiese
51039 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
51040 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
51041 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
51042 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
51043 //'FONT' // CLEAN LATER..
51044 'COLGROUP', 'COL' // messy tables.
51048 Roo.HtmlEditorCore.clean = [ // ?? needed???
51049 'SCRIPT', 'STYLE', 'TITLE', 'XML'
51051 Roo.HtmlEditorCore.tag_remove = [
51056 Roo.HtmlEditorCore.ablack = [
51060 Roo.HtmlEditorCore.aclean = [
51061 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
51065 Roo.HtmlEditorCore.pwhite= [
51066 'http', 'https', 'mailto'
51069 // white listed style attributes.
51070 Roo.HtmlEditorCore.cwhite= [
51071 // 'text-align', /// default is to allow most things..
51077 // black listed style attributes.
51078 Roo.HtmlEditorCore.cblack= [
51079 // 'font-size' -- this can be set by the project
51085 //<script type="text/javascript">
51088 * Ext JS Library 1.1.1
51089 * Copyright(c) 2006-2007, Ext JS, LLC.
51095 Roo.form.HtmlEditor = function(config){
51099 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51101 if (!this.toolbars) {
51102 this.toolbars = [];
51104 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51110 * @class Roo.form.HtmlEditor
51111 * @extends Roo.form.Field
51112 * Provides a lightweight HTML Editor component.
51114 * This has been tested on Fireforx / Chrome.. IE may not be so great..
51116 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51117 * supported by this editor.</b><br/><br/>
51118 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51119 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51121 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51123 * @cfg {Boolean} clearUp
51127 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51132 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
51137 * @cfg {Number} height (in pixels)
51141 * @cfg {Number} width (in pixels)
51146 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
51149 stylesheets: false,
51153 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51158 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51164 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51169 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51174 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51176 allowComments: false,
51178 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51180 enableBlocks : true,
51183 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51184 * if you are doing an email editor, this probably needs disabling, it's designed
51188 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51192 * @cfg {String} language default en - language of text (usefull for rtl languages)
51201 // private properties
51202 validationEvent : false,
51204 initialized : false,
51207 onFocus : Roo.emptyFn,
51209 hideMode:'offsets',
51211 actionMode : 'container', // defaults to hiding it...
51213 defaultAutoCreate : { // modified by initCompnoent..
51215 style:"width:500px;height:300px;",
51216 autocomplete: "new-password"
51220 initComponent : function(){
51223 * @event initialize
51224 * Fires when the editor is fully initialized (including the iframe)
51225 * @param {HtmlEditor} this
51230 * Fires when the editor is first receives the focus. Any insertion must wait
51231 * until after this event.
51232 * @param {HtmlEditor} this
51236 * @event beforesync
51237 * Fires before the textarea is updated with content from the editor iframe. Return false
51238 * to cancel the sync.
51239 * @param {HtmlEditor} this
51240 * @param {String} html
51244 * @event beforepush
51245 * Fires before the iframe editor is updated with content from the textarea. Return false
51246 * to cancel the push.
51247 * @param {HtmlEditor} this
51248 * @param {String} html
51253 * Fires when the textarea is updated with content from the editor iframe.
51254 * @param {HtmlEditor} this
51255 * @param {String} html
51260 * Fires when the iframe editor is updated with content from the textarea.
51261 * @param {HtmlEditor} this
51262 * @param {String} html
51266 * @event editmodechange
51267 * Fires when the editor switches edit modes
51268 * @param {HtmlEditor} this
51269 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51271 editmodechange: true,
51273 * @event editorevent
51274 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51275 * @param {HtmlEditor} this
51279 * @event firstfocus
51280 * Fires when on first focus - needed by toolbars..
51281 * @param {HtmlEditor} this
51286 * Auto save the htmlEditor value as a file into Events
51287 * @param {HtmlEditor} this
51291 * @event savedpreview
51292 * preview the saved version of htmlEditor
51293 * @param {HtmlEditor} this
51295 savedpreview: true,
51298 * @event stylesheetsclick
51299 * Fires when press the Sytlesheets button
51300 * @param {Roo.HtmlEditorCore} this
51302 stylesheetsclick: true,
51305 * Fires when press user pastes into the editor
51306 * @param {Roo.HtmlEditorCore} this
51310 this.defaultAutoCreate = {
51312 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51313 autocomplete: "new-password"
51318 * Protected method that will not generally be called directly. It
51319 * is called when the editor creates its toolbar. Override this method if you need to
51320 * add custom toolbar buttons.
51321 * @param {HtmlEditor} editor
51323 createToolbar : function(editor){
51324 Roo.log("create toolbars");
51325 if (!editor.toolbars || !editor.toolbars.length) {
51326 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51329 for (var i =0 ; i < editor.toolbars.length;i++) {
51330 editor.toolbars[i] = Roo.factory(
51331 typeof(editor.toolbars[i]) == 'string' ?
51332 { xtype: editor.toolbars[i]} : editor.toolbars[i],
51333 Roo.form.HtmlEditor);
51334 editor.toolbars[i].init(editor);
51340 * get the Context selected node
51341 * @returns {DomElement|boolean} selected node if active or false if none
51344 getSelectedNode : function()
51346 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51349 return this.toolbars[1].tb.selectedNode;
51353 onRender : function(ct, position)
51356 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51358 this.wrap = this.el.wrap({
51359 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51362 this.editorcore.onRender(ct, position);
51364 if (this.resizable) {
51365 this.resizeEl = new Roo.Resizable(this.wrap, {
51369 minHeight : this.height,
51370 height: this.height,
51371 handles : this.resizable,
51374 resize : function(r, w, h) {
51375 _t.onResize(w,h); // -something
51381 this.createToolbar(this);
51385 this.setSize(this.wrap.getSize());
51387 if (this.resizeEl) {
51388 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51389 // should trigger onReize..
51392 this.keyNav = new Roo.KeyNav(this.el, {
51394 "tab" : function(e){
51395 e.preventDefault();
51397 var value = this.getValue();
51399 var start = this.el.dom.selectionStart;
51400 var end = this.el.dom.selectionEnd;
51404 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51405 this.el.dom.setSelectionRange(end + 1, end + 1);
51409 var f = value.substring(0, start).split("\t");
51411 if(f.pop().length != 0){
51415 this.setValue(f.join("\t") + value.substring(end));
51416 this.el.dom.setSelectionRange(start - 1, start - 1);
51420 "home" : function(e){
51421 e.preventDefault();
51423 var curr = this.el.dom.selectionStart;
51424 var lines = this.getValue().split("\n");
51431 this.el.dom.setSelectionRange(0, 0);
51437 for (var i = 0; i < lines.length;i++) {
51438 pos += lines[i].length;
51448 pos -= lines[i].length;
51454 this.el.dom.setSelectionRange(pos, pos);
51458 this.el.dom.selectionStart = pos;
51459 this.el.dom.selectionEnd = curr;
51462 "end" : function(e){
51463 e.preventDefault();
51465 var curr = this.el.dom.selectionStart;
51466 var lines = this.getValue().split("\n");
51473 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51479 for (var i = 0; i < lines.length;i++) {
51481 pos += lines[i].length;
51495 this.el.dom.setSelectionRange(pos, pos);
51499 this.el.dom.selectionStart = curr;
51500 this.el.dom.selectionEnd = pos;
51505 doRelay : function(foo, bar, hname){
51506 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51512 // if(this.autosave && this.w){
51513 // this.autoSaveFn = setInterval(this.autosave, 1000);
51518 onResize : function(w, h)
51520 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51525 if(typeof w == 'number'){
51526 var aw = w - this.wrap.getFrameWidth('lr');
51527 this.el.setWidth(this.adjustWidth('textarea', aw));
51530 if(typeof h == 'number'){
51532 for (var i =0; i < this.toolbars.length;i++) {
51533 // fixme - ask toolbars for heights?
51534 tbh += this.toolbars[i].tb.el.getHeight();
51535 if (this.toolbars[i].footer) {
51536 tbh += this.toolbars[i].footer.el.getHeight();
51543 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51544 ah -= 5; // knock a few pixes off for look..
51546 this.el.setHeight(this.adjustWidth('textarea', ah));
51550 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51551 this.editorcore.onResize(ew,eh);
51556 * Toggles the editor between standard and source edit mode.
51557 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51559 toggleSourceEdit : function(sourceEditMode)
51561 this.editorcore.toggleSourceEdit(sourceEditMode);
51563 if(this.editorcore.sourceEditMode){
51564 Roo.log('editor - showing textarea');
51567 // Roo.log(this.syncValue());
51568 this.editorcore.syncValue();
51569 this.el.removeClass('x-hidden');
51570 this.el.dom.removeAttribute('tabIndex');
51572 this.el.dom.scrollTop = 0;
51575 for (var i = 0; i < this.toolbars.length; i++) {
51576 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51577 this.toolbars[i].tb.hide();
51578 this.toolbars[i].footer.hide();
51583 Roo.log('editor - hiding textarea');
51585 // Roo.log(this.pushValue());
51586 this.editorcore.pushValue();
51588 this.el.addClass('x-hidden');
51589 this.el.dom.setAttribute('tabIndex', -1);
51591 for (var i = 0; i < this.toolbars.length; i++) {
51592 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51593 this.toolbars[i].tb.show();
51594 this.toolbars[i].footer.show();
51598 //this.deferFocus();
51601 this.setSize(this.wrap.getSize());
51602 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
51604 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
51607 // private (for BoxComponent)
51608 adjustSize : Roo.BoxComponent.prototype.adjustSize,
51610 // private (for BoxComponent)
51611 getResizeEl : function(){
51615 // private (for BoxComponent)
51616 getPositionEl : function(){
51621 initEvents : function(){
51622 this.originalValue = this.getValue();
51626 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51629 markInvalid : Roo.emptyFn,
51631 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51634 clearInvalid : Roo.emptyFn,
51636 setValue : function(v){
51637 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
51638 this.editorcore.pushValue();
51642 * update the language in the body - really done by core
51643 * @param {String} language - eg. en / ar / zh-CN etc..
51645 updateLanguage : function(lang)
51647 this.language = lang;
51648 this.editorcore.language = lang;
51649 this.editorcore.updateLanguage();
51653 deferFocus : function(){
51654 this.focus.defer(10, this);
51658 focus : function(){
51659 this.editorcore.focus();
51665 onDestroy : function(){
51671 for (var i =0; i < this.toolbars.length;i++) {
51672 // fixme - ask toolbars for heights?
51673 this.toolbars[i].onDestroy();
51676 this.wrap.dom.innerHTML = '';
51677 this.wrap.remove();
51682 onFirstFocus : function(){
51683 //Roo.log("onFirstFocus");
51684 this.editorcore.onFirstFocus();
51685 for (var i =0; i < this.toolbars.length;i++) {
51686 this.toolbars[i].onFirstFocus();
51692 syncValue : function()
51694 this.editorcore.syncValue();
51697 pushValue : function()
51699 this.editorcore.pushValue();
51702 setStylesheets : function(stylesheets)
51704 this.editorcore.setStylesheets(stylesheets);
51707 removeStylesheets : function()
51709 this.editorcore.removeStylesheets();
51713 // hide stuff that is not compatible
51727 * @event specialkey
51731 * @cfg {String} fieldClass @hide
51734 * @cfg {String} focusClass @hide
51737 * @cfg {String} autoCreate @hide
51740 * @cfg {String} inputType @hide
51743 * @cfg {String} invalidClass @hide
51746 * @cfg {String} invalidText @hide
51749 * @cfg {String} msgFx @hide
51752 * @cfg {String} validateOnBlur @hide
51758 * Ext JS Library 1.1.1
51759 * Copyright(c) 2006-2007, Ext JS, LLC.
51765 * @class Roo.form.HtmlEditor.ToolbarStandard
51770 new Roo.form.HtmlEditor({
51773 new Roo.form.HtmlEditorToolbar1({
51774 disable : { fonts: 1 , format: 1, ..., ... , ...],
51780 * @cfg {Object} disable List of elements to disable..
51781 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
51785 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
51788 Roo.form.HtmlEditor.ToolbarStandard = function(config)
51791 Roo.apply(this, config);
51793 // default disabled, based on 'good practice'..
51794 this.disable = this.disable || {};
51795 Roo.applyIf(this.disable, {
51798 specialElements : true
51802 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
51803 // dont call parent... till later.
51806 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
51813 editorcore : false,
51815 * @cfg {Object} disable List of toolbar elements to disable
51822 * @cfg {String} createLinkText The default text for the create link prompt
51824 createLinkText : 'Please enter the URL for the link:',
51826 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
51828 defaultLinkValue : 'http:/'+'/',
51832 * @cfg {Array} fontFamilies An array of available font families
51850 // "á" , ?? a acute?
51855 "°" // , // degrees
51857 // "é" , // e ecute
51858 // "ú" , // u ecute?
51861 specialElements : [
51863 text: "Insert Table",
51866 ihtml : '<table><tr><td>Cell</td></tr></table>'
51870 text: "Insert Image",
51873 ihtml : '<img src="about:blank"/>'
51882 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
51883 "input:submit", "input:button", "select", "textarea", "label" ],
51886 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
51888 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
51897 * @cfg {String} defaultFont default font to use.
51899 defaultFont: 'tahoma',
51901 fontSelect : false,
51904 formatCombo : false,
51906 init : function(editor)
51908 this.editor = editor;
51909 this.editorcore = editor.editorcore ? editor.editorcore : editor;
51910 var editorcore = this.editorcore;
51914 var fid = editorcore.frameId;
51916 function btn(id, toggle, handler){
51917 var xid = fid + '-'+ id ;
51921 cls : 'x-btn-icon x-edit-'+id,
51922 enableToggle:toggle !== false,
51923 scope: _t, // was editor...
51924 handler:handler||_t.relayBtnCmd,
51925 clickEvent:'mousedown',
51926 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51933 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51935 // stop form submits
51936 tb.el.on('click', function(e){
51937 e.preventDefault(); // what does this do?
51940 if(!this.disable.font) { // && !Roo.isSafari){
51941 /* why no safari for fonts
51942 editor.fontSelect = tb.el.createChild({
51945 cls:'x-font-select',
51946 html: this.createFontOptions()
51949 editor.fontSelect.on('change', function(){
51950 var font = editor.fontSelect.dom.value;
51951 editor.relayCmd('fontname', font);
51952 editor.deferFocus();
51956 editor.fontSelect.dom,
51962 if(!this.disable.formats){
51963 this.formatCombo = new Roo.form.ComboBox({
51964 store: new Roo.data.SimpleStore({
51967 data : this.formats // from states.js
51971 //autoCreate : {tag: "div", size: "20"},
51972 displayField:'tag',
51976 triggerAction: 'all',
51977 emptyText:'Add tag',
51978 selectOnFocus:true,
51981 'select': function(c, r, i) {
51982 editorcore.insertTag(r.get('tag'));
51988 tb.addField(this.formatCombo);
51992 if(!this.disable.format){
51997 btn('strikethrough')
52000 if(!this.disable.fontSize){
52005 btn('increasefontsize', false, editorcore.adjustFont),
52006 btn('decreasefontsize', false, editorcore.adjustFont)
52011 if(!this.disable.colors){
52014 id:editorcore.frameId +'-forecolor',
52015 cls:'x-btn-icon x-edit-forecolor',
52016 clickEvent:'mousedown',
52017 tooltip: this.buttonTips['forecolor'] || undefined,
52019 menu : new Roo.menu.ColorMenu({
52020 allowReselect: true,
52021 focus: Roo.emptyFn,
52024 selectHandler: function(cp, color){
52025 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52026 editor.deferFocus();
52029 clickEvent:'mousedown'
52032 id:editorcore.frameId +'backcolor',
52033 cls:'x-btn-icon x-edit-backcolor',
52034 clickEvent:'mousedown',
52035 tooltip: this.buttonTips['backcolor'] || undefined,
52037 menu : new Roo.menu.ColorMenu({
52038 focus: Roo.emptyFn,
52041 allowReselect: true,
52042 selectHandler: function(cp, color){
52044 editorcore.execCmd('useCSS', false);
52045 editorcore.execCmd('hilitecolor', color);
52046 editorcore.execCmd('useCSS', true);
52047 editor.deferFocus();
52049 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
52050 Roo.isSafari || Roo.isIE ? '#'+color : color);
52051 editor.deferFocus();
52055 clickEvent:'mousedown'
52060 // now add all the items...
52063 if(!this.disable.alignments){
52066 btn('justifyleft'),
52067 btn('justifycenter'),
52068 btn('justifyright')
52072 //if(!Roo.isSafari){
52073 if(!this.disable.links){
52076 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
52080 if(!this.disable.lists){
52083 btn('insertorderedlist'),
52084 btn('insertunorderedlist')
52087 if(!this.disable.sourceEdit){
52090 btn('sourceedit', true, function(btn){
52091 this.toggleSourceEdit(btn.pressed);
52098 // special menu.. - needs to be tidied up..
52099 if (!this.disable.special) {
52102 cls: 'x-edit-none',
52108 for (var i =0; i < this.specialChars.length; i++) {
52109 smenu.menu.items.push({
52111 html: this.specialChars[i],
52112 handler: function(a,b) {
52113 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52114 //editor.insertAtCursor(a.html);
52128 if (!this.disable.cleanStyles) {
52130 cls: 'x-btn-icon x-btn-clear',
52136 for (var i =0; i < this.cleanStyles.length; i++) {
52137 cmenu.menu.items.push({
52138 actiontype : this.cleanStyles[i],
52139 html: 'Remove ' + this.cleanStyles[i],
52140 handler: function(a,b) {
52143 var c = Roo.get(editorcore.doc.body);
52144 c.select('[style]').each(function(s) {
52145 s.dom.style.removeProperty(a.actiontype);
52147 editorcore.syncValue();
52152 cmenu.menu.items.push({
52153 actiontype : 'tablewidths',
52154 html: 'Remove Table Widths',
52155 handler: function(a,b) {
52156 editorcore.cleanTableWidths();
52157 editorcore.syncValue();
52161 cmenu.menu.items.push({
52162 actiontype : 'word',
52163 html: 'Remove MS Word Formating',
52164 handler: function(a,b) {
52165 editorcore.cleanWord();
52166 editorcore.syncValue();
52171 cmenu.menu.items.push({
52172 actiontype : 'all',
52173 html: 'Remove All Styles',
52174 handler: function(a,b) {
52176 var c = Roo.get(editorcore.doc.body);
52177 c.select('[style]').each(function(s) {
52178 s.dom.removeAttribute('style');
52180 editorcore.syncValue();
52185 cmenu.menu.items.push({
52186 actiontype : 'all',
52187 html: 'Remove All CSS Classes',
52188 handler: function(a,b) {
52190 var c = Roo.get(editorcore.doc.body);
52191 c.select('[class]').each(function(s) {
52192 s.dom.removeAttribute('class');
52194 editorcore.cleanWord();
52195 editorcore.syncValue();
52200 cmenu.menu.items.push({
52201 actiontype : 'tidy',
52202 html: 'Tidy HTML Source',
52203 handler: function(a,b) {
52204 new Roo.htmleditor.Tidy(editorcore.doc.body);
52205 editorcore.syncValue();
52214 if (!this.disable.specialElements) {
52217 cls: 'x-edit-none',
52222 for (var i =0; i < this.specialElements.length; i++) {
52223 semenu.menu.items.push(
52225 handler: function(a,b) {
52226 editor.insertAtCursor(this.ihtml);
52228 }, this.specialElements[i])
52240 for(var i =0; i< this.btns.length;i++) {
52241 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52242 b.cls = 'x-edit-none';
52244 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52245 b.cls += ' x-init-enable';
52248 b.scope = editorcore;
52256 // disable everything...
52258 this.tb.items.each(function(item){
52261 item.id != editorcore.frameId+ '-sourceedit' &&
52262 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52268 this.rendered = true;
52270 // the all the btns;
52271 editor.on('editorevent', this.updateToolbar, this);
52272 // other toolbars need to implement this..
52273 //editor.on('editmodechange', this.updateToolbar, this);
52277 relayBtnCmd : function(btn) {
52278 this.editorcore.relayCmd(btn.cmd);
52280 // private used internally
52281 createLink : function(){
52282 //Roo.log("create link?");
52283 var ec = this.editorcore;
52284 var ar = ec.getAllAncestors();
52286 for(var i = 0;i< ar.length;i++) {
52287 if (ar[i] && ar[i].nodeName == 'A') {
52295 Roo.MessageBox.show({
52296 title : "Add / Edit Link URL",
52297 msg : "Enter the url for the link",
52298 buttons: Roo.MessageBox.OKCANCEL,
52299 fn: function(btn, url){
52303 if(url && url != 'http:/'+'/'){
52305 n.setAttribute('href', url);
52307 ec.relayCmd('createlink', url);
52313 //multiline: multiline,
52315 value : n ? n.getAttribute('href') : ''
52319 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52325 * Protected method that will not generally be called directly. It triggers
52326 * a toolbar update by reading the markup state of the current selection in the editor.
52328 updateToolbar: function(){
52330 if(!this.editorcore.activated){
52331 this.editor.onFirstFocus();
52335 var btns = this.tb.items.map,
52336 doc = this.editorcore.doc,
52337 frameId = this.editorcore.frameId;
52339 if(!this.disable.font && !Roo.isSafari){
52341 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52342 if(name != this.fontSelect.dom.value){
52343 this.fontSelect.dom.value = name;
52347 if(!this.disable.format){
52348 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52349 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52350 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52351 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52353 if(!this.disable.alignments){
52354 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52355 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52356 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52358 if(!Roo.isSafari && !this.disable.lists){
52359 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52360 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52363 var ans = this.editorcore.getAllAncestors();
52364 if (this.formatCombo) {
52367 var store = this.formatCombo.store;
52368 this.formatCombo.setValue("");
52369 for (var i =0; i < ans.length;i++) {
52370 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52372 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52380 // hides menus... - so this cant be on a menu...
52381 Roo.menu.MenuMgr.hideAll();
52383 //this.editorsyncValue();
52387 createFontOptions : function(){
52388 var buf = [], fs = this.fontFamilies, ff, lc;
52392 for(var i = 0, len = fs.length; i< len; i++){
52394 lc = ff.toLowerCase();
52396 '<option value="',lc,'" style="font-family:',ff,';"',
52397 (this.defaultFont == lc ? ' selected="true">' : '>'),
52402 return buf.join('');
52405 toggleSourceEdit : function(sourceEditMode){
52407 Roo.log("toolbar toogle");
52408 if(sourceEditMode === undefined){
52409 sourceEditMode = !this.sourceEditMode;
52411 this.sourceEditMode = sourceEditMode === true;
52412 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52413 // just toggle the button?
52414 if(btn.pressed !== this.sourceEditMode){
52415 btn.toggle(this.sourceEditMode);
52419 if(sourceEditMode){
52420 Roo.log("disabling buttons");
52421 this.tb.items.each(function(item){
52422 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52428 Roo.log("enabling buttons");
52429 if(this.editorcore.initialized){
52430 this.tb.items.each(function(item){
52433 // initialize 'blocks'
52434 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52435 Roo.htmleditor.Block.factory(e).updateElement(e);
52441 Roo.log("calling toggole on editor");
52442 // tell the editor that it's been pressed..
52443 this.editor.toggleSourceEdit(sourceEditMode);
52447 * Object collection of toolbar tooltips for the buttons in the editor. The key
52448 * is the command id associated with that button and the value is a valid QuickTips object.
52453 title: 'Bold (Ctrl+B)',
52454 text: 'Make the selected text bold.',
52455 cls: 'x-html-editor-tip'
52458 title: 'Italic (Ctrl+I)',
52459 text: 'Make the selected text italic.',
52460 cls: 'x-html-editor-tip'
52468 title: 'Bold (Ctrl+B)',
52469 text: 'Make the selected text bold.',
52470 cls: 'x-html-editor-tip'
52473 title: 'Italic (Ctrl+I)',
52474 text: 'Make the selected text italic.',
52475 cls: 'x-html-editor-tip'
52478 title: 'Underline (Ctrl+U)',
52479 text: 'Underline the selected text.',
52480 cls: 'x-html-editor-tip'
52483 title: 'Strikethrough',
52484 text: 'Strikethrough the selected text.',
52485 cls: 'x-html-editor-tip'
52487 increasefontsize : {
52488 title: 'Grow Text',
52489 text: 'Increase the font size.',
52490 cls: 'x-html-editor-tip'
52492 decreasefontsize : {
52493 title: 'Shrink Text',
52494 text: 'Decrease the font size.',
52495 cls: 'x-html-editor-tip'
52498 title: 'Text Highlight Color',
52499 text: 'Change the background color of the selected text.',
52500 cls: 'x-html-editor-tip'
52503 title: 'Font Color',
52504 text: 'Change the color of the selected text.',
52505 cls: 'x-html-editor-tip'
52508 title: 'Align Text Left',
52509 text: 'Align text to the left.',
52510 cls: 'x-html-editor-tip'
52513 title: 'Center Text',
52514 text: 'Center text in the editor.',
52515 cls: 'x-html-editor-tip'
52518 title: 'Align Text Right',
52519 text: 'Align text to the right.',
52520 cls: 'x-html-editor-tip'
52522 insertunorderedlist : {
52523 title: 'Bullet List',
52524 text: 'Start a bulleted list.',
52525 cls: 'x-html-editor-tip'
52527 insertorderedlist : {
52528 title: 'Numbered List',
52529 text: 'Start a numbered list.',
52530 cls: 'x-html-editor-tip'
52533 title: 'Hyperlink',
52534 text: 'Make the selected text a hyperlink.',
52535 cls: 'x-html-editor-tip'
52538 title: 'Source Edit',
52539 text: 'Switch to source editing mode.',
52540 cls: 'x-html-editor-tip'
52544 onDestroy : function(){
52547 this.tb.items.each(function(item){
52549 item.menu.removeAll();
52551 item.menu.el.destroy();
52559 onFirstFocus: function() {
52560 this.tb.items.each(function(item){
52569 // <script type="text/javascript">
52572 * Ext JS Library 1.1.1
52573 * Copyright(c) 2006-2007, Ext JS, LLC.
52580 * @class Roo.form.HtmlEditor.ToolbarContext
52585 new Roo.form.HtmlEditor({
52588 { xtype: 'ToolbarStandard', styles : {} }
52589 { xtype: 'ToolbarContext', disable : {} }
52595 * @config : {Object} disable List of elements to disable.. (not done yet.)
52596 * @config : {Object} styles Map of styles available.
52600 Roo.form.HtmlEditor.ToolbarContext = function(config)
52603 Roo.apply(this, config);
52604 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52605 // dont call parent... till later.
52606 this.styles = this.styles || {};
52611 Roo.form.HtmlEditor.ToolbarContext.types = {
52626 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52652 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52723 name : 'selectoptions',
52729 // should we really allow this??
52730 // should this just be
52747 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
52748 Roo.form.HtmlEditor.ToolbarContext.stores = false;
52750 Roo.form.HtmlEditor.ToolbarContext.options = {
52752 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
52753 [ 'Courier New', 'Courier New'],
52754 [ 'Tahoma', 'Tahoma'],
52755 [ 'Times New Roman,serif', 'Times'],
52756 [ 'Verdana','Verdana' ]
52760 // fixme - these need to be configurable..
52763 //Roo.form.HtmlEditor.ToolbarContext.types
52766 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
52773 editorcore : false,
52775 * @cfg {Object} disable List of toolbar elements to disable
52780 * @cfg {Object} styles List of styles
52781 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
52783 * These must be defined in the page, so they get rendered correctly..
52794 init : function(editor)
52796 this.editor = editor;
52797 this.editorcore = editor.editorcore ? editor.editorcore : editor;
52798 var editorcore = this.editorcore;
52800 var fid = editorcore.frameId;
52802 function btn(id, toggle, handler){
52803 var xid = fid + '-'+ id ;
52807 cls : 'x-btn-icon x-edit-'+id,
52808 enableToggle:toggle !== false,
52809 scope: editorcore, // was editor...
52810 handler:handler||editorcore.relayBtnCmd,
52811 clickEvent:'mousedown',
52812 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52816 // create a new element.
52817 var wdiv = editor.wrap.createChild({
52819 }, editor.wrap.dom.firstChild.nextSibling, true);
52821 // can we do this more than once??
52823 // stop form submits
52826 // disable everything...
52827 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
52828 this.toolbars = {};
52829 // block toolbars are built in updateToolbar when needed.
52830 for (var i in ty) {
52832 this.toolbars[i] = this.buildToolbar(ty[i],i);
52834 this.tb = this.toolbars.BODY;
52836 this.buildFooter();
52837 this.footer.show();
52838 editor.on('hide', function( ) { this.footer.hide() }, this);
52839 editor.on('show', function( ) { this.footer.show() }, this);
52842 this.rendered = true;
52844 // the all the btns;
52845 editor.on('editorevent', this.updateToolbar, this);
52846 // other toolbars need to implement this..
52847 //editor.on('editmodechange', this.updateToolbar, this);
52853 * Protected method that will not generally be called directly. It triggers
52854 * a toolbar update by reading the markup state of the current selection in the editor.
52856 * Note you can force an update by calling on('editorevent', scope, false)
52858 updateToolbar: function(editor ,ev, sel)
52862 ev.stopEvent(); // se if we can stop this looping with mutiple events.
52866 // capture mouse up - this is handy for selecting images..
52867 // perhaps should go somewhere else...
52868 if(!this.editorcore.activated){
52869 this.editor.onFirstFocus();
52872 //Roo.log(ev ? ev.target : 'NOTARGET');
52875 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
52876 // selectNode - might want to handle IE?
52881 (ev.type == 'mouseup' || ev.type == 'click' ) &&
52882 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
52883 // they have click on an image...
52884 // let's see if we can change the selection...
52887 // this triggers looping?
52888 //this.editorcore.selectNode(sel);
52892 // this forces an id..
52893 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
52894 e.classList.remove('roo-ed-selection');
52896 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
52897 //Roo.get(node).addClass('roo-ed-selection');
52899 //var updateFooter = sel ? false : true;
52902 var ans = this.editorcore.getAllAncestors();
52905 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
52908 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
52909 sel = sel ? sel : this.editorcore.doc.body;
52910 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
52914 var tn = sel.tagName.toUpperCase();
52915 var lastSel = this.tb.selectedNode;
52916 this.tb.selectedNode = sel;
52917 var left_label = tn;
52919 // ok see if we are editing a block?
52922 // you are not actually selecting the block.
52923 if (sel && sel.hasAttribute('data-block')) {
52925 } else if (sel && sel.closest('[data-block]')) {
52927 db = sel.closest('[data-block]');
52928 //var cepar = sel.closest('[contenteditable=true]');
52929 //if (db && cepar && cepar.tagName != 'BODY') {
52930 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
52936 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
52937 if (db && this.editorcore.enableBlocks) {
52938 block = Roo.htmleditor.Block.factory(db);
52943 db.classList.length > 0 ? db.className + ' ' : ''
52944 ) + 'roo-ed-selection';
52946 // since we removed it earlier... its not there..
52947 tn = 'BLOCK.' + db.getAttribute('data-block');
52949 //this.editorcore.selectNode(db);
52950 if (typeof(this.toolbars[tn]) == 'undefined') {
52951 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
52953 this.toolbars[tn].selectedNode = db;
52954 left_label = block.friendly_name;
52955 ans = this.editorcore.getAllAncestors();
52963 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
52964 return; // no change?
52970 ///console.log("show: " + tn);
52971 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
52975 this.tb.items.first().el.innerHTML = left_label + ': ';
52978 // update attributes
52979 if (block && this.tb.fields) {
52981 this.tb.fields.each(function(e) {
52982 e.setValue(block[e.name]);
52986 } else if (this.tb.fields && this.tb.selectedNode) {
52987 this.tb.fields.each( function(e) {
52989 e.setValue(this.tb.selectedNode.style[e.stylename]);
52992 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
52994 this.updateToolbarStyles(this.tb.selectedNode);
52999 Roo.menu.MenuMgr.hideAll();
53004 // update the footer
53006 this.updateFooter(ans);
53010 updateToolbarStyles : function(sel)
53012 var hasStyles = false;
53013 for(var i in this.styles) {
53019 if (hasStyles && this.tb.hasStyles) {
53020 var st = this.tb.fields.item(0);
53022 st.store.removeAll();
53023 var cn = sel.className.split(/\s+/);
53026 if (this.styles['*']) {
53028 Roo.each(this.styles['*'], function(v) {
53029 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53032 if (this.styles[tn]) {
53033 Roo.each(this.styles[tn], function(v) {
53034 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53038 st.store.loadData(avs);
53045 updateFooter : function(ans)
53048 if (ans === false) {
53049 this.footDisp.dom.innerHTML = '';
53053 this.footerEls = ans.reverse();
53054 Roo.each(this.footerEls, function(a,i) {
53055 if (!a) { return; }
53056 html += html.length ? ' > ' : '';
53058 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53063 var sz = this.footDisp.up('td').getSize();
53064 this.footDisp.dom.style.width = (sz.width -10) + 'px';
53065 this.footDisp.dom.style.marginLeft = '5px';
53067 this.footDisp.dom.style.overflow = 'hidden';
53069 this.footDisp.dom.innerHTML = html;
53076 onDestroy : function(){
53079 this.tb.items.each(function(item){
53081 item.menu.removeAll();
53083 item.menu.el.destroy();
53091 onFirstFocus: function() {
53092 // need to do this for all the toolbars..
53093 this.tb.items.each(function(item){
53097 buildToolbar: function(tlist, nm, friendly_name, block)
53099 var editor = this.editor;
53100 var editorcore = this.editorcore;
53101 // create a new element.
53102 var wdiv = editor.wrap.createChild({
53104 }, editor.wrap.dom.firstChild.nextSibling, true);
53107 var tb = new Roo.Toolbar(wdiv);
53108 ///this.tb = tb; // << this sets the active toolbar..
53109 if (tlist === false && block) {
53110 tlist = block.contextMenu(this);
53113 tb.hasStyles = false;
53116 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
53118 var styles = Array.from(this.styles);
53122 if (styles && styles.length) {
53123 tb.hasStyles = true;
53124 // this needs a multi-select checkbox...
53125 tb.addField( new Roo.form.ComboBox({
53126 store: new Roo.data.SimpleStore({
53128 fields: ['val', 'selected'],
53131 name : '-roo-edit-className',
53132 attrname : 'className',
53133 displayField: 'val',
53137 triggerAction: 'all',
53138 emptyText:'Select Style',
53139 selectOnFocus:true,
53142 'select': function(c, r, i) {
53143 // initial support only for on class per el..
53144 tb.selectedNode.className = r ? r.get('val') : '';
53145 editorcore.syncValue();
53152 var tbc = Roo.form.HtmlEditor.ToolbarContext;
53155 for (var i = 0; i < tlist.length; i++) {
53157 // newer versions will use xtype cfg to create menus.
53158 if (typeof(tlist[i].xtype) != 'undefined') {
53160 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53166 var item = tlist[i];
53167 tb.add(item.title + ": ");
53170 //optname == used so you can configure the options available..
53171 var opts = item.opts ? item.opts : false;
53172 if (item.optname) { // use the b
53173 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53178 // opts == pulldown..
53179 tb.addField( new Roo.form.ComboBox({
53180 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53182 fields: ['val', 'display'],
53185 name : '-roo-edit-' + tlist[i].name,
53187 attrname : tlist[i].name,
53188 stylename : item.style ? item.style : false,
53190 displayField: item.displayField ? item.displayField : 'val',
53191 valueField : 'val',
53193 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
53195 triggerAction: 'all',
53196 emptyText:'Select',
53197 selectOnFocus:true,
53198 width: item.width ? item.width : 130,
53200 'select': function(c, r, i) {
53204 tb.selectedNode.style[c.stylename] = r.get('val');
53205 editorcore.syncValue();
53209 tb.selectedNode.removeAttribute(c.attrname);
53210 editorcore.syncValue();
53213 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53214 editorcore.syncValue();
53223 tb.addField( new Roo.form.TextField({
53226 //allowBlank:false,
53232 tb.addField( new Roo.form.TextField({
53233 name: '-roo-edit-' + tlist[i].name,
53234 attrname : tlist[i].name,
53240 'change' : function(f, nv, ov) {
53243 tb.selectedNode.setAttribute(f.attrname, nv);
53244 editorcore.syncValue();
53252 var show_delete = !block || block.deleteTitle !== false;
53254 show_delete = false;
53258 text: 'Stylesheets',
53261 click : function ()
53263 _this.editor.fireEvent('stylesheetsclick', _this.editor);
53272 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53275 click : function ()
53277 var sn = tb.selectedNode;
53279 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53285 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53286 if (sn.hasAttribute('data-block')) {
53287 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
53288 sn.parentNode.removeChild(sn);
53290 } else if (sn && sn.tagName != 'BODY') {
53291 // remove and keep parents.
53292 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53297 var range = editorcore.createRange();
53299 range.setStart(stn,0);
53300 range.setEnd(stn,0);
53301 var selection = editorcore.getSelection();
53302 selection.removeAllRanges();
53303 selection.addRange(range);
53306 //_this.updateToolbar(null, null, pn);
53307 _this.updateToolbar(null, null, null);
53308 _this.updateFooter(false);
53319 tb.el.on('click', function(e){
53320 e.preventDefault(); // what does this do?
53322 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53325 // dont need to disable them... as they will get hidden
53330 buildFooter : function()
53333 var fel = this.editor.wrap.createChild();
53334 this.footer = new Roo.Toolbar(fel);
53335 // toolbar has scrolly on left / right?
53336 var footDisp= new Roo.Toolbar.Fill();
53342 handler : function() {
53343 _t.footDisp.scrollTo('left',0,true)
53347 this.footer.add( footDisp );
53352 handler : function() {
53354 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53358 var fel = Roo.get(footDisp.el);
53359 fel.addClass('x-editor-context');
53360 this.footDispWrap = fel;
53361 this.footDispWrap.overflow = 'hidden';
53363 this.footDisp = fel.createChild();
53364 this.footDispWrap.on('click', this.onContextClick, this)
53368 // when the footer contect changes
53369 onContextClick : function (ev,dom)
53371 ev.preventDefault();
53372 var cn = dom.className;
53374 if (!cn.match(/x-ed-loc-/)) {
53377 var n = cn.split('-').pop();
53378 var ans = this.footerEls;
53381 this.editorcore.selectNode(sel);
53384 this.updateToolbar(null, null, sel);
53401 * Ext JS Library 1.1.1
53402 * Copyright(c) 2006-2007, Ext JS, LLC.
53404 * Originally Released Under LGPL - original licence link has changed is not relivant.
53407 * <script type="text/javascript">
53411 * @class Roo.form.BasicForm
53412 * @extends Roo.util.Observable
53413 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53415 * @param {String/HTMLElement/Roo.Element} el The form element or its id
53416 * @param {Object} config Configuration options
53418 Roo.form.BasicForm = function(el, config){
53419 this.allItems = [];
53420 this.childForms = [];
53421 Roo.apply(this, config);
53423 * The Roo.form.Field items in this form.
53424 * @type MixedCollection
53428 this.items = new Roo.util.MixedCollection(false, function(o){
53429 return o.id || (o.id = Roo.id());
53433 * @event beforeaction
53434 * Fires before any action is performed. Return false to cancel the action.
53435 * @param {Form} this
53436 * @param {Action} action The action to be performed
53438 beforeaction: true,
53440 * @event actionfailed
53441 * Fires when an action fails.
53442 * @param {Form} this
53443 * @param {Action} action The action that failed
53445 actionfailed : true,
53447 * @event actioncomplete
53448 * Fires when an action is completed.
53449 * @param {Form} this
53450 * @param {Action} action The action that completed
53452 actioncomplete : true
53457 Roo.form.BasicForm.superclass.constructor.call(this);
53459 Roo.form.BasicForm.popover.apply();
53462 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53464 * @cfg {String} method
53465 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53468 * @cfg {DataReader} reader
53469 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53470 * This is optional as there is built-in support for processing JSON.
53473 * @cfg {DataReader} errorReader
53474 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53475 * This is completely optional as there is built-in support for processing JSON.
53478 * @cfg {String} url
53479 * The URL to use for form actions if one isn't supplied in the action options.
53482 * @cfg {Boolean} fileUpload
53483 * Set to true if this form is a file upload.
53487 * @cfg {Object} baseParams
53488 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53493 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53498 activeAction : null,
53501 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53502 * or setValues() data instead of when the form was first created.
53504 trackResetOnLoad : false,
53508 * childForms - used for multi-tab forms
53511 childForms : false,
53514 * allItems - full list of fields.
53520 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53521 * element by passing it or its id or mask the form itself by passing in true.
53524 waitMsgTarget : false,
53529 disableMask : false,
53532 * @cfg {Boolean} errorMask (true|false) default false
53537 * @cfg {Number} maskOffset Default 100
53542 initEl : function(el){
53543 this.el = Roo.get(el);
53544 this.id = this.el.id || Roo.id();
53545 this.el.on('submit', this.onSubmit, this);
53546 this.el.addClass('x-form');
53550 onSubmit : function(e){
53555 * Returns true if client-side validation on the form is successful.
53558 isValid : function(){
53560 var target = false;
53561 this.items.each(function(f){
53568 if(!target && f.el.isVisible(true)){
53573 if(this.errorMask && !valid){
53574 Roo.form.BasicForm.popover.mask(this, target);
53580 * Returns array of invalid form fields.
53584 invalidFields : function()
53587 this.items.each(function(f){
53600 * DEPRICATED Returns true if any fields in this form have changed since their original load.
53603 isDirty : function(){
53605 this.items.each(function(f){
53615 * Returns true if any fields in this form have changed since their original load. (New version)
53619 hasChanged : function()
53622 this.items.each(function(f){
53623 if(f.hasChanged()){
53632 * Resets all hasChanged to 'false' -
53633 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
53634 * So hasChanged storage is only to be used for this purpose
53637 resetHasChanged : function()
53639 this.items.each(function(f){
53640 f.resetHasChanged();
53647 * Performs a predefined action (submit or load) or custom actions you define on this form.
53648 * @param {String} actionName The name of the action type
53649 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
53650 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
53651 * accept other config options):
53653 Property Type Description
53654 ---------------- --------------- ----------------------------------------------------------------------------------
53655 url String The url for the action (defaults to the form's url)
53656 method String The form method to use (defaults to the form's method, or POST if not defined)
53657 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
53658 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
53659 validate the form on the client (defaults to false)
53661 * @return {BasicForm} this
53663 doAction : function(action, options){
53664 if(typeof action == 'string'){
53665 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
53667 if(this.fireEvent('beforeaction', this, action) !== false){
53668 this.beforeAction(action);
53669 action.run.defer(100, action);
53675 * Shortcut to do a submit action.
53676 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53677 * @return {BasicForm} this
53679 submit : function(options){
53680 this.doAction('submit', options);
53685 * Shortcut to do a load action.
53686 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53687 * @return {BasicForm} this
53689 load : function(options){
53690 this.doAction('load', options);
53695 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
53696 * @param {Record} record The record to edit
53697 * @return {BasicForm} this
53699 updateRecord : function(record){
53700 record.beginEdit();
53701 var fs = record.fields;
53702 fs.each(function(f){
53703 var field = this.findField(f.name);
53705 record.set(f.name, field.getValue());
53713 * Loads an Roo.data.Record into this form.
53714 * @param {Record} record The record to load
53715 * @return {BasicForm} this
53717 loadRecord : function(record){
53718 this.setValues(record.data);
53723 beforeAction : function(action){
53724 var o = action.options;
53726 if(!this.disableMask) {
53727 if(this.waitMsgTarget === true){
53728 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
53729 }else if(this.waitMsgTarget){
53730 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
53731 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
53733 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
53741 afterAction : function(action, success){
53742 this.activeAction = null;
53743 var o = action.options;
53745 if(!this.disableMask) {
53746 if(this.waitMsgTarget === true){
53748 }else if(this.waitMsgTarget){
53749 this.waitMsgTarget.unmask();
53751 Roo.MessageBox.updateProgress(1);
53752 Roo.MessageBox.hide();
53760 Roo.callback(o.success, o.scope, [this, action]);
53761 this.fireEvent('actioncomplete', this, action);
53765 // failure condition..
53766 // we have a scenario where updates need confirming.
53767 // eg. if a locking scenario exists..
53768 // we look for { errors : { needs_confirm : true }} in the response.
53770 (typeof(action.result) != 'undefined') &&
53771 (typeof(action.result.errors) != 'undefined') &&
53772 (typeof(action.result.errors.needs_confirm) != 'undefined')
53775 Roo.MessageBox.confirm(
53776 "Change requires confirmation",
53777 action.result.errorMsg,
53782 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
53792 Roo.callback(o.failure, o.scope, [this, action]);
53793 // show an error message if no failed handler is set..
53794 if (!this.hasListener('actionfailed')) {
53795 Roo.MessageBox.alert("Error",
53796 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
53797 action.result.errorMsg :
53798 "Saving Failed, please check your entries or try again"
53802 this.fireEvent('actionfailed', this, action);
53808 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
53809 * @param {String} id The value to search for
53812 findField : function(id){
53813 var field = this.items.get(id);
53815 this.items.each(function(f){
53816 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
53822 return field || null;
53826 * Add a secondary form to this one,
53827 * Used to provide tabbed forms. One form is primary, with hidden values
53828 * which mirror the elements from the other forms.
53830 * @param {Roo.form.Form} form to add.
53833 addForm : function(form)
53836 if (this.childForms.indexOf(form) > -1) {
53840 this.childForms.push(form);
53842 Roo.each(form.allItems, function (fe) {
53844 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
53845 if (this.findField(n)) { // already added..
53848 var add = new Roo.form.Hidden({
53851 add.render(this.el);
53858 * Mark fields in this form invalid in bulk.
53859 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
53860 * @return {BasicForm} this
53862 markInvalid : function(errors){
53863 if(errors instanceof Array){
53864 for(var i = 0, len = errors.length; i < len; i++){
53865 var fieldError = errors[i];
53866 var f = this.findField(fieldError.id);
53868 f.markInvalid(fieldError.msg);
53874 if(typeof errors[id] != 'function' && (field = this.findField(id))){
53875 field.markInvalid(errors[id]);
53879 Roo.each(this.childForms || [], function (f) {
53880 f.markInvalid(errors);
53887 * Set values for fields in this form in bulk.
53888 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
53889 * @return {BasicForm} this
53891 setValues : function(values){
53892 if(values instanceof Array){ // array of objects
53893 for(var i = 0, len = values.length; i < len; i++){
53895 var f = this.findField(v.id);
53897 f.setValue(v.value);
53898 if(this.trackResetOnLoad){
53899 f.originalValue = f.getValue();
53903 }else{ // object hash
53906 if(typeof values[id] != 'function' && (field = this.findField(id))){
53908 if (field.setFromData &&
53909 field.valueField &&
53910 field.displayField &&
53911 // combos' with local stores can
53912 // be queried via setValue()
53913 // to set their value..
53914 (field.store && !field.store.isLocal)
53918 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
53919 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
53920 field.setFromData(sd);
53923 field.setValue(values[id]);
53927 if(this.trackResetOnLoad){
53928 field.originalValue = field.getValue();
53933 this.resetHasChanged();
53936 Roo.each(this.childForms || [], function (f) {
53937 f.setValues(values);
53938 f.resetHasChanged();
53945 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
53946 * they are returned as an array.
53947 * @param {Boolean} asString
53950 getValues : function(asString)
53952 if (this.childForms) {
53953 // copy values from the child forms
53954 Roo.each(this.childForms, function (f) {
53955 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
53960 if (typeof(FormData) != 'undefined' && asString !== true) {
53961 // this relies on a 'recent' version of chrome apparently...
53963 var fd = (new FormData(this.el.dom)).entries();
53965 var ent = fd.next();
53966 while (!ent.done) {
53967 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
53978 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
53979 if(asString === true){
53982 return Roo.urlDecode(fs);
53986 * Returns the fields in this form as an object with key/value pairs.
53987 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
53988 * Normally this will not return readOnly data
53989 * @param {Boolean} with_readonly return readonly field data.
53992 getFieldValues : function(with_readonly)
53994 if (this.childForms) {
53995 // copy values from the child forms
53996 // should this call getFieldValues - probably not as we do not currently copy
53997 // hidden fields when we generate..
53998 Roo.each(this.childForms, function (f) {
53999 this.setValues(f.getFieldValues());
54004 this.items.each(function(f){
54006 if (f.readOnly && with_readonly !== true) {
54007 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54008 // if a subform contains a copy of them.
54009 // if you have subforms with the same editable data, you will need to copy the data back
54013 if (!f.getName()) {
54016 var v = f.getValue();
54017 if (f.inputType =='radio') {
54018 if (typeof(ret[f.getName()]) == 'undefined') {
54019 ret[f.getName()] = ''; // empty..
54022 if (!f.el.dom.checked) {
54026 v = f.el.dom.value;
54030 // not sure if this supported any more..
54031 if ((typeof(v) == 'object') && f.getRawValue) {
54032 v = f.getRawValue() ; // dates..
54034 // combo boxes where name != hiddenName...
54035 if (f.name != f.getName()) {
54036 ret[f.name] = f.getRawValue();
54038 ret[f.getName()] = v;
54045 * Clears all invalid messages in this form.
54046 * @return {BasicForm} this
54048 clearInvalid : function(){
54049 this.items.each(function(f){
54053 Roo.each(this.childForms || [], function (f) {
54062 * Resets this form.
54063 * @return {BasicForm} this
54065 reset : function(){
54066 this.items.each(function(f){
54070 Roo.each(this.childForms || [], function (f) {
54073 this.resetHasChanged();
54079 * Add Roo.form components to this form.
54080 * @param {Field} field1
54081 * @param {Field} field2 (optional)
54082 * @param {Field} etc (optional)
54083 * @return {BasicForm} this
54086 this.items.addAll(Array.prototype.slice.call(arguments, 0));
54092 * Removes a field from the items collection (does NOT remove its markup).
54093 * @param {Field} field
54094 * @return {BasicForm} this
54096 remove : function(field){
54097 this.items.remove(field);
54102 * Looks at the fields in this form, checks them for an id attribute,
54103 * and calls applyTo on the existing dom element with that id.
54104 * @return {BasicForm} this
54106 render : function(){
54107 this.items.each(function(f){
54108 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54116 * Calls {@link Ext#apply} for all fields in this form with the passed object.
54117 * @param {Object} values
54118 * @return {BasicForm} this
54120 applyToFields : function(o){
54121 this.items.each(function(f){
54128 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54129 * @param {Object} values
54130 * @return {BasicForm} this
54132 applyIfToFields : function(o){
54133 this.items.each(function(f){
54141 Roo.BasicForm = Roo.form.BasicForm;
54143 Roo.apply(Roo.form.BasicForm, {
54157 intervalID : false,
54163 if(this.isApplied){
54168 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54169 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54170 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54171 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54174 this.maskEl.top.enableDisplayMode("block");
54175 this.maskEl.left.enableDisplayMode("block");
54176 this.maskEl.bottom.enableDisplayMode("block");
54177 this.maskEl.right.enableDisplayMode("block");
54179 Roo.get(document.body).on('click', function(){
54183 Roo.get(document.body).on('touchstart', function(){
54187 this.isApplied = true
54190 mask : function(form, target)
54194 this.target = target;
54196 if(!this.form.errorMask || !target.el){
54200 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54202 var ot = this.target.el.calcOffsetsTo(scrollable);
54204 var scrollTo = ot[1] - this.form.maskOffset;
54206 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54208 scrollable.scrollTo('top', scrollTo);
54210 var el = this.target.wrap || this.target.el;
54212 var box = el.getBox();
54214 this.maskEl.top.setStyle('position', 'absolute');
54215 this.maskEl.top.setStyle('z-index', 10000);
54216 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54217 this.maskEl.top.setLeft(0);
54218 this.maskEl.top.setTop(0);
54219 this.maskEl.top.show();
54221 this.maskEl.left.setStyle('position', 'absolute');
54222 this.maskEl.left.setStyle('z-index', 10000);
54223 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54224 this.maskEl.left.setLeft(0);
54225 this.maskEl.left.setTop(box.y - this.padding);
54226 this.maskEl.left.show();
54228 this.maskEl.bottom.setStyle('position', 'absolute');
54229 this.maskEl.bottom.setStyle('z-index', 10000);
54230 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54231 this.maskEl.bottom.setLeft(0);
54232 this.maskEl.bottom.setTop(box.bottom + this.padding);
54233 this.maskEl.bottom.show();
54235 this.maskEl.right.setStyle('position', 'absolute');
54236 this.maskEl.right.setStyle('z-index', 10000);
54237 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54238 this.maskEl.right.setLeft(box.right + this.padding);
54239 this.maskEl.right.setTop(box.y - this.padding);
54240 this.maskEl.right.show();
54242 this.intervalID = window.setInterval(function() {
54243 Roo.form.BasicForm.popover.unmask();
54246 window.onwheel = function(){ return false;};
54248 (function(){ this.isMasked = true; }).defer(500, this);
54252 unmask : function()
54254 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54258 this.maskEl.top.setStyle('position', 'absolute');
54259 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54260 this.maskEl.top.hide();
54262 this.maskEl.left.setStyle('position', 'absolute');
54263 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54264 this.maskEl.left.hide();
54266 this.maskEl.bottom.setStyle('position', 'absolute');
54267 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54268 this.maskEl.bottom.hide();
54270 this.maskEl.right.setStyle('position', 'absolute');
54271 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54272 this.maskEl.right.hide();
54274 window.onwheel = function(){ return true;};
54276 if(this.intervalID){
54277 window.clearInterval(this.intervalID);
54278 this.intervalID = false;
54281 this.isMasked = false;
54289 * Ext JS Library 1.1.1
54290 * Copyright(c) 2006-2007, Ext JS, LLC.
54292 * Originally Released Under LGPL - original licence link has changed is not relivant.
54295 * <script type="text/javascript">
54299 * @class Roo.form.Form
54300 * @extends Roo.form.BasicForm
54301 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54302 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54304 * @param {Object} config Configuration options
54306 Roo.form.Form = function(config){
54308 if (config.items) {
54309 xitems = config.items;
54310 delete config.items;
54314 Roo.form.Form.superclass.constructor.call(this, null, config);
54315 this.url = this.url || this.action;
54317 this.root = new Roo.form.Layout(Roo.applyIf({
54321 this.active = this.root;
54323 * Array of all the buttons that have been added to this form via {@link addButton}
54327 this.allItems = [];
54330 * @event clientvalidation
54331 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54332 * @param {Form} this
54333 * @param {Boolean} valid true if the form has passed client-side validation
54335 clientvalidation: true,
54338 * Fires when the form is rendered
54339 * @param {Roo.form.Form} form
54344 if (this.progressUrl) {
54345 // push a hidden field onto the list of fields..
54349 name : 'UPLOAD_IDENTIFIER'
54354 Roo.each(xitems, this.addxtype, this);
54358 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54360 * @cfg {Roo.Button} buttons[] buttons at bottom of form
54364 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54367 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54370 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54372 buttonAlign:'center',
54375 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54380 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54381 * This property cascades to child containers if not set.
54386 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54387 * fires a looping event with that state. This is required to bind buttons to the valid
54388 * state using the config value formBind:true on the button.
54390 monitorValid : false,
54393 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54398 * @cfg {String} progressUrl - Url to return progress data
54401 progressUrl : false,
54403 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54404 * sending a formdata with extra parameters - eg uploaded elements.
54410 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54411 * fields are added and the column is closed. If no fields are passed the column remains open
54412 * until end() is called.
54413 * @param {Object} config The config to pass to the column
54414 * @param {Field} field1 (optional)
54415 * @param {Field} field2 (optional)
54416 * @param {Field} etc (optional)
54417 * @return Column The column container object
54419 column : function(c){
54420 var col = new Roo.form.Column(c);
54422 if(arguments.length > 1){ // duplicate code required because of Opera
54423 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54430 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54431 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54432 * until end() is called.
54433 * @param {Object} config The config to pass to the fieldset
54434 * @param {Field} field1 (optional)
54435 * @param {Field} field2 (optional)
54436 * @param {Field} etc (optional)
54437 * @return FieldSet The fieldset container object
54439 fieldset : function(c){
54440 var fs = new Roo.form.FieldSet(c);
54442 if(arguments.length > 1){ // duplicate code required because of Opera
54443 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54450 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54451 * fields are added and the container is closed. If no fields are passed the container remains open
54452 * until end() is called.
54453 * @param {Object} config The config to pass to the Layout
54454 * @param {Field} field1 (optional)
54455 * @param {Field} field2 (optional)
54456 * @param {Field} etc (optional)
54457 * @return Layout The container object
54459 container : function(c){
54460 var l = new Roo.form.Layout(c);
54462 if(arguments.length > 1){ // duplicate code required because of Opera
54463 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54470 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54471 * @param {Object} container A Roo.form.Layout or subclass of Layout
54472 * @return {Form} this
54474 start : function(c){
54475 // cascade label info
54476 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54477 this.active.stack.push(c);
54478 c.ownerCt = this.active;
54484 * Closes the current open container
54485 * @return {Form} this
54488 if(this.active == this.root){
54491 this.active = this.active.ownerCt;
54496 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
54497 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54498 * as the label of the field.
54499 * @param {Field} field1
54500 * @param {Field} field2 (optional)
54501 * @param {Field} etc. (optional)
54502 * @return {Form} this
54505 this.active.stack.push.apply(this.active.stack, arguments);
54506 this.allItems.push.apply(this.allItems,arguments);
54508 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54509 if(a[i].isFormField){
54514 Roo.form.Form.superclass.add.apply(this, r);
54524 * Find any element that has been added to a form, using it's ID or name
54525 * This can include framesets, columns etc. along with regular fields..
54526 * @param {String} id - id or name to find.
54528 * @return {Element} e - or false if nothing found.
54530 findbyId : function(id)
54536 Roo.each(this.allItems, function(f){
54537 if (f.id == id || f.name == id ){
54548 * Render this form into the passed container. This should only be called once!
54549 * @param {String/HTMLElement/Element} container The element this component should be rendered into
54550 * @return {Form} this
54552 render : function(ct)
54558 var o = this.autoCreate || {
54560 method : this.method || 'POST',
54561 id : this.id || Roo.id()
54563 this.initEl(ct.createChild(o));
54565 this.root.render(this.el);
54569 this.items.each(function(f){
54570 f.render('x-form-el-'+f.id);
54573 if(this.buttons.length > 0){
54574 // tables are required to maintain order and for correct IE layout
54575 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
54576 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
54577 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
54579 var tr = tb.getElementsByTagName('tr')[0];
54580 for(var i = 0, len = this.buttons.length; i < len; i++) {
54581 var b = this.buttons[i];
54582 var td = document.createElement('td');
54583 td.className = 'x-form-btn-td';
54584 b.render(tr.appendChild(td));
54587 if(this.monitorValid){ // initialize after render
54588 this.startMonitoring();
54590 this.fireEvent('rendered', this);
54595 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
54596 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
54597 * object or a valid Roo.DomHelper element config
54598 * @param {Function} handler The function called when the button is clicked
54599 * @param {Object} scope (optional) The scope of the handler function
54600 * @return {Roo.Button}
54602 addButton : function(config, handler, scope){
54606 minWidth: this.minButtonWidth,
54609 if(typeof config == "string"){
54612 Roo.apply(bc, config);
54614 var btn = new Roo.Button(null, bc);
54615 this.buttons.push(btn);
54620 * Adds a series of form elements (using the xtype property as the factory method.
54621 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
54622 * @param {Object} config
54625 addxtype : function()
54627 var ar = Array.prototype.slice.call(arguments, 0);
54629 for(var i = 0; i < ar.length; i++) {
54631 continue; // skip -- if this happends something invalid got sent, we
54632 // should ignore it, as basically that interface element will not show up
54633 // and that should be pretty obvious!!
54636 if (Roo.form[ar[i].xtype]) {
54638 var fe = Roo.factory(ar[i], Roo.form);
54644 fe.store.form = this;
54649 this.allItems.push(fe);
54650 if (fe.items && fe.addxtype) {
54651 fe.addxtype.apply(fe, fe.items);
54661 // console.log('adding ' + ar[i].xtype);
54663 if (ar[i].xtype == 'Button') {
54664 //console.log('adding button');
54665 //console.log(ar[i]);
54666 this.addButton(ar[i]);
54667 this.allItems.push(fe);
54671 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
54672 alert('end is not supported on xtype any more, use items');
54674 // //console.log('adding end');
54682 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
54683 * option "monitorValid"
54685 startMonitoring : function(){
54688 Roo.TaskMgr.start({
54689 run : this.bindHandler,
54690 interval : this.monitorPoll || 200,
54697 * Stops monitoring of the valid state of this form
54699 stopMonitoring : function(){
54700 this.bound = false;
54704 bindHandler : function(){
54706 return false; // stops binding
54709 this.items.each(function(f){
54710 if(!f.isValid(true)){
54715 for(var i = 0, len = this.buttons.length; i < len; i++){
54716 var btn = this.buttons[i];
54717 if(btn.formBind === true && btn.disabled === valid){
54718 btn.setDisabled(!valid);
54721 this.fireEvent('clientvalidation', this, valid);
54735 Roo.Form = Roo.form.Form;
54738 * Ext JS Library 1.1.1
54739 * Copyright(c) 2006-2007, Ext JS, LLC.
54741 * Originally Released Under LGPL - original licence link has changed is not relivant.
54744 * <script type="text/javascript">
54747 // as we use this in bootstrap.
54748 Roo.namespace('Roo.form');
54750 * @class Roo.form.Action
54751 * Internal Class used to handle form actions
54753 * @param {Roo.form.BasicForm} el The form element or its id
54754 * @param {Object} config Configuration options
54759 // define the action interface
54760 Roo.form.Action = function(form, options){
54762 this.options = options || {};
54765 * Client Validation Failed
54768 Roo.form.Action.CLIENT_INVALID = 'client';
54770 * Server Validation Failed
54773 Roo.form.Action.SERVER_INVALID = 'server';
54775 * Connect to Server Failed
54778 Roo.form.Action.CONNECT_FAILURE = 'connect';
54780 * Reading Data from Server Failed
54783 Roo.form.Action.LOAD_FAILURE = 'load';
54785 Roo.form.Action.prototype = {
54787 failureType : undefined,
54788 response : undefined,
54789 result : undefined,
54791 // interface method
54792 run : function(options){
54796 // interface method
54797 success : function(response){
54801 // interface method
54802 handleResponse : function(response){
54806 // default connection failure
54807 failure : function(response){
54809 this.response = response;
54810 this.failureType = Roo.form.Action.CONNECT_FAILURE;
54811 this.form.afterAction(this, false);
54814 processResponse : function(response){
54815 this.response = response;
54816 if(!response.responseText){
54819 this.result = this.handleResponse(response);
54820 return this.result;
54823 // utility functions used internally
54824 getUrl : function(appendParams){
54825 var url = this.options.url || this.form.url || this.form.el.dom.action;
54827 var p = this.getParams();
54829 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
54835 getMethod : function(){
54836 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
54839 getParams : function(){
54840 var bp = this.form.baseParams;
54841 var p = this.options.params;
54843 if(typeof p == "object"){
54844 p = Roo.urlEncode(Roo.applyIf(p, bp));
54845 }else if(typeof p == 'string' && bp){
54846 p += '&' + Roo.urlEncode(bp);
54849 p = Roo.urlEncode(bp);
54854 createCallback : function(){
54856 success: this.success,
54857 failure: this.failure,
54859 timeout: (this.form.timeout*1000),
54860 upload: this.form.fileUpload ? this.success : undefined
54865 Roo.form.Action.Submit = function(form, options){
54866 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
54869 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
54872 haveProgress : false,
54873 uploadComplete : false,
54875 // uploadProgress indicator.
54876 uploadProgress : function()
54878 if (!this.form.progressUrl) {
54882 if (!this.haveProgress) {
54883 Roo.MessageBox.progress("Uploading", "Uploading");
54885 if (this.uploadComplete) {
54886 Roo.MessageBox.hide();
54890 this.haveProgress = true;
54892 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
54894 var c = new Roo.data.Connection();
54896 url : this.form.progressUrl,
54901 success : function(req){
54902 //console.log(data);
54906 rdata = Roo.decode(req.responseText)
54908 Roo.log("Invalid data from server..");
54912 if (!rdata || !rdata.success) {
54914 Roo.MessageBox.alert(Roo.encode(rdata));
54917 var data = rdata.data;
54919 if (this.uploadComplete) {
54920 Roo.MessageBox.hide();
54925 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
54926 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
54929 this.uploadProgress.defer(2000,this);
54932 failure: function(data) {
54933 Roo.log('progress url failed ');
54944 // run get Values on the form, so it syncs any secondary forms.
54945 this.form.getValues();
54947 var o = this.options;
54948 var method = this.getMethod();
54949 var isPost = method == 'POST';
54950 if(o.clientValidation === false || this.form.isValid()){
54952 if (this.form.progressUrl) {
54953 this.form.findField('UPLOAD_IDENTIFIER').setValue(
54954 (new Date() * 1) + '' + Math.random());
54959 Roo.Ajax.request(Roo.apply(this.createCallback(), {
54960 form:this.form.el.dom,
54961 url:this.getUrl(!isPost),
54963 params:isPost ? this.getParams() : null,
54964 isUpload: this.form.fileUpload,
54965 formData : this.form.formData
54968 this.uploadProgress();
54970 }else if (o.clientValidation !== false){ // client validation failed
54971 this.failureType = Roo.form.Action.CLIENT_INVALID;
54972 this.form.afterAction(this, false);
54976 success : function(response)
54978 this.uploadComplete= true;
54979 if (this.haveProgress) {
54980 Roo.MessageBox.hide();
54984 var result = this.processResponse(response);
54985 if(result === true || result.success){
54986 this.form.afterAction(this, true);
54990 this.form.markInvalid(result.errors);
54991 this.failureType = Roo.form.Action.SERVER_INVALID;
54993 this.form.afterAction(this, false);
54995 failure : function(response)
54997 this.uploadComplete= true;
54998 if (this.haveProgress) {
54999 Roo.MessageBox.hide();
55002 this.response = response;
55003 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55004 this.form.afterAction(this, false);
55007 handleResponse : function(response){
55008 if(this.form.errorReader){
55009 var rs = this.form.errorReader.read(response);
55012 for(var i = 0, len = rs.records.length; i < len; i++) {
55013 var r = rs.records[i];
55014 errors[i] = r.data;
55017 if(errors.length < 1){
55021 success : rs.success,
55027 ret = Roo.decode(response.responseText);
55031 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55041 Roo.form.Action.Load = function(form, options){
55042 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55043 this.reader = this.form.reader;
55046 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55051 Roo.Ajax.request(Roo.apply(
55052 this.createCallback(), {
55053 method:this.getMethod(),
55054 url:this.getUrl(false),
55055 params:this.getParams()
55059 success : function(response){
55061 var result = this.processResponse(response);
55062 if(result === true || !result.success || !result.data){
55063 this.failureType = Roo.form.Action.LOAD_FAILURE;
55064 this.form.afterAction(this, false);
55067 this.form.clearInvalid();
55068 this.form.setValues(result.data);
55069 this.form.afterAction(this, true);
55072 handleResponse : function(response){
55073 if(this.form.reader){
55074 var rs = this.form.reader.read(response);
55075 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55077 success : rs.success,
55081 return Roo.decode(response.responseText);
55085 Roo.form.Action.ACTION_TYPES = {
55086 'load' : Roo.form.Action.Load,
55087 'submit' : Roo.form.Action.Submit
55090 * Ext JS Library 1.1.1
55091 * Copyright(c) 2006-2007, Ext JS, LLC.
55093 * Originally Released Under LGPL - original licence link has changed is not relivant.
55096 * <script type="text/javascript">
55100 * @class Roo.form.Layout
55101 * @extends Roo.Component
55102 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55103 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55105 * @param {Object} config Configuration options
55107 Roo.form.Layout = function(config){
55109 if (config.items) {
55110 xitems = config.items;
55111 delete config.items;
55113 Roo.form.Layout.superclass.constructor.call(this, config);
55115 Roo.each(xitems, this.addxtype, this);
55119 Roo.extend(Roo.form.Layout, Roo.Component, {
55121 * @cfg {String/Object} autoCreate
55122 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55125 * @cfg {String/Object/Function} style
55126 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55127 * a function which returns such a specification.
55130 * @cfg {String} labelAlign
55131 * Valid values are "left," "top" and "right" (defaults to "left")
55134 * @cfg {Number} labelWidth
55135 * Fixed width in pixels of all field labels (defaults to undefined)
55138 * @cfg {Boolean} clear
55139 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55143 * @cfg {String} labelSeparator
55144 * The separator to use after field labels (defaults to ':')
55146 labelSeparator : ':',
55148 * @cfg {Boolean} hideLabels
55149 * True to suppress the display of field labels in this layout (defaults to false)
55151 hideLabels : false,
55154 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55159 onRender : function(ct, position){
55160 if(this.el){ // from markup
55161 this.el = Roo.get(this.el);
55162 }else { // generate
55163 var cfg = this.getAutoCreate();
55164 this.el = ct.createChild(cfg, position);
55167 this.el.applyStyles(this.style);
55169 if(this.labelAlign){
55170 this.el.addClass('x-form-label-'+this.labelAlign);
55172 if(this.hideLabels){
55173 this.labelStyle = "display:none";
55174 this.elementStyle = "padding-left:0;";
55176 if(typeof this.labelWidth == 'number'){
55177 this.labelStyle = "width:"+this.labelWidth+"px;";
55178 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55180 if(this.labelAlign == 'top'){
55181 this.labelStyle = "width:auto;";
55182 this.elementStyle = "padding-left:0;";
55185 var stack = this.stack;
55186 var slen = stack.length;
55188 if(!this.fieldTpl){
55189 var t = new Roo.Template(
55190 '<div class="x-form-item {5}">',
55191 '<label for="{0}" style="{2}">{1}{4}</label>',
55192 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55194 '</div><div class="x-form-clear-left"></div>'
55196 t.disableFormats = true;
55198 Roo.form.Layout.prototype.fieldTpl = t;
55200 for(var i = 0; i < slen; i++) {
55201 if(stack[i].isFormField){
55202 this.renderField(stack[i]);
55204 this.renderComponent(stack[i]);
55209 this.el.createChild({cls:'x-form-clear'});
55214 renderField : function(f){
55215 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55218 f.labelStyle||this.labelStyle||'', //2
55219 this.elementStyle||'', //3
55220 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55221 f.itemCls||this.itemCls||'' //5
55222 ], true).getPrevSibling());
55226 renderComponent : function(c){
55227 c.render(c.isLayout ? this.el : this.el.createChild());
55230 * Adds a object form elements (using the xtype property as the factory method.)
55231 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
55232 * @param {Object} config
55234 addxtype : function(o)
55236 // create the lement.
55237 o.form = this.form;
55238 var fe = Roo.factory(o, Roo.form);
55239 this.form.allItems.push(fe);
55240 this.stack.push(fe);
55242 if (fe.isFormField) {
55243 this.form.items.add(fe);
55251 * @class Roo.form.Column
55252 * @extends Roo.form.Layout
55253 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55254 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55256 * @param {Object} config Configuration options
55258 Roo.form.Column = function(config){
55259 Roo.form.Column.superclass.constructor.call(this, config);
55262 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55264 * @cfg {Number/String} width
55265 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55268 * @cfg {String/Object} autoCreate
55269 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55273 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55276 onRender : function(ct, position){
55277 Roo.form.Column.superclass.onRender.call(this, ct, position);
55279 this.el.setWidth(this.width);
55286 * @class Roo.form.Row
55287 * @extends Roo.form.Layout
55288 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55289 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55291 * @param {Object} config Configuration options
55295 Roo.form.Row = function(config){
55296 Roo.form.Row.superclass.constructor.call(this, config);
55299 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55301 * @cfg {Number/String} width
55302 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55305 * @cfg {Number/String} height
55306 * The fixed height of the column in pixels or CSS value (defaults to "auto")
55308 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55312 onRender : function(ct, position){
55313 //console.log('row render');
55315 var t = new Roo.Template(
55316 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55317 '<label for="{0}" style="{2}">{1}{4}</label>',
55318 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55322 t.disableFormats = true;
55324 Roo.form.Layout.prototype.rowTpl = t;
55326 this.fieldTpl = this.rowTpl;
55328 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55329 var labelWidth = 100;
55331 if ((this.labelAlign != 'top')) {
55332 if (typeof this.labelWidth == 'number') {
55333 labelWidth = this.labelWidth
55335 this.padWidth = 20 + labelWidth;
55339 Roo.form.Column.superclass.onRender.call(this, ct, position);
55341 this.el.setWidth(this.width);
55344 this.el.setHeight(this.height);
55349 renderField : function(f){
55350 f.fieldEl = this.fieldTpl.append(this.el, [
55351 f.id, f.fieldLabel,
55352 f.labelStyle||this.labelStyle||'',
55353 this.elementStyle||'',
55354 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55355 f.itemCls||this.itemCls||'',
55356 f.width ? f.width + this.padWidth : 160 + this.padWidth
55363 * @class Roo.form.FieldSet
55364 * @extends Roo.form.Layout
55365 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55366 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55368 * @param {Object} config Configuration options
55370 Roo.form.FieldSet = function(config){
55371 Roo.form.FieldSet.superclass.constructor.call(this, config);
55374 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55376 * @cfg {String} legend
55377 * The text to display as the legend for the FieldSet (defaults to '')
55380 * @cfg {String/Object} autoCreate
55381 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55385 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55388 onRender : function(ct, position){
55389 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55391 this.setLegend(this.legend);
55396 setLegend : function(text){
55398 this.el.child('legend').update(text);
55403 * Ext JS Library 1.1.1
55404 * Copyright(c) 2006-2007, Ext JS, LLC.
55406 * Originally Released Under LGPL - original licence link has changed is not relivant.
55409 * <script type="text/javascript">
55412 * @class Roo.form.VTypes
55413 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55416 Roo.form.VTypes = function(){
55417 // closure these in so they are only created once.
55418 var alpha = /^[a-zA-Z_]+$/;
55419 var alphanum = /^[a-zA-Z0-9_]+$/;
55420 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55421 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55423 // All these messages and functions are configurable
55426 * The function used to validate email addresses
55427 * @param {String} value The email address
55429 'email' : function(v){
55430 return email.test(v);
55433 * The error text to display when the email validation function returns false
55436 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
55438 * The keystroke filter mask to be applied on email input
55441 'emailMask' : /[a-z0-9_\.\-@]/i,
55444 * The function used to validate URLs
55445 * @param {String} value The URL
55447 'url' : function(v){
55448 return url.test(v);
55451 * The error text to display when the url validation function returns false
55454 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55457 * The function used to validate alpha values
55458 * @param {String} value The value
55460 'alpha' : function(v){
55461 return alpha.test(v);
55464 * The error text to display when the alpha validation function returns false
55467 'alphaText' : 'This field should only contain letters and _',
55469 * The keystroke filter mask to be applied on alpha input
55472 'alphaMask' : /[a-z_]/i,
55475 * The function used to validate alphanumeric values
55476 * @param {String} value The value
55478 'alphanum' : function(v){
55479 return alphanum.test(v);
55482 * The error text to display when the alphanumeric validation function returns false
55485 'alphanumText' : 'This field should only contain letters, numbers and _',
55487 * The keystroke filter mask to be applied on alphanumeric input
55490 'alphanumMask' : /[a-z0-9_]/i
55492 }();//<script type="text/javascript">
55495 * @class Roo.form.FCKeditor
55496 * @extends Roo.form.TextArea
55497 * Wrapper around the FCKEditor http://www.fckeditor.net
55499 * Creates a new FCKeditor
55500 * @param {Object} config Configuration options
55502 Roo.form.FCKeditor = function(config){
55503 Roo.form.FCKeditor.superclass.constructor.call(this, config);
55506 * @event editorinit
55507 * Fired when the editor is initialized - you can add extra handlers here..
55508 * @param {FCKeditor} this
55509 * @param {Object} the FCK object.
55516 Roo.form.FCKeditor.editors = { };
55517 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55519 //defaultAutoCreate : {
55520 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
55524 * @cfg {Object} fck options - see fck manual for details.
55529 * @cfg {Object} fck toolbar set (Basic or Default)
55531 toolbarSet : 'Basic',
55533 * @cfg {Object} fck BasePath
55535 basePath : '/fckeditor/',
55543 onRender : function(ct, position)
55546 this.defaultAutoCreate = {
55548 style:"width:300px;height:60px;",
55549 autocomplete: "new-password"
55552 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55555 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55556 if(this.preventScrollbars){
55557 this.el.setStyle("overflow", "hidden");
55559 this.el.setHeight(this.growMin);
55562 //console.log('onrender' + this.getId() );
55563 Roo.form.FCKeditor.editors[this.getId()] = this;
55566 this.replaceTextarea() ;
55570 getEditor : function() {
55571 return this.fckEditor;
55574 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
55575 * @param {Mixed} value The value to set
55579 setValue : function(value)
55581 //console.log('setValue: ' + value);
55583 if(typeof(value) == 'undefined') { // not sure why this is happending...
55586 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55588 //if(!this.el || !this.getEditor()) {
55589 // this.value = value;
55590 //this.setValue.defer(100,this,[value]);
55594 if(!this.getEditor()) {
55598 this.getEditor().SetData(value);
55605 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
55606 * @return {Mixed} value The field value
55608 getValue : function()
55611 if (this.frame && this.frame.dom.style.display == 'none') {
55612 return Roo.form.FCKeditor.superclass.getValue.call(this);
55615 if(!this.el || !this.getEditor()) {
55617 // this.getValue.defer(100,this);
55622 var value=this.getEditor().GetData();
55623 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55624 return Roo.form.FCKeditor.superclass.getValue.call(this);
55630 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
55631 * @return {Mixed} value The field value
55633 getRawValue : function()
55635 if (this.frame && this.frame.dom.style.display == 'none') {
55636 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55639 if(!this.el || !this.getEditor()) {
55640 //this.getRawValue.defer(100,this);
55647 var value=this.getEditor().GetData();
55648 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
55649 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55653 setSize : function(w,h) {
55657 //if (this.frame && this.frame.dom.style.display == 'none') {
55658 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55661 //if(!this.el || !this.getEditor()) {
55662 // this.setSize.defer(100,this, [w,h]);
55668 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55670 this.frame.dom.setAttribute('width', w);
55671 this.frame.dom.setAttribute('height', h);
55672 this.frame.setSize(w,h);
55676 toggleSourceEdit : function(value) {
55680 this.el.dom.style.display = value ? '' : 'none';
55681 this.frame.dom.style.display = value ? 'none' : '';
55686 focus: function(tag)
55688 if (this.frame.dom.style.display == 'none') {
55689 return Roo.form.FCKeditor.superclass.focus.call(this);
55691 if(!this.el || !this.getEditor()) {
55692 this.focus.defer(100,this, [tag]);
55699 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
55700 this.getEditor().Focus();
55702 if (!this.getEditor().Selection.GetSelection()) {
55703 this.focus.defer(100,this, [tag]);
55708 var r = this.getEditor().EditorDocument.createRange();
55709 r.setStart(tgs[0],0);
55710 r.setEnd(tgs[0],0);
55711 this.getEditor().Selection.GetSelection().removeAllRanges();
55712 this.getEditor().Selection.GetSelection().addRange(r);
55713 this.getEditor().Focus();
55720 replaceTextarea : function()
55722 if ( document.getElementById( this.getId() + '___Frame' ) ) {
55725 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
55727 // We must check the elements firstly using the Id and then the name.
55728 var oTextarea = document.getElementById( this.getId() );
55730 var colElementsByName = document.getElementsByName( this.getId() ) ;
55732 oTextarea.style.display = 'none' ;
55734 if ( oTextarea.tabIndex ) {
55735 this.TabIndex = oTextarea.tabIndex ;
55738 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
55739 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
55740 this.frame = Roo.get(this.getId() + '___Frame')
55743 _getConfigHtml : function()
55747 for ( var o in this.fckconfig ) {
55748 sConfig += sConfig.length > 0 ? '&' : '';
55749 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
55752 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
55756 _getIFrameHtml : function()
55758 var sFile = 'fckeditor.html' ;
55759 /* no idea what this is about..
55762 if ( (/fcksource=true/i).test( window.top.location.search ) )
55763 sFile = 'fckeditor.original.html' ;
55768 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
55769 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
55772 var html = '<iframe id="' + this.getId() +
55773 '___Frame" src="' + sLink +
55774 '" width="' + this.width +
55775 '" height="' + this.height + '"' +
55776 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
55777 ' frameborder="0" scrolling="no"></iframe>' ;
55782 _insertHtmlBefore : function( html, element )
55784 if ( element.insertAdjacentHTML ) {
55786 element.insertAdjacentHTML( 'beforeBegin', html ) ;
55788 var oRange = document.createRange() ;
55789 oRange.setStartBefore( element ) ;
55790 var oFragment = oRange.createContextualFragment( html );
55791 element.parentNode.insertBefore( oFragment, element ) ;
55804 //Roo.reg('fckeditor', Roo.form.FCKeditor);
55806 function FCKeditor_OnComplete(editorInstance){
55807 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
55808 f.fckEditor = editorInstance;
55809 //console.log("loaded");
55810 f.fireEvent('editorinit', f, editorInstance);
55830 //<script type="text/javascript">
55832 * @class Roo.form.GridField
55833 * @extends Roo.form.Field
55834 * Embed a grid (or editable grid into a form)
55837 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
55839 * xgrid.store = Roo.data.Store
55840 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
55841 * xgrid.store.reader = Roo.data.JsonReader
55845 * Creates a new GridField
55846 * @param {Object} config Configuration options
55848 Roo.form.GridField = function(config){
55849 Roo.form.GridField.superclass.constructor.call(this, config);
55853 Roo.extend(Roo.form.GridField, Roo.form.Field, {
55855 * @cfg {Number} width - used to restrict width of grid..
55859 * @cfg {Number} height - used to restrict height of grid..
55863 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
55869 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
55870 * {tag: "input", type: "checkbox", autocomplete: "off"})
55872 // defaultAutoCreate : { tag: 'div' },
55873 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
55875 * @cfg {String} addTitle Text to include for adding a title.
55879 onResize : function(){
55880 Roo.form.Field.superclass.onResize.apply(this, arguments);
55883 initEvents : function(){
55884 // Roo.form.Checkbox.superclass.initEvents.call(this);
55885 // has no events...
55890 getResizeEl : function(){
55894 getPositionEl : function(){
55899 onRender : function(ct, position){
55901 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
55902 var style = this.style;
55905 Roo.form.GridField.superclass.onRender.call(this, ct, position);
55906 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
55907 this.viewEl = this.wrap.createChild({ tag: 'div' });
55909 this.viewEl.applyStyles(style);
55912 this.viewEl.setWidth(this.width);
55915 this.viewEl.setHeight(this.height);
55917 //if(this.inputValue !== undefined){
55918 //this.setValue(this.value);
55921 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
55924 this.grid.render();
55925 this.grid.getDataSource().on('remove', this.refreshValue, this);
55926 this.grid.getDataSource().on('update', this.refreshValue, this);
55927 this.grid.on('afteredit', this.refreshValue, this);
55933 * Sets the value of the item.
55934 * @param {String} either an object or a string..
55936 setValue : function(v){
55938 v = v || []; // empty set..
55939 // this does not seem smart - it really only affects memoryproxy grids..
55940 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
55941 var ds = this.grid.getDataSource();
55942 // assumes a json reader..
55944 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
55945 ds.loadData( data);
55947 // clear selection so it does not get stale.
55948 if (this.grid.sm) {
55949 this.grid.sm.clearSelections();
55952 Roo.form.GridField.superclass.setValue.call(this, v);
55953 this.refreshValue();
55954 // should load data in the grid really....
55958 refreshValue: function() {
55960 this.grid.getDataSource().each(function(r) {
55963 this.el.dom.value = Roo.encode(val);
55971 * Ext JS Library 1.1.1
55972 * Copyright(c) 2006-2007, Ext JS, LLC.
55974 * Originally Released Under LGPL - original licence link has changed is not relivant.
55977 * <script type="text/javascript">
55980 * @class Roo.form.DisplayField
55981 * @extends Roo.form.Field
55982 * A generic Field to display non-editable data.
55983 * @cfg {Boolean} closable (true|false) default false
55985 * Creates a new Display Field item.
55986 * @param {Object} config Configuration options
55988 Roo.form.DisplayField = function(config){
55989 Roo.form.DisplayField.superclass.constructor.call(this, config);
55994 * Fires after the click the close btn
55995 * @param {Roo.form.DisplayField} this
56001 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
56002 inputType: 'hidden',
56008 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56010 focusClass : undefined,
56012 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56014 fieldClass: 'x-form-field',
56017 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56019 valueRenderer: undefined,
56023 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56024 * {tag: "input", type: "checkbox", autocomplete: "off"})
56027 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56031 onResize : function(){
56032 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56036 initEvents : function(){
56037 // Roo.form.Checkbox.superclass.initEvents.call(this);
56038 // has no events...
56041 this.closeEl.on('click', this.onClose, this);
56047 getResizeEl : function(){
56051 getPositionEl : function(){
56056 onRender : function(ct, position){
56058 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56059 //if(this.inputValue !== undefined){
56060 this.wrap = this.el.wrap();
56062 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56065 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56068 if (this.bodyStyle) {
56069 this.viewEl.applyStyles(this.bodyStyle);
56071 //this.viewEl.setStyle('padding', '2px');
56073 this.setValue(this.value);
56078 initValue : Roo.emptyFn,
56083 onClick : function(){
56088 * Sets the checked state of the checkbox.
56089 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56091 setValue : function(v){
56093 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
56094 // this might be called before we have a dom element..
56095 if (!this.viewEl) {
56098 this.viewEl.dom.innerHTML = html;
56099 Roo.form.DisplayField.superclass.setValue.call(this, v);
56103 onClose : function(e)
56105 e.preventDefault();
56107 this.fireEvent('close', this);
56116 * @class Roo.form.DayPicker
56117 * @extends Roo.form.Field
56118 * A Day picker show [M] [T] [W] ....
56120 * Creates a new Day Picker
56121 * @param {Object} config Configuration options
56123 Roo.form.DayPicker= function(config){
56124 Roo.form.DayPicker.superclass.constructor.call(this, config);
56128 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
56130 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56132 focusClass : undefined,
56134 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56136 fieldClass: "x-form-field",
56139 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56140 * {tag: "input", type: "checkbox", autocomplete: "off"})
56142 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56145 actionMode : 'viewEl',
56149 inputType : 'hidden',
56152 inputElement: false, // real input element?
56153 basedOn: false, // ????
56155 isFormField: true, // not sure where this is needed!!!!
56157 onResize : function(){
56158 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56159 if(!this.boxLabel){
56160 this.el.alignTo(this.wrap, 'c-c');
56164 initEvents : function(){
56165 Roo.form.Checkbox.superclass.initEvents.call(this);
56166 this.el.on("click", this.onClick, this);
56167 this.el.on("change", this.onClick, this);
56171 getResizeEl : function(){
56175 getPositionEl : function(){
56181 onRender : function(ct, position){
56182 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56184 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56186 var r1 = '<table><tr>';
56187 var r2 = '<tr class="x-form-daypick-icons">';
56188 for (var i=0; i < 7; i++) {
56189 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56190 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
56193 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56194 viewEl.select('img').on('click', this.onClick, this);
56195 this.viewEl = viewEl;
56198 // this will not work on Chrome!!!
56199 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
56200 this.el.on('propertychange', this.setFromHidden, this); //ie
56208 initValue : Roo.emptyFn,
56211 * Returns the checked state of the checkbox.
56212 * @return {Boolean} True if checked, else false
56214 getValue : function(){
56215 return this.el.dom.value;
56220 onClick : function(e){
56221 //this.setChecked(!this.checked);
56222 Roo.get(e.target).toggleClass('x-menu-item-checked');
56223 this.refreshValue();
56224 //if(this.el.dom.checked != this.checked){
56225 // this.setValue(this.el.dom.checked);
56230 refreshValue : function()
56233 this.viewEl.select('img',true).each(function(e,i,n) {
56234 val += e.is(".x-menu-item-checked") ? String(n) : '';
56236 this.setValue(val, true);
56240 * Sets the checked state of the checkbox.
56241 * On is always based on a string comparison between inputValue and the param.
56242 * @param {Boolean/String} value - the value to set
56243 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56245 setValue : function(v,suppressEvent){
56246 if (!this.el.dom) {
56249 var old = this.el.dom.value ;
56250 this.el.dom.value = v;
56251 if (suppressEvent) {
56255 // update display..
56256 this.viewEl.select('img',true).each(function(e,i,n) {
56258 var on = e.is(".x-menu-item-checked");
56259 var newv = v.indexOf(String(n)) > -1;
56261 e.toggleClass('x-menu-item-checked');
56267 this.fireEvent('change', this, v, old);
56272 // handle setting of hidden value by some other method!!?!?
56273 setFromHidden: function()
56278 //console.log("SET FROM HIDDEN");
56279 //alert('setFrom hidden');
56280 this.setValue(this.el.dom.value);
56283 onDestroy : function()
56286 Roo.get(this.viewEl).remove();
56289 Roo.form.DayPicker.superclass.onDestroy.call(this);
56293 * RooJS Library 1.1.1
56294 * Copyright(c) 2008-2011 Alan Knowles
56301 * @class Roo.form.ComboCheck
56302 * @extends Roo.form.ComboBox
56303 * A combobox for multiple select items.
56305 * FIXME - could do with a reset button..
56308 * Create a new ComboCheck
56309 * @param {Object} config Configuration options
56311 Roo.form.ComboCheck = function(config){
56312 Roo.form.ComboCheck.superclass.constructor.call(this, config);
56313 // should verify some data...
56315 // hiddenName = required..
56316 // displayField = required
56317 // valudField == required
56318 var req= [ 'hiddenName', 'displayField', 'valueField' ];
56320 Roo.each(req, function(e) {
56321 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56322 throw "Roo.form.ComboCheck : missing value for: " + e;
56329 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56334 selectedClass: 'x-menu-item-checked',
56337 onRender : function(ct, position){
56343 var cls = 'x-combo-list';
56346 this.tpl = new Roo.Template({
56347 html : '<div class="'+cls+'-item x-menu-check-item">' +
56348 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
56349 '<span>{' + this.displayField + '}</span>' +
56356 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56357 this.view.singleSelect = false;
56358 this.view.multiSelect = true;
56359 this.view.toggleSelect = true;
56360 this.pageTb.add(new Roo.Toolbar.Fill(), {
56363 handler: function()
56370 onViewOver : function(e, t){
56376 onViewClick : function(doFocus,index){
56380 select: function () {
56381 //Roo.log("SELECT CALLED");
56384 selectByValue : function(xv, scrollIntoView){
56385 var ar = this.getValueArray();
56388 Roo.each(ar, function(v) {
56389 if(v === undefined || v === null){
56392 var r = this.findRecord(this.valueField, v);
56394 sels.push(this.store.indexOf(r))
56398 this.view.select(sels);
56404 onSelect : function(record, index){
56405 // Roo.log("onselect Called");
56406 // this is only called by the clear button now..
56407 this.view.clearSelections();
56408 this.setValue('[]');
56409 if (this.value != this.valueBefore) {
56410 this.fireEvent('change', this, this.value, this.valueBefore);
56411 this.valueBefore = this.value;
56414 getValueArray : function()
56419 //Roo.log(this.value);
56420 if (typeof(this.value) == 'undefined') {
56423 var ar = Roo.decode(this.value);
56424 return ar instanceof Array ? ar : []; //?? valid?
56427 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
56432 expand : function ()
56435 Roo.form.ComboCheck.superclass.expand.call(this);
56436 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56437 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56442 collapse : function(){
56443 Roo.form.ComboCheck.superclass.collapse.call(this);
56444 var sl = this.view.getSelectedIndexes();
56445 var st = this.store;
56449 Roo.each(sl, function(i) {
56451 nv.push(r.get(this.valueField));
56453 this.setValue(Roo.encode(nv));
56454 if (this.value != this.valueBefore) {
56456 this.fireEvent('change', this, this.value, this.valueBefore);
56457 this.valueBefore = this.value;
56462 setValue : function(v){
56466 var vals = this.getValueArray();
56468 Roo.each(vals, function(k) {
56469 var r = this.findRecord(this.valueField, k);
56471 tv.push(r.data[this.displayField]);
56472 }else if(this.valueNotFoundText !== undefined){
56473 tv.push( this.valueNotFoundText );
56478 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56479 this.hiddenField.value = v;
56485 * Ext JS Library 1.1.1
56486 * Copyright(c) 2006-2007, Ext JS, LLC.
56488 * Originally Released Under LGPL - original licence link has changed is not relivant.
56491 * <script type="text/javascript">
56495 * @class Roo.form.Signature
56496 * @extends Roo.form.Field
56500 * @param {Object} config Configuration options
56503 Roo.form.Signature = function(config){
56504 Roo.form.Signature.superclass.constructor.call(this, config);
56506 this.addEvents({// not in used??
56509 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56510 * @param {Roo.form.Signature} combo This combo box
56515 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56516 * @param {Roo.form.ComboBox} combo This combo box
56517 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56523 Roo.extend(Roo.form.Signature, Roo.form.Field, {
56525 * @cfg {Object} labels Label to use when rendering a form.
56529 * confirm : "Confirm"
56534 confirm : "Confirm"
56537 * @cfg {Number} width The signature panel width (defaults to 300)
56541 * @cfg {Number} height The signature panel height (defaults to 100)
56545 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56547 allowBlank : false,
56550 // {Object} signPanel The signature SVG panel element (defaults to {})
56552 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56553 isMouseDown : false,
56554 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56555 isConfirmed : false,
56556 // {String} signatureTmp SVG mapping string (defaults to empty string)
56560 defaultAutoCreate : { // modified by initCompnoent..
56566 onRender : function(ct, position){
56568 Roo.form.Signature.superclass.onRender.call(this, ct, position);
56570 this.wrap = this.el.wrap({
56571 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
56574 this.createToolbar(this);
56575 this.signPanel = this.wrap.createChild({
56577 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
56581 this.svgID = Roo.id();
56582 this.svgEl = this.signPanel.createChild({
56583 xmlns : 'http://www.w3.org/2000/svg',
56585 id : this.svgID + "-svg",
56587 height: this.height,
56588 viewBox: '0 0 '+this.width+' '+this.height,
56592 id: this.svgID + "-svg-r",
56594 height: this.height,
56599 id: this.svgID + "-svg-l",
56601 y1: (this.height*0.8), // start set the line in 80% of height
56602 x2: this.width, // end
56603 y2: (this.height*0.8), // end set the line in 80% of height
56605 'stroke-width': "1",
56606 'stroke-dasharray': "3",
56607 'shape-rendering': "crispEdges",
56608 'pointer-events': "none"
56612 id: this.svgID + "-svg-p",
56614 'stroke-width': "3",
56616 'pointer-events': 'none'
56621 this.svgBox = this.svgEl.dom.getScreenCTM();
56623 createSVG : function(){
56624 var svg = this.signPanel;
56625 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
56628 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
56629 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
56630 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
56631 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
56632 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
56633 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
56634 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
56637 isTouchEvent : function(e){
56638 return e.type.match(/^touch/);
56640 getCoords : function (e) {
56641 var pt = this.svgEl.dom.createSVGPoint();
56644 if (this.isTouchEvent(e)) {
56645 pt.x = e.targetTouches[0].clientX;
56646 pt.y = e.targetTouches[0].clientY;
56648 var a = this.svgEl.dom.getScreenCTM();
56649 var b = a.inverse();
56650 var mx = pt.matrixTransform(b);
56651 return mx.x + ',' + mx.y;
56653 //mouse event headler
56654 down : function (e) {
56655 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
56656 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
56658 this.isMouseDown = true;
56660 e.preventDefault();
56662 move : function (e) {
56663 if (this.isMouseDown) {
56664 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
56665 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
56668 e.preventDefault();
56670 up : function (e) {
56671 this.isMouseDown = false;
56672 var sp = this.signatureTmp.split(' ');
56675 if(!sp[sp.length-2].match(/^L/)){
56679 this.signatureTmp = sp.join(" ");
56682 if(this.getValue() != this.signatureTmp){
56683 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56684 this.isConfirmed = false;
56686 e.preventDefault();
56690 * Protected method that will not generally be called directly. It
56691 * is called when the editor creates its toolbar. Override this method if you need to
56692 * add custom toolbar buttons.
56693 * @param {HtmlEditor} editor
56695 createToolbar : function(editor){
56696 function btn(id, toggle, handler){
56697 var xid = fid + '-'+ id ;
56701 cls : 'x-btn-icon x-edit-'+id,
56702 enableToggle:toggle !== false,
56703 scope: editor, // was editor...
56704 handler:handler||editor.relayBtnCmd,
56705 clickEvent:'mousedown',
56706 tooltip: etb.buttonTips[id] || undefined, ///tips ???
56712 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
56716 cls : ' x-signature-btn x-signature-'+id,
56717 scope: editor, // was editor...
56718 handler: this.reset,
56719 clickEvent:'mousedown',
56720 text: this.labels.clear
56727 cls : ' x-signature-btn x-signature-'+id,
56728 scope: editor, // was editor...
56729 handler: this.confirmHandler,
56730 clickEvent:'mousedown',
56731 text: this.labels.confirm
56738 * when user is clicked confirm then show this image.....
56740 * @return {String} Image Data URI
56742 getImageDataURI : function(){
56743 var svg = this.svgEl.dom.parentNode.innerHTML;
56744 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
56749 * @return {Boolean} this.isConfirmed
56751 getConfirmed : function(){
56752 return this.isConfirmed;
56756 * @return {Number} this.width
56758 getWidth : function(){
56763 * @return {Number} this.height
56765 getHeight : function(){
56766 return this.height;
56769 getSignature : function(){
56770 return this.signatureTmp;
56773 reset : function(){
56774 this.signatureTmp = '';
56775 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56776 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
56777 this.isConfirmed = false;
56778 Roo.form.Signature.superclass.reset.call(this);
56780 setSignature : function(s){
56781 this.signatureTmp = s;
56782 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56783 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
56785 this.isConfirmed = false;
56786 Roo.form.Signature.superclass.reset.call(this);
56789 // Roo.log(this.signPanel.dom.contentWindow.up())
56792 setConfirmed : function(){
56796 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
56799 confirmHandler : function(){
56800 if(!this.getSignature()){
56804 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
56805 this.setValue(this.getSignature());
56806 this.isConfirmed = true;
56808 this.fireEvent('confirm', this);
56811 // Subclasses should provide the validation implementation by overriding this
56812 validateValue : function(value){
56813 if(this.allowBlank){
56817 if(this.isConfirmed){
56824 * Ext JS Library 1.1.1
56825 * Copyright(c) 2006-2007, Ext JS, LLC.
56827 * Originally Released Under LGPL - original licence link has changed is not relivant.
56830 * <script type="text/javascript">
56835 * @class Roo.form.ComboBox
56836 * @extends Roo.form.TriggerField
56837 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
56839 * Create a new ComboBox.
56840 * @param {Object} config Configuration options
56842 Roo.form.Select = function(config){
56843 Roo.form.Select.superclass.constructor.call(this, config);
56847 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
56849 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
56852 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
56853 * rendering into an Roo.Editor, defaults to false)
56856 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
56857 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
56860 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
56863 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
56864 * the dropdown list (defaults to undefined, with no header element)
56868 * @cfg {String/Roo.Template} tpl The template to use to render the output
56872 defaultAutoCreate : {tag: "select" },
56874 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
56876 listWidth: undefined,
56878 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
56879 * mode = 'remote' or 'text' if mode = 'local')
56881 displayField: undefined,
56883 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
56884 * mode = 'remote' or 'value' if mode = 'local').
56885 * Note: use of a valueField requires the user make a selection
56886 * in order for a value to be mapped.
56888 valueField: undefined,
56892 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
56893 * field's data value (defaults to the underlying DOM element's name)
56895 hiddenName: undefined,
56897 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
56901 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
56903 selectedClass: 'x-combo-selected',
56905 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
56906 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
56907 * which displays a downward arrow icon).
56909 triggerClass : 'x-form-arrow-trigger',
56911 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
56915 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
56916 * anchor positions (defaults to 'tl-bl')
56918 listAlign: 'tl-bl?',
56920 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
56924 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
56925 * query specified by the allQuery config option (defaults to 'query')
56927 triggerAction: 'query',
56929 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
56930 * (defaults to 4, does not apply if editable = false)
56934 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
56935 * delay (typeAheadDelay) if it matches a known value (defaults to false)
56939 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
56940 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
56944 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
56945 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
56949 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
56950 * when editable = true (defaults to false)
56952 selectOnFocus:false,
56954 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
56956 queryParam: 'query',
56958 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
56959 * when mode = 'remote' (defaults to 'Loading...')
56961 loadingText: 'Loading...',
56963 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
56967 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
56971 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
56972 * traditional select (defaults to true)
56976 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
56980 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
56984 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
56985 * listWidth has a higher value)
56989 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
56990 * allow the user to set arbitrary text into the field (defaults to false)
56992 forceSelection:false,
56994 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
56995 * if typeAhead = true (defaults to 250)
56997 typeAheadDelay : 250,
56999 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57000 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57002 valueNotFoundText : undefined,
57005 * @cfg {String} defaultValue The value displayed after loading the store.
57010 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57012 blockFocus : false,
57015 * @cfg {Boolean} disableClear Disable showing of clear button.
57017 disableClear : false,
57019 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
57021 alwaysQuery : false,
57027 // element that contains real text value.. (when hidden is used..)
57030 onRender : function(ct, position){
57031 Roo.form.Field.prototype.onRender.call(this, ct, position);
57034 this.store.on('beforeload', this.onBeforeLoad, this);
57035 this.store.on('load', this.onLoad, this);
57036 this.store.on('loadexception', this.onLoadException, this);
57037 this.store.load({});
57045 initEvents : function(){
57046 //Roo.form.ComboBox.superclass.initEvents.call(this);
57050 onDestroy : function(){
57053 this.store.un('beforeload', this.onBeforeLoad, this);
57054 this.store.un('load', this.onLoad, this);
57055 this.store.un('loadexception', this.onLoadException, this);
57057 //Roo.form.ComboBox.superclass.onDestroy.call(this);
57061 fireKey : function(e){
57062 if(e.isNavKeyPress() && !this.list.isVisible()){
57063 this.fireEvent("specialkey", this, e);
57068 onResize: function(w, h){
57076 * Allow or prevent the user from directly editing the field text. If false is passed,
57077 * the user will only be able to select from the items defined in the dropdown list. This method
57078 * is the runtime equivalent of setting the 'editable' config option at config time.
57079 * @param {Boolean} value True to allow the user to directly edit the field text
57081 setEditable : function(value){
57086 onBeforeLoad : function(){
57088 Roo.log("Select before load");
57091 this.innerList.update(this.loadingText ?
57092 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57093 //this.restrictHeight();
57094 this.selectedIndex = -1;
57098 onLoad : function(){
57101 var dom = this.el.dom;
57102 dom.innerHTML = '';
57103 var od = dom.ownerDocument;
57105 if (this.emptyText) {
57106 var op = od.createElement('option');
57107 op.setAttribute('value', '');
57108 op.innerHTML = String.format('{0}', this.emptyText);
57109 dom.appendChild(op);
57111 if(this.store.getCount() > 0){
57113 var vf = this.valueField;
57114 var df = this.displayField;
57115 this.store.data.each(function(r) {
57116 // which colmsn to use... testing - cdoe / title..
57117 var op = od.createElement('option');
57118 op.setAttribute('value', r.data[vf]);
57119 op.innerHTML = String.format('{0}', r.data[df]);
57120 dom.appendChild(op);
57122 if (typeof(this.defaultValue != 'undefined')) {
57123 this.setValue(this.defaultValue);
57128 //this.onEmptyResults();
57133 onLoadException : function()
57135 dom.innerHTML = '';
57137 Roo.log("Select on load exception");
57141 Roo.log(this.store.reader.jsonData);
57142 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57143 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57149 onTypeAhead : function(){
57154 onSelect : function(record, index){
57155 Roo.log('on select?');
57157 if(this.fireEvent('beforeselect', this, record, index) !== false){
57158 this.setFromData(index > -1 ? record.data : false);
57160 this.fireEvent('select', this, record, index);
57165 * Returns the currently selected field value or empty string if no value is set.
57166 * @return {String} value The selected value
57168 getValue : function(){
57169 var dom = this.el.dom;
57170 this.value = dom.options[dom.selectedIndex].value;
57176 * Clears any text/value currently set in the field
57178 clearValue : function(){
57180 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57185 * Sets the specified value into the field. If the value finds a match, the corresponding record text
57186 * will be displayed in the field. If the value does not match the data value of an existing item,
57187 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57188 * Otherwise the field will be blank (although the value will still be set).
57189 * @param {String} value The value to match
57191 setValue : function(v){
57192 var d = this.el.dom;
57193 for (var i =0; i < d.options.length;i++) {
57194 if (v == d.options[i].value) {
57195 d.selectedIndex = i;
57203 * @property {Object} the last set data for the element
57208 * Sets the value of the field based on a object which is related to the record format for the store.
57209 * @param {Object} value the value to set as. or false on reset?
57211 setFromData : function(o){
57212 Roo.log('setfrom data?');
57218 reset : function(){
57222 findRecord : function(prop, value){
57227 if(this.store.getCount() > 0){
57228 this.store.each(function(r){
57229 if(r.data[prop] == value){
57239 getName: function()
57241 // returns hidden if it's set..
57242 if (!this.rendered) {return ''};
57243 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
57251 onEmptyResults : function(){
57252 Roo.log('empty results');
57257 * Returns true if the dropdown list is expanded, else false.
57259 isExpanded : function(){
57264 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57265 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57266 * @param {String} value The data value of the item to select
57267 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57268 * selected item if it is not currently in view (defaults to true)
57269 * @return {Boolean} True if the value matched an item in the list, else false
57271 selectByValue : function(v, scrollIntoView){
57272 Roo.log('select By Value');
57275 if(v !== undefined && v !== null){
57276 var r = this.findRecord(this.valueField || this.displayField, v);
57278 this.select(this.store.indexOf(r), scrollIntoView);
57286 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57287 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57288 * @param {Number} index The zero-based index of the list item to select
57289 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57290 * selected item if it is not currently in view (defaults to true)
57292 select : function(index, scrollIntoView){
57293 Roo.log('select ');
57296 this.selectedIndex = index;
57297 this.view.select(index);
57298 if(scrollIntoView !== false){
57299 var el = this.view.getNode(index);
57301 this.innerList.scrollChildIntoView(el, false);
57309 validateBlur : function(){
57316 initQuery : function(){
57317 this.doQuery(this.getRawValue());
57321 doForce : function(){
57322 if(this.el.dom.value.length > 0){
57323 this.el.dom.value =
57324 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57330 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
57331 * query allowing the query action to be canceled if needed.
57332 * @param {String} query The SQL query to execute
57333 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57334 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
57335 * saved in the current store (defaults to false)
57337 doQuery : function(q, forceAll){
57339 Roo.log('doQuery?');
57340 if(q === undefined || q === null){
57345 forceAll: forceAll,
57349 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57353 forceAll = qe.forceAll;
57354 if(forceAll === true || (q.length >= this.minChars)){
57355 if(this.lastQuery != q || this.alwaysQuery){
57356 this.lastQuery = q;
57357 if(this.mode == 'local'){
57358 this.selectedIndex = -1;
57360 this.store.clearFilter();
57362 this.store.filter(this.displayField, q);
57366 this.store.baseParams[this.queryParam] = q;
57368 params: this.getParams(q)
57373 this.selectedIndex = -1;
57380 getParams : function(q){
57382 //p[this.queryParam] = q;
57385 p.limit = this.pageSize;
57391 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57393 collapse : function(){
57398 collapseIf : function(e){
57403 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57405 expand : function(){
57413 * @cfg {Boolean} grow
57417 * @cfg {Number} growMin
57421 * @cfg {Number} growMax
57429 setWidth : function()
57433 getResizeEl : function(){
57436 });//<script type="text/javasscript">
57440 * @class Roo.DDView
57441 * A DnD enabled version of Roo.View.
57442 * @param {Element/String} container The Element in which to create the View.
57443 * @param {String} tpl The template string used to create the markup for each element of the View
57444 * @param {Object} config The configuration properties. These include all the config options of
57445 * {@link Roo.View} plus some specific to this class.<br>
57447 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57448 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57450 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57451 .x-view-drag-insert-above {
57452 border-top:1px dotted #3366cc;
57454 .x-view-drag-insert-below {
57455 border-bottom:1px dotted #3366cc;
57461 Roo.DDView = function(container, tpl, config) {
57462 Roo.DDView.superclass.constructor.apply(this, arguments);
57463 this.getEl().setStyle("outline", "0px none");
57464 this.getEl().unselectable();
57465 if (this.dragGroup) {
57466 this.setDraggable(this.dragGroup.split(","));
57468 if (this.dropGroup) {
57469 this.setDroppable(this.dropGroup.split(","));
57471 if (this.deletable) {
57472 this.setDeletable();
57474 this.isDirtyFlag = false;
57480 Roo.extend(Roo.DDView, Roo.View, {
57481 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57482 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57483 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57484 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57488 reset: Roo.emptyFn,
57490 clearInvalid: Roo.form.Field.prototype.clearInvalid,
57492 validate: function() {
57496 destroy: function() {
57497 this.purgeListeners();
57498 this.getEl.removeAllListeners();
57499 this.getEl().remove();
57500 if (this.dragZone) {
57501 if (this.dragZone.destroy) {
57502 this.dragZone.destroy();
57505 if (this.dropZone) {
57506 if (this.dropZone.destroy) {
57507 this.dropZone.destroy();
57512 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57513 getName: function() {
57517 /** Loads the View from a JSON string representing the Records to put into the Store. */
57518 setValue: function(v) {
57520 throw "DDView.setValue(). DDView must be constructed with a valid Store";
57523 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57524 this.store.proxy = new Roo.data.MemoryProxy(data);
57528 /** @return {String} a parenthesised list of the ids of the Records in the View. */
57529 getValue: function() {
57531 this.store.each(function(rec) {
57532 result += rec.id + ',';
57534 return result.substr(0, result.length - 1) + ')';
57537 getIds: function() {
57538 var i = 0, result = new Array(this.store.getCount());
57539 this.store.each(function(rec) {
57540 result[i++] = rec.id;
57545 isDirty: function() {
57546 return this.isDirtyFlag;
57550 * Part of the Roo.dd.DropZone interface. If no target node is found, the
57551 * whole Element becomes the target, and this causes the drop gesture to append.
57553 getTargetFromEvent : function(e) {
57554 var target = e.getTarget();
57555 while ((target !== null) && (target.parentNode != this.el.dom)) {
57556 target = target.parentNode;
57559 target = this.el.dom.lastChild || this.el.dom;
57565 * Create the drag data which consists of an object which has the property "ddel" as
57566 * the drag proxy element.
57568 getDragData : function(e) {
57569 var target = this.findItemFromChild(e.getTarget());
57571 this.handleSelection(e);
57572 var selNodes = this.getSelectedNodes();
57575 copy: this.copy || (this.allowCopy && e.ctrlKey),
57579 var selectedIndices = this.getSelectedIndexes();
57580 for (var i = 0; i < selectedIndices.length; i++) {
57581 dragData.records.push(this.store.getAt(selectedIndices[i]));
57583 if (selNodes.length == 1) {
57584 dragData.ddel = target.cloneNode(true); // the div element
57586 var div = document.createElement('div'); // create the multi element drag "ghost"
57587 div.className = 'multi-proxy';
57588 for (var i = 0, len = selNodes.length; i < len; i++) {
57589 div.appendChild(selNodes[i].cloneNode(true));
57591 dragData.ddel = div;
57593 //console.log(dragData)
57594 //console.log(dragData.ddel.innerHTML)
57597 //console.log('nodragData')
57601 /** Specify to which ddGroup items in this DDView may be dragged. */
57602 setDraggable: function(ddGroup) {
57603 if (ddGroup instanceof Array) {
57604 Roo.each(ddGroup, this.setDraggable, this);
57607 if (this.dragZone) {
57608 this.dragZone.addToGroup(ddGroup);
57610 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
57611 containerScroll: true,
57615 // Draggability implies selection. DragZone's mousedown selects the element.
57616 if (!this.multiSelect) { this.singleSelect = true; }
57618 // Wire the DragZone's handlers up to methods in *this*
57619 this.dragZone.getDragData = this.getDragData.createDelegate(this);
57623 /** Specify from which ddGroup this DDView accepts drops. */
57624 setDroppable: function(ddGroup) {
57625 if (ddGroup instanceof Array) {
57626 Roo.each(ddGroup, this.setDroppable, this);
57629 if (this.dropZone) {
57630 this.dropZone.addToGroup(ddGroup);
57632 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
57633 containerScroll: true,
57637 // Wire the DropZone's handlers up to methods in *this*
57638 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
57639 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
57640 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
57641 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
57642 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
57646 /** Decide whether to drop above or below a View node. */
57647 getDropPoint : function(e, n, dd){
57648 if (n == this.el.dom) { return "above"; }
57649 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
57650 var c = t + (b - t) / 2;
57651 var y = Roo.lib.Event.getPageY(e);
57659 onNodeEnter : function(n, dd, e, data){
57663 onNodeOver : function(n, dd, e, data){
57664 var pt = this.getDropPoint(e, n, dd);
57665 // set the insert point style on the target node
57666 var dragElClass = this.dropNotAllowed;
57669 if (pt == "above"){
57670 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
57671 targetElClass = "x-view-drag-insert-above";
57673 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
57674 targetElClass = "x-view-drag-insert-below";
57676 if (this.lastInsertClass != targetElClass){
57677 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
57678 this.lastInsertClass = targetElClass;
57681 return dragElClass;
57684 onNodeOut : function(n, dd, e, data){
57685 this.removeDropIndicators(n);
57688 onNodeDrop : function(n, dd, e, data){
57689 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
57692 var pt = this.getDropPoint(e, n, dd);
57693 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
57694 if (pt == "below") { insertAt++; }
57695 for (var i = 0; i < data.records.length; i++) {
57696 var r = data.records[i];
57697 var dup = this.store.getById(r.id);
57698 if (dup && (dd != this.dragZone)) {
57699 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
57702 this.store.insert(insertAt++, r.copy());
57704 data.source.isDirtyFlag = true;
57706 this.store.insert(insertAt++, r);
57708 this.isDirtyFlag = true;
57711 this.dragZone.cachedTarget = null;
57715 removeDropIndicators : function(n){
57717 Roo.fly(n).removeClass([
57718 "x-view-drag-insert-above",
57719 "x-view-drag-insert-below"]);
57720 this.lastInsertClass = "_noclass";
57725 * Utility method. Add a delete option to the DDView's context menu.
57726 * @param {String} imageUrl The URL of the "delete" icon image.
57728 setDeletable: function(imageUrl) {
57729 if (!this.singleSelect && !this.multiSelect) {
57730 this.singleSelect = true;
57732 var c = this.getContextMenu();
57733 this.contextMenu.on("itemclick", function(item) {
57736 this.remove(this.getSelectedIndexes());
57740 this.contextMenu.add({
57747 /** Return the context menu for this DDView. */
57748 getContextMenu: function() {
57749 if (!this.contextMenu) {
57750 // Create the View's context menu
57751 this.contextMenu = new Roo.menu.Menu({
57752 id: this.id + "-contextmenu"
57754 this.el.on("contextmenu", this.showContextMenu, this);
57756 return this.contextMenu;
57759 disableContextMenu: function() {
57760 if (this.contextMenu) {
57761 this.el.un("contextmenu", this.showContextMenu, this);
57765 showContextMenu: function(e, item) {
57766 item = this.findItemFromChild(e.getTarget());
57769 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
57770 this.contextMenu.showAt(e.getXY());
57775 * Remove {@link Roo.data.Record}s at the specified indices.
57776 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
57778 remove: function(selectedIndices) {
57779 selectedIndices = [].concat(selectedIndices);
57780 for (var i = 0; i < selectedIndices.length; i++) {
57781 var rec = this.store.getAt(selectedIndices[i]);
57782 this.store.remove(rec);
57787 * Double click fires the event, but also, if this is draggable, and there is only one other
57788 * related DropZone, it transfers the selected node.
57790 onDblClick : function(e){
57791 var item = this.findItemFromChild(e.getTarget());
57793 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
57796 if (this.dragGroup) {
57797 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
57798 while (targets.indexOf(this.dropZone) > -1) {
57799 targets.remove(this.dropZone);
57801 if (targets.length == 1) {
57802 this.dragZone.cachedTarget = null;
57803 var el = Roo.get(targets[0].getEl());
57804 var box = el.getBox(true);
57805 targets[0].onNodeDrop(el.dom, {
57807 xy: [box.x, box.y + box.height - 1]
57808 }, null, this.getDragData(e));
57814 handleSelection: function(e) {
57815 this.dragZone.cachedTarget = null;
57816 var item = this.findItemFromChild(e.getTarget());
57818 this.clearSelections(true);
57821 if (item && (this.multiSelect || this.singleSelect)){
57822 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
57823 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
57824 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
57825 this.unselect(item);
57827 this.select(item, this.multiSelect && e.ctrlKey);
57828 this.lastSelection = item;
57833 onItemClick : function(item, index, e){
57834 if(this.fireEvent("beforeclick", this, index, item, e) === false){
57840 unselect : function(nodeInfo, suppressEvent){
57841 var node = this.getNode(nodeInfo);
57842 if(node && this.isSelected(node)){
57843 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
57844 Roo.fly(node).removeClass(this.selectedClass);
57845 this.selections.remove(node);
57846 if(!suppressEvent){
57847 this.fireEvent("selectionchange", this, this.selections);
57855 * Ext JS Library 1.1.1
57856 * Copyright(c) 2006-2007, Ext JS, LLC.
57858 * Originally Released Under LGPL - original licence link has changed is not relivant.
57861 * <script type="text/javascript">
57865 * @class Roo.LayoutManager
57866 * @extends Roo.util.Observable
57867 * Base class for layout managers.
57869 Roo.LayoutManager = function(container, config){
57870 Roo.LayoutManager.superclass.constructor.call(this);
57871 this.el = Roo.get(container);
57872 // ie scrollbar fix
57873 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
57874 document.body.scroll = "no";
57875 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
57876 this.el.position('relative');
57878 this.id = this.el.id;
57879 this.el.addClass("x-layout-container");
57880 /** false to disable window resize monitoring @type Boolean */
57881 this.monitorWindowResize = true;
57886 * Fires when a layout is performed.
57887 * @param {Roo.LayoutManager} this
57891 * @event regionresized
57892 * Fires when the user resizes a region.
57893 * @param {Roo.LayoutRegion} region The resized region
57894 * @param {Number} newSize The new size (width for east/west, height for north/south)
57896 "regionresized" : true,
57898 * @event regioncollapsed
57899 * Fires when a region is collapsed.
57900 * @param {Roo.LayoutRegion} region The collapsed region
57902 "regioncollapsed" : true,
57904 * @event regionexpanded
57905 * Fires when a region is expanded.
57906 * @param {Roo.LayoutRegion} region The expanded region
57908 "regionexpanded" : true
57910 this.updating = false;
57911 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57914 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
57916 * Returns true if this layout is currently being updated
57917 * @return {Boolean}
57919 isUpdating : function(){
57920 return this.updating;
57924 * Suspend the LayoutManager from doing auto-layouts while
57925 * making multiple add or remove calls
57927 beginUpdate : function(){
57928 this.updating = true;
57932 * Restore auto-layouts and optionally disable the manager from performing a layout
57933 * @param {Boolean} noLayout true to disable a layout update
57935 endUpdate : function(noLayout){
57936 this.updating = false;
57942 layout: function(){
57946 onRegionResized : function(region, newSize){
57947 this.fireEvent("regionresized", region, newSize);
57951 onRegionCollapsed : function(region){
57952 this.fireEvent("regioncollapsed", region);
57955 onRegionExpanded : function(region){
57956 this.fireEvent("regionexpanded", region);
57960 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
57961 * performs box-model adjustments.
57962 * @return {Object} The size as an object {width: (the width), height: (the height)}
57964 getViewSize : function(){
57966 if(this.el.dom != document.body){
57967 size = this.el.getSize();
57969 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
57971 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
57972 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
57977 * Returns the Element this layout is bound to.
57978 * @return {Roo.Element}
57980 getEl : function(){
57985 * Returns the specified region.
57986 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
57987 * @return {Roo.LayoutRegion}
57989 getRegion : function(target){
57990 return this.regions[target.toLowerCase()];
57993 onWindowResize : function(){
57994 if(this.monitorWindowResize){
58000 * Ext JS Library 1.1.1
58001 * Copyright(c) 2006-2007, Ext JS, LLC.
58003 * Originally Released Under LGPL - original licence link has changed is not relivant.
58006 * <script type="text/javascript">
58009 * @class Roo.BorderLayout
58010 * @extends Roo.LayoutManager
58011 * @children Roo.ContentPanel
58012 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58013 * please see: <br><br>
58014 * <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>
58015 * <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>
58018 var layout = new Roo.BorderLayout(document.body, {
58052 preferredTabWidth: 150
58057 var CP = Roo.ContentPanel;
58059 layout.beginUpdate();
58060 layout.add("north", new CP("north", "North"));
58061 layout.add("south", new CP("south", {title: "South", closable: true}));
58062 layout.add("west", new CP("west", {title: "West"}));
58063 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58064 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58065 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58066 layout.getRegion("center").showPanel("center1");
58067 layout.endUpdate();
58070 <b>The container the layout is rendered into can be either the body element or any other element.
58071 If it is not the body element, the container needs to either be an absolute positioned element,
58072 or you will need to add "position:relative" to the css of the container. You will also need to specify
58073 the container size if it is not the body element.</b>
58076 * Create a new BorderLayout
58077 * @param {String/HTMLElement/Element} container The container this layout is bound to
58078 * @param {Object} config Configuration options
58080 Roo.BorderLayout = function(container, config){
58081 config = config || {};
58082 Roo.BorderLayout.superclass.constructor.call(this, container, config);
58083 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58084 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58085 var target = this.factory.validRegions[i];
58086 if(config[target]){
58087 this.addRegion(target, config[target]);
58092 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58095 * @cfg {Roo.LayoutRegion} east
58098 * @cfg {Roo.LayoutRegion} west
58101 * @cfg {Roo.LayoutRegion} north
58104 * @cfg {Roo.LayoutRegion} south
58107 * @cfg {Roo.LayoutRegion} center
58110 * Creates and adds a new region if it doesn't already exist.
58111 * @param {String} target The target region key (north, south, east, west or center).
58112 * @param {Object} config The regions config object
58113 * @return {BorderLayoutRegion} The new region
58115 addRegion : function(target, config){
58116 if(!this.regions[target]){
58117 var r = this.factory.create(target, this, config);
58118 this.bindRegion(target, r);
58120 return this.regions[target];
58124 bindRegion : function(name, r){
58125 this.regions[name] = r;
58126 r.on("visibilitychange", this.layout, this);
58127 r.on("paneladded", this.layout, this);
58128 r.on("panelremoved", this.layout, this);
58129 r.on("invalidated", this.layout, this);
58130 r.on("resized", this.onRegionResized, this);
58131 r.on("collapsed", this.onRegionCollapsed, this);
58132 r.on("expanded", this.onRegionExpanded, this);
58136 * Performs a layout update.
58138 layout : function(){
58139 if(this.updating) {
58142 var size = this.getViewSize();
58143 var w = size.width;
58144 var h = size.height;
58149 //var x = 0, y = 0;
58151 var rs = this.regions;
58152 var north = rs["north"];
58153 var south = rs["south"];
58154 var west = rs["west"];
58155 var east = rs["east"];
58156 var center = rs["center"];
58157 //if(this.hideOnLayout){ // not supported anymore
58158 //c.el.setStyle("display", "none");
58160 if(north && north.isVisible()){
58161 var b = north.getBox();
58162 var m = north.getMargins();
58163 b.width = w - (m.left+m.right);
58166 centerY = b.height + b.y + m.bottom;
58167 centerH -= centerY;
58168 north.updateBox(this.safeBox(b));
58170 if(south && south.isVisible()){
58171 var b = south.getBox();
58172 var m = south.getMargins();
58173 b.width = w - (m.left+m.right);
58175 var totalHeight = (b.height + m.top + m.bottom);
58176 b.y = h - totalHeight + m.top;
58177 centerH -= totalHeight;
58178 south.updateBox(this.safeBox(b));
58180 if(west && west.isVisible()){
58181 var b = west.getBox();
58182 var m = west.getMargins();
58183 b.height = centerH - (m.top+m.bottom);
58185 b.y = centerY + m.top;
58186 var totalWidth = (b.width + m.left + m.right);
58187 centerX += totalWidth;
58188 centerW -= totalWidth;
58189 west.updateBox(this.safeBox(b));
58191 if(east && east.isVisible()){
58192 var b = east.getBox();
58193 var m = east.getMargins();
58194 b.height = centerH - (m.top+m.bottom);
58195 var totalWidth = (b.width + m.left + m.right);
58196 b.x = w - totalWidth + m.left;
58197 b.y = centerY + m.top;
58198 centerW -= totalWidth;
58199 east.updateBox(this.safeBox(b));
58202 var m = center.getMargins();
58204 x: centerX + m.left,
58205 y: centerY + m.top,
58206 width: centerW - (m.left+m.right),
58207 height: centerH - (m.top+m.bottom)
58209 //if(this.hideOnLayout){
58210 //center.el.setStyle("display", "block");
58212 center.updateBox(this.safeBox(centerBox));
58215 this.fireEvent("layout", this);
58219 safeBox : function(box){
58220 box.width = Math.max(0, box.width);
58221 box.height = Math.max(0, box.height);
58226 * Adds a ContentPanel (or subclass) to this layout.
58227 * @param {String} target The target region key (north, south, east, west or center).
58228 * @param {Roo.ContentPanel} panel The panel to add
58229 * @return {Roo.ContentPanel} The added panel
58231 add : function(target, panel){
58233 target = target.toLowerCase();
58234 return this.regions[target].add(panel);
58238 * Remove a ContentPanel (or subclass) to this layout.
58239 * @param {String} target The target region key (north, south, east, west or center).
58240 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58241 * @return {Roo.ContentPanel} The removed panel
58243 remove : function(target, panel){
58244 target = target.toLowerCase();
58245 return this.regions[target].remove(panel);
58249 * Searches all regions for a panel with the specified id
58250 * @param {String} panelId
58251 * @return {Roo.ContentPanel} The panel or null if it wasn't found
58253 findPanel : function(panelId){
58254 var rs = this.regions;
58255 for(var target in rs){
58256 if(typeof rs[target] != "function"){
58257 var p = rs[target].getPanel(panelId);
58267 * Searches all regions for a panel with the specified id and activates (shows) it.
58268 * @param {String/ContentPanel} panelId The panels id or the panel itself
58269 * @return {Roo.ContentPanel} The shown panel or null
58271 showPanel : function(panelId) {
58272 var rs = this.regions;
58273 for(var target in rs){
58274 var r = rs[target];
58275 if(typeof r != "function"){
58276 if(r.hasPanel(panelId)){
58277 return r.showPanel(panelId);
58285 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58286 * @param {Roo.state.Provider} provider (optional) An alternate state provider
58288 restoreState : function(provider){
58290 provider = Roo.state.Manager;
58292 var sm = new Roo.LayoutStateManager();
58293 sm.init(this, provider);
58297 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
58298 * object should contain properties for each region to add ContentPanels to, and each property's value should be
58299 * a valid ContentPanel config object. Example:
58301 // Create the main layout
58302 var layout = new Roo.BorderLayout('main-ct', {
58313 // Create and add multiple ContentPanels at once via configs
58316 id: 'source-files',
58318 title:'Ext Source Files',
58331 * @param {Object} regions An object containing ContentPanel configs by region name
58333 batchAdd : function(regions){
58334 this.beginUpdate();
58335 for(var rname in regions){
58336 var lr = this.regions[rname];
58338 this.addTypedPanels(lr, regions[rname]);
58345 addTypedPanels : function(lr, ps){
58346 if(typeof ps == 'string'){
58347 lr.add(new Roo.ContentPanel(ps));
58349 else if(ps instanceof Array){
58350 for(var i =0, len = ps.length; i < len; i++){
58351 this.addTypedPanels(lr, ps[i]);
58354 else if(!ps.events){ // raw config?
58356 delete ps.el; // prevent conflict
58357 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58359 else { // panel object assumed!
58364 * Adds a xtype elements to the layout.
58368 xtype : 'ContentPanel',
58375 xtype : 'NestedLayoutPanel',
58381 items : [ ... list of content panels or nested layout panels.. ]
58385 * @param {Object} cfg Xtype definition of item to add.
58387 addxtype : function(cfg)
58389 // basically accepts a pannel...
58390 // can accept a layout region..!?!?
58391 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58393 if (!cfg.xtype.match(/Panel$/)) {
58398 if (typeof(cfg.region) == 'undefined') {
58399 Roo.log("Failed to add Panel, region was not set");
58403 var region = cfg.region;
58409 xitems = cfg.items;
58416 case 'ContentPanel': // ContentPanel (el, cfg)
58417 case 'ScrollPanel': // ContentPanel (el, cfg)
58419 if(cfg.autoCreate) {
58420 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58422 var el = this.el.createChild();
58423 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58426 this.add(region, ret);
58430 case 'TreePanel': // our new panel!
58431 cfg.el = this.el.createChild();
58432 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58433 this.add(region, ret);
58436 case 'NestedLayoutPanel':
58437 // create a new Layout (which is a Border Layout...
58438 var el = this.el.createChild();
58439 var clayout = cfg.layout;
58441 clayout.items = clayout.items || [];
58442 // replace this exitems with the clayout ones..
58443 xitems = clayout.items;
58446 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58447 cfg.background = false;
58449 var layout = new Roo.BorderLayout(el, clayout);
58451 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58452 //console.log('adding nested layout panel ' + cfg.toSource());
58453 this.add(region, ret);
58454 nb = {}; /// find first...
58459 // needs grid and region
58461 //var el = this.getRegion(region).el.createChild();
58462 var el = this.el.createChild();
58463 // create the grid first...
58465 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58467 if (region == 'center' && this.active ) {
58468 cfg.background = false;
58470 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58472 this.add(region, ret);
58473 if (cfg.background) {
58474 ret.on('activate', function(gp) {
58475 if (!gp.grid.rendered) {
58490 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58492 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58493 this.add(region, ret);
58496 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58500 // GridPanel (grid, cfg)
58503 this.beginUpdate();
58507 Roo.each(xitems, function(i) {
58508 region = nb && i.region ? i.region : false;
58510 var add = ret.addxtype(i);
58513 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58514 if (!i.background) {
58515 abn[region] = nb[region] ;
58522 // make the last non-background panel active..
58523 //if (nb) { Roo.log(abn); }
58526 for(var r in abn) {
58527 region = this.getRegion(r);
58529 // tried using nb[r], but it does not work..
58531 region.showPanel(abn[r]);
58542 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58543 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
58544 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58545 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
58548 var CP = Roo.ContentPanel;
58550 var layout = Roo.BorderLayout.create({
58554 panels: [new CP("north", "North")]
58563 panels: [new CP("west", {title: "West"})]
58572 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
58581 panels: [new CP("south", {title: "South", closable: true})]
58588 preferredTabWidth: 150,
58590 new CP("center1", {title: "Close Me", closable: true}),
58591 new CP("center2", {title: "Center Panel", closable: false})
58596 layout.getRegion("center").showPanel("center1");
58601 Roo.BorderLayout.create = function(config, targetEl){
58602 var layout = new Roo.BorderLayout(targetEl || document.body, config);
58603 layout.beginUpdate();
58604 var regions = Roo.BorderLayout.RegionFactory.validRegions;
58605 for(var j = 0, jlen = regions.length; j < jlen; j++){
58606 var lr = regions[j];
58607 if(layout.regions[lr] && config[lr].panels){
58608 var r = layout.regions[lr];
58609 var ps = config[lr].panels;
58610 layout.addTypedPanels(r, ps);
58613 layout.endUpdate();
58618 Roo.BorderLayout.RegionFactory = {
58620 validRegions : ["north","south","east","west","center"],
58623 create : function(target, mgr, config){
58624 target = target.toLowerCase();
58625 if(config.lightweight || config.basic){
58626 return new Roo.BasicLayoutRegion(mgr, config, target);
58630 return new Roo.NorthLayoutRegion(mgr, config);
58632 return new Roo.SouthLayoutRegion(mgr, config);
58634 return new Roo.EastLayoutRegion(mgr, config);
58636 return new Roo.WestLayoutRegion(mgr, config);
58638 return new Roo.CenterLayoutRegion(mgr, config);
58640 throw 'Layout region "'+target+'" not supported.';
58644 * Ext JS Library 1.1.1
58645 * Copyright(c) 2006-2007, Ext JS, LLC.
58647 * Originally Released Under LGPL - original licence link has changed is not relivant.
58650 * <script type="text/javascript">
58654 * @class Roo.BasicLayoutRegion
58655 * @extends Roo.util.Observable
58656 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
58657 * and does not have a titlebar, tabs or any other features. All it does is size and position
58658 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
58660 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
58662 this.position = pos;
58665 * @scope Roo.BasicLayoutRegion
58669 * @event beforeremove
58670 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
58671 * @param {Roo.LayoutRegion} this
58672 * @param {Roo.ContentPanel} panel The panel
58673 * @param {Object} e The cancel event object
58675 "beforeremove" : true,
58677 * @event invalidated
58678 * Fires when the layout for this region is changed.
58679 * @param {Roo.LayoutRegion} this
58681 "invalidated" : true,
58683 * @event visibilitychange
58684 * Fires when this region is shown or hidden
58685 * @param {Roo.LayoutRegion} this
58686 * @param {Boolean} visibility true or false
58688 "visibilitychange" : true,
58690 * @event paneladded
58691 * Fires when a panel is added.
58692 * @param {Roo.LayoutRegion} this
58693 * @param {Roo.ContentPanel} panel The panel
58695 "paneladded" : true,
58697 * @event panelremoved
58698 * Fires when a panel is removed.
58699 * @param {Roo.LayoutRegion} this
58700 * @param {Roo.ContentPanel} panel The panel
58702 "panelremoved" : true,
58704 * @event beforecollapse
58705 * Fires when this region before collapse.
58706 * @param {Roo.LayoutRegion} this
58708 "beforecollapse" : true,
58711 * Fires when this region is collapsed.
58712 * @param {Roo.LayoutRegion} this
58714 "collapsed" : true,
58717 * Fires when this region is expanded.
58718 * @param {Roo.LayoutRegion} this
58723 * Fires when this region is slid into view.
58724 * @param {Roo.LayoutRegion} this
58726 "slideshow" : true,
58729 * Fires when this region slides out of view.
58730 * @param {Roo.LayoutRegion} this
58732 "slidehide" : true,
58734 * @event panelactivated
58735 * Fires when a panel is activated.
58736 * @param {Roo.LayoutRegion} this
58737 * @param {Roo.ContentPanel} panel The activated panel
58739 "panelactivated" : true,
58742 * Fires when the user resizes this region.
58743 * @param {Roo.LayoutRegion} this
58744 * @param {Number} newSize The new size (width for east/west, height for north/south)
58748 /** A collection of panels in this region. @type Roo.util.MixedCollection */
58749 this.panels = new Roo.util.MixedCollection();
58750 this.panels.getKey = this.getPanelId.createDelegate(this);
58752 this.activePanel = null;
58753 // ensure listeners are added...
58755 if (config.listeners || config.events) {
58756 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
58757 listeners : config.listeners || {},
58758 events : config.events || {}
58762 if(skipConfig !== true){
58763 this.applyConfig(config);
58767 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
58768 getPanelId : function(p){
58772 applyConfig : function(config){
58773 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
58774 this.config = config;
58779 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
58780 * the width, for horizontal (north, south) the height.
58781 * @param {Number} newSize The new width or height
58783 resizeTo : function(newSize){
58784 var el = this.el ? this.el :
58785 (this.activePanel ? this.activePanel.getEl() : null);
58787 switch(this.position){
58790 el.setWidth(newSize);
58791 this.fireEvent("resized", this, newSize);
58795 el.setHeight(newSize);
58796 this.fireEvent("resized", this, newSize);
58802 getBox : function(){
58803 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
58806 getMargins : function(){
58807 return this.margins;
58810 updateBox : function(box){
58812 var el = this.activePanel.getEl();
58813 el.dom.style.left = box.x + "px";
58814 el.dom.style.top = box.y + "px";
58815 this.activePanel.setSize(box.width, box.height);
58819 * Returns the container element for this region.
58820 * @return {Roo.Element}
58822 getEl : function(){
58823 return this.activePanel;
58827 * Returns true if this region is currently visible.
58828 * @return {Boolean}
58830 isVisible : function(){
58831 return this.activePanel ? true : false;
58834 setActivePanel : function(panel){
58835 panel = this.getPanel(panel);
58836 if(this.activePanel && this.activePanel != panel){
58837 this.activePanel.setActiveState(false);
58838 this.activePanel.getEl().setLeftTop(-10000,-10000);
58840 this.activePanel = panel;
58841 panel.setActiveState(true);
58843 panel.setSize(this.box.width, this.box.height);
58845 this.fireEvent("panelactivated", this, panel);
58846 this.fireEvent("invalidated");
58850 * Show the specified panel.
58851 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
58852 * @return {Roo.ContentPanel} The shown panel or null
58854 showPanel : function(panel){
58855 if(panel = this.getPanel(panel)){
58856 this.setActivePanel(panel);
58862 * Get the active panel for this region.
58863 * @return {Roo.ContentPanel} The active panel or null
58865 getActivePanel : function(){
58866 return this.activePanel;
58870 * Add the passed ContentPanel(s)
58871 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
58872 * @return {Roo.ContentPanel} The panel added (if only one was added)
58874 add : function(panel){
58875 if(arguments.length > 1){
58876 for(var i = 0, len = arguments.length; i < len; i++) {
58877 this.add(arguments[i]);
58881 if(this.hasPanel(panel)){
58882 this.showPanel(panel);
58885 var el = panel.getEl();
58886 if(el.dom.parentNode != this.mgr.el.dom){
58887 this.mgr.el.dom.appendChild(el.dom);
58889 if(panel.setRegion){
58890 panel.setRegion(this);
58892 this.panels.add(panel);
58893 el.setStyle("position", "absolute");
58894 if(!panel.background){
58895 this.setActivePanel(panel);
58896 if(this.config.initialSize && this.panels.getCount()==1){
58897 this.resizeTo(this.config.initialSize);
58900 this.fireEvent("paneladded", this, panel);
58905 * Returns true if the panel is in this region.
58906 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58907 * @return {Boolean}
58909 hasPanel : function(panel){
58910 if(typeof panel == "object"){ // must be panel obj
58911 panel = panel.getId();
58913 return this.getPanel(panel) ? true : false;
58917 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
58918 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58919 * @param {Boolean} preservePanel Overrides the config preservePanel option
58920 * @return {Roo.ContentPanel} The panel that was removed
58922 remove : function(panel, preservePanel){
58923 panel = this.getPanel(panel);
58928 this.fireEvent("beforeremove", this, panel, e);
58929 if(e.cancel === true){
58932 var panelId = panel.getId();
58933 this.panels.removeKey(panelId);
58938 * Returns the panel specified or null if it's not in this region.
58939 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58940 * @return {Roo.ContentPanel}
58942 getPanel : function(id){
58943 if(typeof id == "object"){ // must be panel obj
58946 return this.panels.get(id);
58950 * Returns this regions position (north/south/east/west/center).
58953 getPosition: function(){
58954 return this.position;
58958 * Ext JS Library 1.1.1
58959 * Copyright(c) 2006-2007, Ext JS, LLC.
58961 * Originally Released Under LGPL - original licence link has changed is not relivant.
58964 * <script type="text/javascript">
58968 * @class Roo.LayoutRegion
58969 * @extends Roo.BasicLayoutRegion
58970 * This class represents a region in a layout manager.
58971 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
58972 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
58973 * @cfg {Boolean} floatable False to disable floating (defaults to true)
58974 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
58975 * @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})
58976 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
58977 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
58978 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
58979 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
58980 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
58981 * @cfg {String} title The title for the region (overrides panel titles)
58982 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
58983 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
58984 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
58985 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
58986 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
58987 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
58988 * the space available, similar to FireFox 1.5 tabs (defaults to false)
58989 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
58990 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
58991 * @cfg {Boolean} showPin True to show a pin button
58992 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
58993 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
58994 * @cfg {Boolean} disableTabTips True to disable tab tooltips
58995 * @cfg {Number} width For East/West panels
58996 * @cfg {Number} height For North/South panels
58997 * @cfg {Boolean} split To show the splitter
58998 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
59000 Roo.LayoutRegion = function(mgr, config, pos){
59001 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59002 var dh = Roo.DomHelper;
59003 /** This region's container element
59004 * @type Roo.Element */
59005 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59006 /** This region's title element
59007 * @type Roo.Element */
59009 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59010 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
59011 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59013 this.titleEl.enableDisplayMode();
59014 /** This region's title text element
59015 * @type HTMLElement */
59016 this.titleTextEl = this.titleEl.dom.firstChild;
59017 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59018 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59019 this.closeBtn.enableDisplayMode();
59020 this.closeBtn.on("click", this.closeClicked, this);
59021 this.closeBtn.hide();
59023 this.createBody(config);
59024 this.visible = true;
59025 this.collapsed = false;
59027 if(config.hideWhenEmpty){
59029 this.on("paneladded", this.validateVisibility, this);
59030 this.on("panelremoved", this.validateVisibility, this);
59032 this.applyConfig(config);
59035 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59037 createBody : function(){
59038 /** This region's body element
59039 * @type Roo.Element */
59040 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59043 applyConfig : function(c){
59044 if(c.collapsible && this.position != "center" && !this.collapsedEl){
59045 var dh = Roo.DomHelper;
59046 if(c.titlebar !== false){
59047 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59048 this.collapseBtn.on("click", this.collapse, this);
59049 this.collapseBtn.enableDisplayMode();
59051 if(c.showPin === true || this.showPin){
59052 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59053 this.stickBtn.enableDisplayMode();
59054 this.stickBtn.on("click", this.expand, this);
59055 this.stickBtn.hide();
59058 /** This region's collapsed element
59059 * @type Roo.Element */
59060 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59061 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59063 if(c.floatable !== false){
59064 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59065 this.collapsedEl.on("click", this.collapseClick, this);
59068 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59069 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59070 id: "message", unselectable: "on", style:{"float":"left"}});
59071 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59073 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59074 this.expandBtn.on("click", this.expand, this);
59076 if(this.collapseBtn){
59077 this.collapseBtn.setVisible(c.collapsible == true);
59079 this.cmargins = c.cmargins || this.cmargins ||
59080 (this.position == "west" || this.position == "east" ?
59081 {top: 0, left: 2, right:2, bottom: 0} :
59082 {top: 2, left: 0, right:0, bottom: 2});
59083 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59084 this.bottomTabs = c.tabPosition != "top";
59085 this.autoScroll = c.autoScroll || false;
59086 if(this.autoScroll){
59087 this.bodyEl.setStyle("overflow", "auto");
59089 this.bodyEl.setStyle("overflow", "hidden");
59091 //if(c.titlebar !== false){
59092 if((!c.titlebar && !c.title) || c.titlebar === false){
59093 this.titleEl.hide();
59095 this.titleEl.show();
59097 this.titleTextEl.innerHTML = c.title;
59101 this.duration = c.duration || .30;
59102 this.slideDuration = c.slideDuration || .45;
59105 this.collapse(true);
59112 * Returns true if this region is currently visible.
59113 * @return {Boolean}
59115 isVisible : function(){
59116 return this.visible;
59120 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59121 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
59123 setCollapsedTitle : function(title){
59124 title = title || " ";
59125 if(this.collapsedTitleTextEl){
59126 this.collapsedTitleTextEl.innerHTML = title;
59130 getBox : function(){
59132 if(!this.collapsed){
59133 b = this.el.getBox(false, true);
59135 b = this.collapsedEl.getBox(false, true);
59140 getMargins : function(){
59141 return this.collapsed ? this.cmargins : this.margins;
59144 highlight : function(){
59145 this.el.addClass("x-layout-panel-dragover");
59148 unhighlight : function(){
59149 this.el.removeClass("x-layout-panel-dragover");
59152 updateBox : function(box){
59154 if(!this.collapsed){
59155 this.el.dom.style.left = box.x + "px";
59156 this.el.dom.style.top = box.y + "px";
59157 this.updateBody(box.width, box.height);
59159 this.collapsedEl.dom.style.left = box.x + "px";
59160 this.collapsedEl.dom.style.top = box.y + "px";
59161 this.collapsedEl.setSize(box.width, box.height);
59164 this.tabs.autoSizeTabs();
59168 updateBody : function(w, h){
59170 this.el.setWidth(w);
59171 w -= this.el.getBorderWidth("rl");
59172 if(this.config.adjustments){
59173 w += this.config.adjustments[0];
59177 this.el.setHeight(h);
59178 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59179 h -= this.el.getBorderWidth("tb");
59180 if(this.config.adjustments){
59181 h += this.config.adjustments[1];
59183 this.bodyEl.setHeight(h);
59185 h = this.tabs.syncHeight(h);
59188 if(this.panelSize){
59189 w = w !== null ? w : this.panelSize.width;
59190 h = h !== null ? h : this.panelSize.height;
59192 if(this.activePanel){
59193 var el = this.activePanel.getEl();
59194 w = w !== null ? w : el.getWidth();
59195 h = h !== null ? h : el.getHeight();
59196 this.panelSize = {width: w, height: h};
59197 this.activePanel.setSize(w, h);
59199 if(Roo.isIE && this.tabs){
59200 this.tabs.el.repaint();
59205 * Returns the container element for this region.
59206 * @return {Roo.Element}
59208 getEl : function(){
59213 * Hides this region.
59216 if(!this.collapsed){
59217 this.el.dom.style.left = "-2000px";
59220 this.collapsedEl.dom.style.left = "-2000px";
59221 this.collapsedEl.hide();
59223 this.visible = false;
59224 this.fireEvent("visibilitychange", this, false);
59228 * Shows this region if it was previously hidden.
59231 if(!this.collapsed){
59234 this.collapsedEl.show();
59236 this.visible = true;
59237 this.fireEvent("visibilitychange", this, true);
59240 closeClicked : function(){
59241 if(this.activePanel){
59242 this.remove(this.activePanel);
59246 collapseClick : function(e){
59248 e.stopPropagation();
59251 e.stopPropagation();
59257 * Collapses this region.
59258 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59260 collapse : function(skipAnim, skipCheck){
59261 if(this.collapsed) {
59265 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59267 this.collapsed = true;
59269 this.split.el.hide();
59271 if(this.config.animate && skipAnim !== true){
59272 this.fireEvent("invalidated", this);
59273 this.animateCollapse();
59275 this.el.setLocation(-20000,-20000);
59277 this.collapsedEl.show();
59278 this.fireEvent("collapsed", this);
59279 this.fireEvent("invalidated", this);
59285 animateCollapse : function(){
59290 * Expands this region if it was previously collapsed.
59291 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59292 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59294 expand : function(e, skipAnim){
59296 e.stopPropagation();
59298 if(!this.collapsed || this.el.hasActiveFx()) {
59302 this.afterSlideIn();
59305 this.collapsed = false;
59306 if(this.config.animate && skipAnim !== true){
59307 this.animateExpand();
59311 this.split.el.show();
59313 this.collapsedEl.setLocation(-2000,-2000);
59314 this.collapsedEl.hide();
59315 this.fireEvent("invalidated", this);
59316 this.fireEvent("expanded", this);
59320 animateExpand : function(){
59324 initTabs : function()
59326 this.bodyEl.setStyle("overflow", "hidden");
59327 var ts = new Roo.TabPanel(
59330 tabPosition: this.bottomTabs ? 'bottom' : 'top',
59331 disableTooltips: this.config.disableTabTips,
59332 toolbar : this.config.toolbar
59335 if(this.config.hideTabs){
59336 ts.stripWrap.setDisplayed(false);
59339 ts.resizeTabs = this.config.resizeTabs === true;
59340 ts.minTabWidth = this.config.minTabWidth || 40;
59341 ts.maxTabWidth = this.config.maxTabWidth || 250;
59342 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59343 ts.monitorResize = false;
59344 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59345 ts.bodyEl.addClass('x-layout-tabs-body');
59346 this.panels.each(this.initPanelAsTab, this);
59349 initPanelAsTab : function(panel){
59350 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59351 this.config.closeOnTab && panel.isClosable());
59352 if(panel.tabTip !== undefined){
59353 ti.setTooltip(panel.tabTip);
59355 ti.on("activate", function(){
59356 this.setActivePanel(panel);
59358 if(this.config.closeOnTab){
59359 ti.on("beforeclose", function(t, e){
59361 this.remove(panel);
59367 updatePanelTitle : function(panel, title){
59368 if(this.activePanel == panel){
59369 this.updateTitle(title);
59372 var ti = this.tabs.getTab(panel.getEl().id);
59374 if(panel.tabTip !== undefined){
59375 ti.setTooltip(panel.tabTip);
59380 updateTitle : function(title){
59381 if(this.titleTextEl && !this.config.title){
59382 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
59386 setActivePanel : function(panel){
59387 panel = this.getPanel(panel);
59388 if(this.activePanel && this.activePanel != panel){
59389 this.activePanel.setActiveState(false);
59391 this.activePanel = panel;
59392 panel.setActiveState(true);
59393 if(this.panelSize){
59394 panel.setSize(this.panelSize.width, this.panelSize.height);
59397 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59399 this.updateTitle(panel.getTitle());
59401 this.fireEvent("invalidated", this);
59403 this.fireEvent("panelactivated", this, panel);
59407 * Shows the specified panel.
59408 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59409 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59411 showPanel : function(panel)
59413 panel = this.getPanel(panel);
59416 var tab = this.tabs.getTab(panel.getEl().id);
59417 if(tab.isHidden()){
59418 this.tabs.unhideTab(tab.id);
59422 this.setActivePanel(panel);
59429 * Get the active panel for this region.
59430 * @return {Roo.ContentPanel} The active panel or null
59432 getActivePanel : function(){
59433 return this.activePanel;
59436 validateVisibility : function(){
59437 if(this.panels.getCount() < 1){
59438 this.updateTitle(" ");
59439 this.closeBtn.hide();
59442 if(!this.isVisible()){
59449 * Adds the passed ContentPanel(s) to this region.
59450 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59451 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59453 add : function(panel){
59454 if(arguments.length > 1){
59455 for(var i = 0, len = arguments.length; i < len; i++) {
59456 this.add(arguments[i]);
59460 if(this.hasPanel(panel)){
59461 this.showPanel(panel);
59464 panel.setRegion(this);
59465 this.panels.add(panel);
59466 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59467 this.bodyEl.dom.appendChild(panel.getEl().dom);
59468 if(panel.background !== true){
59469 this.setActivePanel(panel);
59471 this.fireEvent("paneladded", this, panel);
59477 this.initPanelAsTab(panel);
59479 if(panel.background !== true){
59480 this.tabs.activate(panel.getEl().id);
59482 this.fireEvent("paneladded", this, panel);
59487 * Hides the tab for the specified panel.
59488 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59490 hidePanel : function(panel){
59491 if(this.tabs && (panel = this.getPanel(panel))){
59492 this.tabs.hideTab(panel.getEl().id);
59497 * Unhides the tab for a previously hidden panel.
59498 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59500 unhidePanel : function(panel){
59501 if(this.tabs && (panel = this.getPanel(panel))){
59502 this.tabs.unhideTab(panel.getEl().id);
59506 clearPanels : function(){
59507 while(this.panels.getCount() > 0){
59508 this.remove(this.panels.first());
59513 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59514 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59515 * @param {Boolean} preservePanel Overrides the config preservePanel option
59516 * @return {Roo.ContentPanel} The panel that was removed
59518 remove : function(panel, preservePanel){
59519 panel = this.getPanel(panel);
59524 this.fireEvent("beforeremove", this, panel, e);
59525 if(e.cancel === true){
59528 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59529 var panelId = panel.getId();
59530 this.panels.removeKey(panelId);
59532 document.body.appendChild(panel.getEl().dom);
59535 this.tabs.removeTab(panel.getEl().id);
59536 }else if (!preservePanel){
59537 this.bodyEl.dom.removeChild(panel.getEl().dom);
59539 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59540 var p = this.panels.first();
59541 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59542 tempEl.appendChild(p.getEl().dom);
59543 this.bodyEl.update("");
59544 this.bodyEl.dom.appendChild(p.getEl().dom);
59546 this.updateTitle(p.getTitle());
59548 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59549 this.setActivePanel(p);
59551 panel.setRegion(null);
59552 if(this.activePanel == panel){
59553 this.activePanel = null;
59555 if(this.config.autoDestroy !== false && preservePanel !== true){
59556 try{panel.destroy();}catch(e){}
59558 this.fireEvent("panelremoved", this, panel);
59563 * Returns the TabPanel component used by this region
59564 * @return {Roo.TabPanel}
59566 getTabs : function(){
59570 createTool : function(parentEl, className){
59571 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
59572 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
59573 btn.addClassOnOver("x-layout-tools-button-over");
59578 * Ext JS Library 1.1.1
59579 * Copyright(c) 2006-2007, Ext JS, LLC.
59581 * Originally Released Under LGPL - original licence link has changed is not relivant.
59584 * <script type="text/javascript">
59590 * @class Roo.SplitLayoutRegion
59591 * @extends Roo.LayoutRegion
59592 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
59594 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
59595 this.cursor = cursor;
59596 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
59599 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
59600 splitTip : "Drag to resize.",
59601 collapsibleSplitTip : "Drag to resize. Double click to hide.",
59602 useSplitTips : false,
59604 applyConfig : function(config){
59605 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
59608 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
59609 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
59610 /** The SplitBar for this region
59611 * @type Roo.SplitBar */
59612 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
59613 this.split.on("moved", this.onSplitMove, this);
59614 this.split.useShim = config.useShim === true;
59615 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
59616 if(this.useSplitTips){
59617 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
59619 if(config.collapsible){
59620 this.split.el.on("dblclick", this.collapse, this);
59623 if(typeof config.minSize != "undefined"){
59624 this.split.minSize = config.minSize;
59626 if(typeof config.maxSize != "undefined"){
59627 this.split.maxSize = config.maxSize;
59629 if(config.hideWhenEmpty || config.hidden || config.collapsed){
59630 this.hideSplitter();
59635 getHMaxSize : function(){
59636 var cmax = this.config.maxSize || 10000;
59637 var center = this.mgr.getRegion("center");
59638 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
59641 getVMaxSize : function(){
59642 var cmax = this.config.maxSize || 10000;
59643 var center = this.mgr.getRegion("center");
59644 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
59647 onSplitMove : function(split, newSize){
59648 this.fireEvent("resized", this, newSize);
59652 * Returns the {@link Roo.SplitBar} for this region.
59653 * @return {Roo.SplitBar}
59655 getSplitBar : function(){
59660 this.hideSplitter();
59661 Roo.SplitLayoutRegion.superclass.hide.call(this);
59664 hideSplitter : function(){
59666 this.split.el.setLocation(-2000,-2000);
59667 this.split.el.hide();
59673 this.split.el.show();
59675 Roo.SplitLayoutRegion.superclass.show.call(this);
59678 beforeSlide: function(){
59679 if(Roo.isGecko){// firefox overflow auto bug workaround
59680 this.bodyEl.clip();
59682 this.tabs.bodyEl.clip();
59684 if(this.activePanel){
59685 this.activePanel.getEl().clip();
59687 if(this.activePanel.beforeSlide){
59688 this.activePanel.beforeSlide();
59694 afterSlide : function(){
59695 if(Roo.isGecko){// firefox overflow auto bug workaround
59696 this.bodyEl.unclip();
59698 this.tabs.bodyEl.unclip();
59700 if(this.activePanel){
59701 this.activePanel.getEl().unclip();
59702 if(this.activePanel.afterSlide){
59703 this.activePanel.afterSlide();
59709 initAutoHide : function(){
59710 if(this.autoHide !== false){
59711 if(!this.autoHideHd){
59712 var st = new Roo.util.DelayedTask(this.slideIn, this);
59713 this.autoHideHd = {
59714 "mouseout": function(e){
59715 if(!e.within(this.el, true)){
59719 "mouseover" : function(e){
59725 this.el.on(this.autoHideHd);
59729 clearAutoHide : function(){
59730 if(this.autoHide !== false){
59731 this.el.un("mouseout", this.autoHideHd.mouseout);
59732 this.el.un("mouseover", this.autoHideHd.mouseover);
59736 clearMonitor : function(){
59737 Roo.get(document).un("click", this.slideInIf, this);
59740 // these names are backwards but not changed for compat
59741 slideOut : function(){
59742 if(this.isSlid || this.el.hasActiveFx()){
59745 this.isSlid = true;
59746 if(this.collapseBtn){
59747 this.collapseBtn.hide();
59749 this.closeBtnState = this.closeBtn.getStyle('display');
59750 this.closeBtn.hide();
59752 this.stickBtn.show();
59755 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
59756 this.beforeSlide();
59757 this.el.setStyle("z-index", 10001);
59758 this.el.slideIn(this.getSlideAnchor(), {
59759 callback: function(){
59761 this.initAutoHide();
59762 Roo.get(document).on("click", this.slideInIf, this);
59763 this.fireEvent("slideshow", this);
59770 afterSlideIn : function(){
59771 this.clearAutoHide();
59772 this.isSlid = false;
59773 this.clearMonitor();
59774 this.el.setStyle("z-index", "");
59775 if(this.collapseBtn){
59776 this.collapseBtn.show();
59778 this.closeBtn.setStyle('display', this.closeBtnState);
59780 this.stickBtn.hide();
59782 this.fireEvent("slidehide", this);
59785 slideIn : function(cb){
59786 if(!this.isSlid || this.el.hasActiveFx()){
59790 this.isSlid = false;
59791 this.beforeSlide();
59792 this.el.slideOut(this.getSlideAnchor(), {
59793 callback: function(){
59794 this.el.setLeftTop(-10000, -10000);
59796 this.afterSlideIn();
59804 slideInIf : function(e){
59805 if(!e.within(this.el)){
59810 animateCollapse : function(){
59811 this.beforeSlide();
59812 this.el.setStyle("z-index", 20000);
59813 var anchor = this.getSlideAnchor();
59814 this.el.slideOut(anchor, {
59815 callback : function(){
59816 this.el.setStyle("z-index", "");
59817 this.collapsedEl.slideIn(anchor, {duration:.3});
59819 this.el.setLocation(-10000,-10000);
59821 this.fireEvent("collapsed", this);
59828 animateExpand : function(){
59829 this.beforeSlide();
59830 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
59831 this.el.setStyle("z-index", 20000);
59832 this.collapsedEl.hide({
59835 this.el.slideIn(this.getSlideAnchor(), {
59836 callback : function(){
59837 this.el.setStyle("z-index", "");
59840 this.split.el.show();
59842 this.fireEvent("invalidated", this);
59843 this.fireEvent("expanded", this);
59871 getAnchor : function(){
59872 return this.anchors[this.position];
59875 getCollapseAnchor : function(){
59876 return this.canchors[this.position];
59879 getSlideAnchor : function(){
59880 return this.sanchors[this.position];
59883 getAlignAdj : function(){
59884 var cm = this.cmargins;
59885 switch(this.position){
59901 getExpandAdj : function(){
59902 var c = this.collapsedEl, cm = this.cmargins;
59903 switch(this.position){
59905 return [-(cm.right+c.getWidth()+cm.left), 0];
59908 return [cm.right+c.getWidth()+cm.left, 0];
59911 return [0, -(cm.top+cm.bottom+c.getHeight())];
59914 return [0, cm.top+cm.bottom+c.getHeight()];
59920 * Ext JS Library 1.1.1
59921 * Copyright(c) 2006-2007, Ext JS, LLC.
59923 * Originally Released Under LGPL - original licence link has changed is not relivant.
59926 * <script type="text/javascript">
59929 * These classes are private internal classes
59931 Roo.CenterLayoutRegion = function(mgr, config){
59932 Roo.LayoutRegion.call(this, mgr, config, "center");
59933 this.visible = true;
59934 this.minWidth = config.minWidth || 20;
59935 this.minHeight = config.minHeight || 20;
59938 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
59940 // center panel can't be hidden
59944 // center panel can't be hidden
59947 getMinWidth: function(){
59948 return this.minWidth;
59951 getMinHeight: function(){
59952 return this.minHeight;
59957 Roo.NorthLayoutRegion = function(mgr, config){
59958 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
59960 this.split.placement = Roo.SplitBar.TOP;
59961 this.split.orientation = Roo.SplitBar.VERTICAL;
59962 this.split.el.addClass("x-layout-split-v");
59964 var size = config.initialSize || config.height;
59965 if(typeof size != "undefined"){
59966 this.el.setHeight(size);
59969 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
59970 orientation: Roo.SplitBar.VERTICAL,
59971 getBox : function(){
59972 if(this.collapsed){
59973 return this.collapsedEl.getBox();
59975 var box = this.el.getBox();
59977 box.height += this.split.el.getHeight();
59982 updateBox : function(box){
59983 if(this.split && !this.collapsed){
59984 box.height -= this.split.el.getHeight();
59985 this.split.el.setLeft(box.x);
59986 this.split.el.setTop(box.y+box.height);
59987 this.split.el.setWidth(box.width);
59989 if(this.collapsed){
59990 this.updateBody(box.width, null);
59992 Roo.LayoutRegion.prototype.updateBox.call(this, box);
59996 Roo.SouthLayoutRegion = function(mgr, config){
59997 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
59999 this.split.placement = Roo.SplitBar.BOTTOM;
60000 this.split.orientation = Roo.SplitBar.VERTICAL;
60001 this.split.el.addClass("x-layout-split-v");
60003 var size = config.initialSize || config.height;
60004 if(typeof size != "undefined"){
60005 this.el.setHeight(size);
60008 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60009 orientation: Roo.SplitBar.VERTICAL,
60010 getBox : function(){
60011 if(this.collapsed){
60012 return this.collapsedEl.getBox();
60014 var box = this.el.getBox();
60016 var sh = this.split.el.getHeight();
60023 updateBox : function(box){
60024 if(this.split && !this.collapsed){
60025 var sh = this.split.el.getHeight();
60028 this.split.el.setLeft(box.x);
60029 this.split.el.setTop(box.y-sh);
60030 this.split.el.setWidth(box.width);
60032 if(this.collapsed){
60033 this.updateBody(box.width, null);
60035 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60039 Roo.EastLayoutRegion = function(mgr, config){
60040 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60042 this.split.placement = Roo.SplitBar.RIGHT;
60043 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60044 this.split.el.addClass("x-layout-split-h");
60046 var size = config.initialSize || config.width;
60047 if(typeof size != "undefined"){
60048 this.el.setWidth(size);
60051 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60052 orientation: Roo.SplitBar.HORIZONTAL,
60053 getBox : function(){
60054 if(this.collapsed){
60055 return this.collapsedEl.getBox();
60057 var box = this.el.getBox();
60059 var sw = this.split.el.getWidth();
60066 updateBox : function(box){
60067 if(this.split && !this.collapsed){
60068 var sw = this.split.el.getWidth();
60070 this.split.el.setLeft(box.x);
60071 this.split.el.setTop(box.y);
60072 this.split.el.setHeight(box.height);
60075 if(this.collapsed){
60076 this.updateBody(null, box.height);
60078 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60082 Roo.WestLayoutRegion = function(mgr, config){
60083 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60085 this.split.placement = Roo.SplitBar.LEFT;
60086 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60087 this.split.el.addClass("x-layout-split-h");
60089 var size = config.initialSize || config.width;
60090 if(typeof size != "undefined"){
60091 this.el.setWidth(size);
60094 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60095 orientation: Roo.SplitBar.HORIZONTAL,
60096 getBox : function(){
60097 if(this.collapsed){
60098 return this.collapsedEl.getBox();
60100 var box = this.el.getBox();
60102 box.width += this.split.el.getWidth();
60107 updateBox : function(box){
60108 if(this.split && !this.collapsed){
60109 var sw = this.split.el.getWidth();
60111 this.split.el.setLeft(box.x+box.width);
60112 this.split.el.setTop(box.y);
60113 this.split.el.setHeight(box.height);
60115 if(this.collapsed){
60116 this.updateBody(null, box.height);
60118 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60123 * Ext JS Library 1.1.1
60124 * Copyright(c) 2006-2007, Ext JS, LLC.
60126 * Originally Released Under LGPL - original licence link has changed is not relivant.
60129 * <script type="text/javascript">
60134 * Private internal class for reading and applying state
60136 Roo.LayoutStateManager = function(layout){
60137 // default empty state
60146 Roo.LayoutStateManager.prototype = {
60147 init : function(layout, provider){
60148 this.provider = provider;
60149 var state = provider.get(layout.id+"-layout-state");
60151 var wasUpdating = layout.isUpdating();
60153 layout.beginUpdate();
60155 for(var key in state){
60156 if(typeof state[key] != "function"){
60157 var rstate = state[key];
60158 var r = layout.getRegion(key);
60161 r.resizeTo(rstate.size);
60163 if(rstate.collapsed == true){
60166 r.expand(null, true);
60172 layout.endUpdate();
60174 this.state = state;
60176 this.layout = layout;
60177 layout.on("regionresized", this.onRegionResized, this);
60178 layout.on("regioncollapsed", this.onRegionCollapsed, this);
60179 layout.on("regionexpanded", this.onRegionExpanded, this);
60182 storeState : function(){
60183 this.provider.set(this.layout.id+"-layout-state", this.state);
60186 onRegionResized : function(region, newSize){
60187 this.state[region.getPosition()].size = newSize;
60191 onRegionCollapsed : function(region){
60192 this.state[region.getPosition()].collapsed = true;
60196 onRegionExpanded : function(region){
60197 this.state[region.getPosition()].collapsed = false;
60202 * Ext JS Library 1.1.1
60203 * Copyright(c) 2006-2007, Ext JS, LLC.
60205 * Originally Released Under LGPL - original licence link has changed is not relivant.
60208 * <script type="text/javascript">
60211 * @class Roo.ContentPanel
60212 * @extends Roo.util.Observable
60213 * @children Roo.form.Form Roo.JsonView Roo.View
60214 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60215 * A basic ContentPanel element.
60216 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
60217 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
60218 * @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
60219 * @cfg {Boolean} closable True if the panel can be closed/removed
60220 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
60221 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60222 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
60223 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
60224 * @cfg {String} title The title for this panel
60225 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60226 * @cfg {String} url Calls {@link #setUrl} with this value
60227 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60228 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
60229 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
60230 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
60231 * @cfg {String} style Extra style to add to the content panel
60232 * @cfg {Roo.menu.Menu} menu popup menu
60235 * Create a new ContentPanel.
60236 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60237 * @param {String/Object} config A string to set only the title or a config object
60238 * @param {String} content (optional) Set the HTML content for this panel
60239 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60241 Roo.ContentPanel = function(el, config, content){
60245 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60249 if (config && config.parentLayout) {
60250 el = config.parentLayout.el.createChild();
60253 if(el.autoCreate){ // xtype is available if this is called from factory
60257 this.el = Roo.get(el);
60258 if(!this.el && config && config.autoCreate){
60259 if(typeof config.autoCreate == "object"){
60260 if(!config.autoCreate.id){
60261 config.autoCreate.id = config.id||el;
60263 this.el = Roo.DomHelper.append(document.body,
60264 config.autoCreate, true);
60266 this.el = Roo.DomHelper.append(document.body,
60267 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60272 this.closable = false;
60273 this.loaded = false;
60274 this.active = false;
60275 if(typeof config == "string"){
60276 this.title = config;
60278 Roo.apply(this, config);
60281 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60282 this.wrapEl = this.el.wrap();
60283 this.toolbar.container = this.el.insertSibling(false, 'before');
60284 this.toolbar = new Roo.Toolbar(this.toolbar);
60287 // xtype created footer. - not sure if will work as we normally have to render first..
60288 if (this.footer && !this.footer.el && this.footer.xtype) {
60289 if (!this.wrapEl) {
60290 this.wrapEl = this.el.wrap();
60293 this.footer.container = this.wrapEl.createChild();
60295 this.footer = Roo.factory(this.footer, Roo);
60300 this.resizeEl = Roo.get(this.resizeEl, true);
60302 this.resizeEl = this.el;
60304 // handle view.xtype
60312 * Fires when this panel is activated.
60313 * @param {Roo.ContentPanel} this
60317 * @event deactivate
60318 * Fires when this panel is activated.
60319 * @param {Roo.ContentPanel} this
60321 "deactivate" : true,
60325 * Fires when this panel is resized if fitToFrame is true.
60326 * @param {Roo.ContentPanel} this
60327 * @param {Number} width The width after any component adjustments
60328 * @param {Number} height The height after any component adjustments
60334 * Fires when this tab is created
60335 * @param {Roo.ContentPanel} this
60345 if(this.autoScroll){
60346 this.resizeEl.setStyle("overflow", "auto");
60348 // fix randome scrolling
60349 this.el.on('scroll', function() {
60350 Roo.log('fix random scolling');
60351 this.scrollTo('top',0);
60354 content = content || this.content;
60356 this.setContent(content);
60358 if(config && config.url){
60359 this.setUrl(this.url, this.params, this.loadOnce);
60364 Roo.ContentPanel.superclass.constructor.call(this);
60366 if (this.view && typeof(this.view.xtype) != 'undefined') {
60367 this.view.el = this.el.appendChild(document.createElement("div"));
60368 this.view = Roo.factory(this.view);
60369 this.view.render && this.view.render(false, '');
60373 this.fireEvent('render', this);
60376 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60378 setRegion : function(region){
60379 this.region = region;
60381 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60383 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60388 * Returns the toolbar for this Panel if one was configured.
60389 * @return {Roo.Toolbar}
60391 getToolbar : function(){
60392 return this.toolbar;
60395 setActiveState : function(active){
60396 this.active = active;
60398 this.fireEvent("deactivate", this);
60400 this.fireEvent("activate", this);
60404 * Updates this panel's element
60405 * @param {String} content The new content
60406 * @param {Boolean} loadScripts (optional) true to look for and process scripts
60408 setContent : function(content, loadScripts){
60409 this.el.update(content, loadScripts);
60412 ignoreResize : function(w, h){
60413 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60416 this.lastSize = {width: w, height: h};
60421 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60422 * @return {Roo.UpdateManager} The UpdateManager
60424 getUpdateManager : function(){
60425 return this.el.getUpdateManager();
60428 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60429 * @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:
60432 url: "your-url.php",
60433 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60434 callback: yourFunction,
60435 scope: yourObject, //(optional scope)
60438 text: "Loading...",
60443 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60444 * 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.
60445 * @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}
60446 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60447 * @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.
60448 * @return {Roo.ContentPanel} this
60451 var um = this.el.getUpdateManager();
60452 um.update.apply(um, arguments);
60458 * 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.
60459 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60460 * @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)
60461 * @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)
60462 * @return {Roo.UpdateManager} The UpdateManager
60464 setUrl : function(url, params, loadOnce){
60465 if(this.refreshDelegate){
60466 this.removeListener("activate", this.refreshDelegate);
60468 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60469 this.on("activate", this.refreshDelegate);
60470 return this.el.getUpdateManager();
60473 _handleRefresh : function(url, params, loadOnce){
60474 if(!loadOnce || !this.loaded){
60475 var updater = this.el.getUpdateManager();
60476 updater.update(url, params, this._setLoaded.createDelegate(this));
60480 _setLoaded : function(){
60481 this.loaded = true;
60485 * Returns this panel's id
60488 getId : function(){
60493 * Returns this panel's element - used by regiosn to add.
60494 * @return {Roo.Element}
60496 getEl : function(){
60497 return this.wrapEl || this.el;
60500 adjustForComponents : function(width, height)
60502 //Roo.log('adjustForComponents ');
60503 if(this.resizeEl != this.el){
60504 width -= this.el.getFrameWidth('lr');
60505 height -= this.el.getFrameWidth('tb');
60508 var te = this.toolbar.getEl();
60509 height -= te.getHeight();
60510 te.setWidth(width);
60513 var te = this.footer.getEl();
60514 //Roo.log("footer:" + te.getHeight());
60516 height -= te.getHeight();
60517 te.setWidth(width);
60521 if(this.adjustments){
60522 width += this.adjustments[0];
60523 height += this.adjustments[1];
60525 return {"width": width, "height": height};
60528 setSize : function(width, height){
60529 if(this.fitToFrame && !this.ignoreResize(width, height)){
60530 if(this.fitContainer && this.resizeEl != this.el){
60531 this.el.setSize(width, height);
60533 var size = this.adjustForComponents(width, height);
60534 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60535 this.fireEvent('resize', this, size.width, size.height);
60540 * Returns this panel's title
60543 getTitle : function(){
60548 * Set this panel's title
60549 * @param {String} title
60551 setTitle : function(title){
60552 this.title = title;
60554 this.region.updatePanelTitle(this, title);
60559 * Returns true is this panel was configured to be closable
60560 * @return {Boolean}
60562 isClosable : function(){
60563 return this.closable;
60566 beforeSlide : function(){
60568 this.resizeEl.clip();
60571 afterSlide : function(){
60573 this.resizeEl.unclip();
60577 * Force a content refresh from the URL specified in the {@link #setUrl} method.
60578 * Will fail silently if the {@link #setUrl} method has not been called.
60579 * This does not activate the panel, just updates its content.
60581 refresh : function(){
60582 if(this.refreshDelegate){
60583 this.loaded = false;
60584 this.refreshDelegate();
60589 * Destroys this panel
60591 destroy : function(){
60592 this.el.removeAllListeners();
60593 var tempEl = document.createElement("span");
60594 tempEl.appendChild(this.el.dom);
60595 tempEl.innerHTML = "";
60601 * form - if the content panel contains a form - this is a reference to it.
60602 * @type {Roo.form.Form}
60606 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
60607 * This contains a reference to it.
60613 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
60623 * @param {Object} cfg Xtype definition of item to add.
60626 addxtype : function(cfg) {
60628 if (cfg.xtype.match(/^Form$/)) {
60631 //if (this.footer) {
60632 // el = this.footer.container.insertSibling(false, 'before');
60634 el = this.el.createChild();
60637 this.form = new Roo.form.Form(cfg);
60640 if ( this.form.allItems.length) {
60641 this.form.render(el.dom);
60645 // should only have one of theses..
60646 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
60647 // views.. should not be just added - used named prop 'view''
60649 cfg.el = this.el.appendChild(document.createElement("div"));
60652 var ret = new Roo.factory(cfg);
60654 ret.render && ret.render(false, ''); // render blank..
60674 * @class Roo.GridPanel
60675 * @extends Roo.ContentPanel
60676 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60678 * Create a new GridPanel.
60679 * @cfg {Roo.grid.Grid} grid The grid for this panel
60681 Roo.GridPanel = function(grid, config){
60683 // universal ctor...
60684 if (typeof(grid.grid) != 'undefined') {
60686 grid = config.grid;
60688 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
60689 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
60691 this.wrapper.dom.appendChild(grid.getGridEl().dom);
60693 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
60696 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
60698 // xtype created footer. - not sure if will work as we normally have to render first..
60699 if (this.footer && !this.footer.el && this.footer.xtype) {
60701 this.footer.container = this.grid.getView().getFooterPanel(true);
60702 this.footer.dataSource = this.grid.dataSource;
60703 this.footer = Roo.factory(this.footer, Roo);
60707 grid.monitorWindowResize = false; // turn off autosizing
60708 grid.autoHeight = false;
60709 grid.autoWidth = false;
60711 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
60714 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
60715 getId : function(){
60716 return this.grid.id;
60720 * Returns the grid for this panel
60721 * @return {Roo.grid.Grid}
60723 getGrid : function(){
60727 setSize : function(width, height){
60728 if(!this.ignoreResize(width, height)){
60729 var grid = this.grid;
60730 var size = this.adjustForComponents(width, height);
60731 grid.getGridEl().setSize(size.width, size.height);
60736 beforeSlide : function(){
60737 this.grid.getView().scroller.clip();
60740 afterSlide : function(){
60741 this.grid.getView().scroller.unclip();
60744 destroy : function(){
60745 this.grid.destroy();
60747 Roo.GridPanel.superclass.destroy.call(this);
60753 * @class Roo.NestedLayoutPanel
60754 * @extends Roo.ContentPanel
60755 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60756 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
60760 * Create a new NestedLayoutPanel.
60763 * @param {Roo.BorderLayout} layout [required] The layout for this panel
60764 * @param {String/Object} config A string to set only the title or a config object
60766 Roo.NestedLayoutPanel = function(layout, config)
60768 // construct with only one argument..
60769 /* FIXME - implement nicer consturctors
60770 if (layout.layout) {
60772 layout = config.layout;
60773 delete config.layout;
60775 if (layout.xtype && !layout.getEl) {
60776 // then layout needs constructing..
60777 layout = Roo.factory(layout, Roo);
60782 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
60784 layout.monitorWindowResize = false; // turn off autosizing
60785 this.layout = layout;
60786 this.layout.getEl().addClass("x-layout-nested-layout");
60793 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
60797 setSize : function(width, height){
60798 if(!this.ignoreResize(width, height)){
60799 var size = this.adjustForComponents(width, height);
60800 var el = this.layout.getEl();
60801 el.setSize(size.width, size.height);
60802 var touch = el.dom.offsetWidth;
60803 this.layout.layout();
60804 // ie requires a double layout on the first pass
60805 if(Roo.isIE && !this.initialized){
60806 this.initialized = true;
60807 this.layout.layout();
60812 // activate all subpanels if not currently active..
60814 setActiveState : function(active){
60815 this.active = active;
60817 this.fireEvent("deactivate", this);
60821 this.fireEvent("activate", this);
60822 // not sure if this should happen before or after..
60823 if (!this.layout) {
60824 return; // should not happen..
60827 for (var r in this.layout.regions) {
60828 reg = this.layout.getRegion(r);
60829 if (reg.getActivePanel()) {
60830 //reg.showPanel(reg.getActivePanel()); // force it to activate..
60831 reg.setActivePanel(reg.getActivePanel());
60834 if (!reg.panels.length) {
60837 reg.showPanel(reg.getPanel(0));
60846 * Returns the nested BorderLayout for this panel
60847 * @return {Roo.BorderLayout}
60849 getLayout : function(){
60850 return this.layout;
60854 * Adds a xtype elements to the layout of the nested panel
60858 xtype : 'ContentPanel',
60865 xtype : 'NestedLayoutPanel',
60871 items : [ ... list of content panels or nested layout panels.. ]
60875 * @param {Object} cfg Xtype definition of item to add.
60877 addxtype : function(cfg) {
60878 return this.layout.addxtype(cfg);
60883 Roo.ScrollPanel = function(el, config, content){
60884 config = config || {};
60885 config.fitToFrame = true;
60886 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
60888 this.el.dom.style.overflow = "hidden";
60889 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
60890 this.el.removeClass("x-layout-inactive-content");
60891 this.el.on("mousewheel", this.onWheel, this);
60893 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
60894 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
60895 up.unselectable(); down.unselectable();
60896 up.on("click", this.scrollUp, this);
60897 down.on("click", this.scrollDown, this);
60898 up.addClassOnOver("x-scroller-btn-over");
60899 down.addClassOnOver("x-scroller-btn-over");
60900 up.addClassOnClick("x-scroller-btn-click");
60901 down.addClassOnClick("x-scroller-btn-click");
60902 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
60904 this.resizeEl = this.el;
60905 this.el = wrap; this.up = up; this.down = down;
60908 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
60910 wheelIncrement : 5,
60911 scrollUp : function(){
60912 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
60915 scrollDown : function(){
60916 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
60919 afterScroll : function(){
60920 var el = this.resizeEl;
60921 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
60922 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
60923 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
60926 setSize : function(){
60927 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
60928 this.afterScroll();
60931 onWheel : function(e){
60932 var d = e.getWheelDelta();
60933 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
60934 this.afterScroll();
60938 setContent : function(content, loadScripts){
60939 this.resizeEl.update(content, loadScripts);
60947 * @class Roo.TreePanel
60948 * @extends Roo.ContentPanel
60949 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60950 * Treepanel component
60953 * Create a new TreePanel. - defaults to fit/scoll contents.
60954 * @param {String/Object} config A string to set only the panel's title, or a config object
60956 Roo.TreePanel = function(config){
60957 var el = config.el;
60958 var tree = config.tree;
60959 delete config.tree;
60960 delete config.el; // hopefull!
60962 // wrapper for IE7 strict & safari scroll issue
60964 var treeEl = el.createChild();
60965 config.resizeEl = treeEl;
60969 Roo.TreePanel.superclass.constructor.call(this, el, config);
60972 this.tree = new Roo.tree.TreePanel(treeEl , tree);
60973 //console.log(tree);
60974 this.on('activate', function()
60976 if (this.tree.rendered) {
60979 //console.log('render tree');
60980 this.tree.render();
60982 // this should not be needed.. - it's actually the 'el' that resizes?
60983 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
60985 //this.on('resize', function (cp, w, h) {
60986 // this.tree.innerCt.setWidth(w);
60987 // this.tree.innerCt.setHeight(h);
60988 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
60995 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
60999 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61006 * Ext JS Library 1.1.1
61007 * Copyright(c) 2006-2007, Ext JS, LLC.
61009 * Originally Released Under LGPL - original licence link has changed is not relivant.
61012 * <script type="text/javascript">
61017 * @class Roo.ReaderLayout
61018 * @extends Roo.BorderLayout
61019 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
61020 * center region containing two nested regions (a top one for a list view and one for item preview below),
61021 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61022 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61023 * expedites the setup of the overall layout and regions for this common application style.
61026 var reader = new Roo.ReaderLayout();
61027 var CP = Roo.ContentPanel; // shortcut for adding
61029 reader.beginUpdate();
61030 reader.add("north", new CP("north", "North"));
61031 reader.add("west", new CP("west", {title: "West"}));
61032 reader.add("east", new CP("east", {title: "East"}));
61034 reader.regions.listView.add(new CP("listView", "List"));
61035 reader.regions.preview.add(new CP("preview", "Preview"));
61036 reader.endUpdate();
61039 * Create a new ReaderLayout
61040 * @param {Object} config Configuration options
61041 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61042 * document.body if omitted)
61044 Roo.ReaderLayout = function(config, renderTo){
61045 var c = config || {size:{}};
61046 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61047 north: c.north !== false ? Roo.apply({
61051 }, c.north) : false,
61052 west: c.west !== false ? Roo.apply({
61060 margins:{left:5,right:0,bottom:5,top:5},
61061 cmargins:{left:5,right:5,bottom:5,top:5}
61062 }, c.west) : false,
61063 east: c.east !== false ? Roo.apply({
61071 margins:{left:0,right:5,bottom:5,top:5},
61072 cmargins:{left:5,right:5,bottom:5,top:5}
61073 }, c.east) : false,
61074 center: Roo.apply({
61075 tabPosition: 'top',
61079 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61083 this.el.addClass('x-reader');
61085 this.beginUpdate();
61087 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61088 south: c.preview !== false ? Roo.apply({
61095 cmargins:{top:5,left:0, right:0, bottom:0}
61096 }, c.preview) : false,
61097 center: Roo.apply({
61103 this.add('center', new Roo.NestedLayoutPanel(inner,
61104 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61108 this.regions.preview = inner.getRegion('south');
61109 this.regions.listView = inner.getRegion('center');
61112 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61114 * Ext JS Library 1.1.1
61115 * Copyright(c) 2006-2007, Ext JS, LLC.
61117 * Originally Released Under LGPL - original licence link has changed is not relivant.
61120 * <script type="text/javascript">
61124 * @class Roo.grid.Grid
61125 * @extends Roo.util.Observable
61126 * This class represents the primary interface of a component based grid control.
61127 * <br><br>Usage:<pre><code>
61128 var grid = new Roo.grid.Grid("my-container-id", {
61131 selModel: mySelectionModel,
61132 autoSizeColumns: true,
61133 monitorWindowResize: false,
61134 trackMouseOver: true
61139 * <b>Common Problems:</b><br/>
61140 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61141 * element will correct this<br/>
61142 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61143 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61144 * are unpredictable.<br/>
61145 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61146 * grid to calculate dimensions/offsets.<br/>
61148 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61149 * The container MUST have some type of size defined for the grid to fill. The container will be
61150 * automatically set to position relative if it isn't already.
61151 * @param {Object} config A config object that sets properties on this grid.
61153 Roo.grid.Grid = function(container, config){
61154 // initialize the container
61155 this.container = Roo.get(container);
61156 this.container.update("");
61157 this.container.setStyle("overflow", "hidden");
61158 this.container.addClass('x-grid-container');
61160 this.id = this.container.id;
61162 Roo.apply(this, config);
61163 // check and correct shorthanded configs
61165 this.dataSource = this.ds;
61169 this.colModel = this.cm;
61173 this.selModel = this.sm;
61177 if (this.selModel) {
61178 this.selModel = Roo.factory(this.selModel, Roo.grid);
61179 this.sm = this.selModel;
61180 this.sm.xmodule = this.xmodule || false;
61182 if (typeof(this.colModel.config) == 'undefined') {
61183 this.colModel = new Roo.grid.ColumnModel(this.colModel);
61184 this.cm = this.colModel;
61185 this.cm.xmodule = this.xmodule || false;
61187 if (this.dataSource) {
61188 this.dataSource= Roo.factory(this.dataSource, Roo.data);
61189 this.ds = this.dataSource;
61190 this.ds.xmodule = this.xmodule || false;
61197 this.container.setWidth(this.width);
61201 this.container.setHeight(this.height);
61208 * The raw click event for the entire grid.
61209 * @param {Roo.EventObject} e
61214 * The raw dblclick event for the entire grid.
61215 * @param {Roo.EventObject} e
61219 * @event contextmenu
61220 * The raw contextmenu event for the entire grid.
61221 * @param {Roo.EventObject} e
61223 "contextmenu" : true,
61226 * The raw mousedown event for the entire grid.
61227 * @param {Roo.EventObject} e
61229 "mousedown" : true,
61232 * The raw mouseup event for the entire grid.
61233 * @param {Roo.EventObject} e
61238 * The raw mouseover event for the entire grid.
61239 * @param {Roo.EventObject} e
61241 "mouseover" : true,
61244 * The raw mouseout event for the entire grid.
61245 * @param {Roo.EventObject} e
61250 * The raw keypress event for the entire grid.
61251 * @param {Roo.EventObject} e
61256 * The raw keydown event for the entire grid.
61257 * @param {Roo.EventObject} e
61265 * Fires when a cell is clicked
61266 * @param {Grid} this
61267 * @param {Number} rowIndex
61268 * @param {Number} columnIndex
61269 * @param {Roo.EventObject} e
61271 "cellclick" : true,
61273 * @event celldblclick
61274 * Fires when a cell is double clicked
61275 * @param {Grid} this
61276 * @param {Number} rowIndex
61277 * @param {Number} columnIndex
61278 * @param {Roo.EventObject} e
61280 "celldblclick" : true,
61283 * Fires when a row is clicked
61284 * @param {Grid} this
61285 * @param {Number} rowIndex
61286 * @param {Roo.EventObject} e
61290 * @event rowdblclick
61291 * Fires when a row is double clicked
61292 * @param {Grid} this
61293 * @param {Number} rowIndex
61294 * @param {Roo.EventObject} e
61296 "rowdblclick" : true,
61298 * @event headerclick
61299 * Fires when a header is clicked
61300 * @param {Grid} this
61301 * @param {Number} columnIndex
61302 * @param {Roo.EventObject} e
61304 "headerclick" : true,
61306 * @event headerdblclick
61307 * Fires when a header cell is double clicked
61308 * @param {Grid} this
61309 * @param {Number} columnIndex
61310 * @param {Roo.EventObject} e
61312 "headerdblclick" : true,
61314 * @event rowcontextmenu
61315 * Fires when a row is right clicked
61316 * @param {Grid} this
61317 * @param {Number} rowIndex
61318 * @param {Roo.EventObject} e
61320 "rowcontextmenu" : true,
61322 * @event cellcontextmenu
61323 * Fires when a cell is right clicked
61324 * @param {Grid} this
61325 * @param {Number} rowIndex
61326 * @param {Number} cellIndex
61327 * @param {Roo.EventObject} e
61329 "cellcontextmenu" : true,
61331 * @event headercontextmenu
61332 * Fires when a header is right clicked
61333 * @param {Grid} this
61334 * @param {Number} columnIndex
61335 * @param {Roo.EventObject} e
61337 "headercontextmenu" : true,
61339 * @event bodyscroll
61340 * Fires when the body element is scrolled
61341 * @param {Number} scrollLeft
61342 * @param {Number} scrollTop
61344 "bodyscroll" : true,
61346 * @event columnresize
61347 * Fires when the user resizes a column
61348 * @param {Number} columnIndex
61349 * @param {Number} newSize
61351 "columnresize" : true,
61353 * @event columnmove
61354 * Fires when the user moves a column
61355 * @param {Number} oldIndex
61356 * @param {Number} newIndex
61358 "columnmove" : true,
61361 * Fires when row(s) start being dragged
61362 * @param {Grid} this
61363 * @param {Roo.GridDD} dd The drag drop object
61364 * @param {event} e The raw browser event
61366 "startdrag" : true,
61369 * Fires when a drag operation is complete
61370 * @param {Grid} this
61371 * @param {Roo.GridDD} dd The drag drop object
61372 * @param {event} e The raw browser event
61377 * Fires when dragged row(s) are dropped on a valid DD target
61378 * @param {Grid} this
61379 * @param {Roo.GridDD} dd The drag drop object
61380 * @param {String} targetId The target drag drop object
61381 * @param {event} e The raw browser event
61386 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61387 * @param {Grid} this
61388 * @param {Roo.GridDD} dd The drag drop object
61389 * @param {String} targetId The target drag drop object
61390 * @param {event} e The raw browser event
61395 * Fires when the dragged row(s) first cross another DD target while being dragged
61396 * @param {Grid} this
61397 * @param {Roo.GridDD} dd The drag drop object
61398 * @param {String} targetId The target drag drop object
61399 * @param {event} e The raw browser event
61401 "dragenter" : true,
61404 * Fires when the dragged row(s) leave another DD target while being dragged
61405 * @param {Grid} this
61406 * @param {Roo.GridDD} dd The drag drop object
61407 * @param {String} targetId The target drag drop object
61408 * @param {event} e The raw browser event
61413 * Fires when a row is rendered, so you can change add a style to it.
61414 * @param {GridView} gridview The grid view
61415 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
61421 * Fires when the grid is rendered
61422 * @param {Grid} grid
61427 Roo.grid.Grid.superclass.constructor.call(this);
61429 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61432 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61435 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
61438 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61441 * @cfg {Roo.data.Store} ds The data store for the grid
61444 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61447 * @cfg {String} ddGroup - drag drop group.
61450 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61454 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61456 minColumnWidth : 25,
61459 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61460 * <b>on initial render.</b> It is more efficient to explicitly size the columns
61461 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
61463 autoSizeColumns : false,
61466 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61468 autoSizeHeaders : true,
61471 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61473 monitorWindowResize : true,
61476 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61477 * rows measured to get a columns size. Default is 0 (all rows).
61479 maxRowsToMeasure : 0,
61482 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61484 trackMouseOver : true,
61487 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
61490 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
61494 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61496 enableDragDrop : false,
61499 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61501 enableColumnMove : true,
61504 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61506 enableColumnHide : true,
61509 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61511 enableRowHeightSync : false,
61514 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
61519 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61521 autoHeight : false,
61524 * @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.
61526 autoExpandColumn : false,
61529 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61532 autoExpandMin : 50,
61535 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61537 autoExpandMax : 1000,
61540 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61545 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61549 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61553 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
61555 sortColMenu : false,
61561 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
61562 * of a fixed width. Default is false.
61565 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
61570 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
61571 * %0 is replaced with the number of selected rows.
61573 ddText : "{0} selected row{1}",
61577 * Called once after all setup has been completed and the grid is ready to be rendered.
61578 * @return {Roo.grid.Grid} this
61580 render : function()
61582 var c = this.container;
61583 // try to detect autoHeight/width mode
61584 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
61585 this.autoHeight = true;
61587 var view = this.getView();
61590 c.on("click", this.onClick, this);
61591 c.on("dblclick", this.onDblClick, this);
61592 c.on("contextmenu", this.onContextMenu, this);
61593 c.on("keydown", this.onKeyDown, this);
61595 c.on("touchstart", this.onTouchStart, this);
61598 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
61600 this.getSelectionModel().init(this);
61605 this.loadMask = new Roo.LoadMask(this.container,
61606 Roo.apply({store:this.dataSource}, this.loadMask));
61610 if (this.toolbar && this.toolbar.xtype) {
61611 this.toolbar.container = this.getView().getHeaderPanel(true);
61612 this.toolbar = new Roo.Toolbar(this.toolbar);
61614 if (this.footer && this.footer.xtype) {
61615 this.footer.dataSource = this.getDataSource();
61616 this.footer.container = this.getView().getFooterPanel(true);
61617 this.footer = Roo.factory(this.footer, Roo);
61619 if (this.dropTarget && this.dropTarget.xtype) {
61620 delete this.dropTarget.xtype;
61621 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
61625 this.rendered = true;
61626 this.fireEvent('render', this);
61631 * Reconfigures the grid to use a different Store and Column Model.
61632 * The View will be bound to the new objects and refreshed.
61633 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
61634 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
61636 reconfigure : function(dataSource, colModel){
61638 this.loadMask.destroy();
61639 this.loadMask = new Roo.LoadMask(this.container,
61640 Roo.apply({store:dataSource}, this.loadMask));
61642 this.view.bind(dataSource, colModel);
61643 this.dataSource = dataSource;
61644 this.colModel = colModel;
61645 this.view.refresh(true);
61649 * Add's a column, default at the end..
61651 * @param {int} position to add (default end)
61652 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
61654 addColumns : function(pos, ar)
61657 for (var i =0;i< ar.length;i++) {
61659 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
61660 this.cm.lookup[cfg.id] = cfg;
61664 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
61665 pos = this.cm.config.length; //this.cm.config.push(cfg);
61667 pos = Math.max(0,pos);
61670 this.cm.config.splice.apply(this.cm.config, ar);
61674 this.view.generateRules(this.cm);
61675 this.view.refresh(true);
61683 onKeyDown : function(e){
61684 this.fireEvent("keydown", e);
61688 * Destroy this grid.
61689 * @param {Boolean} removeEl True to remove the element
61691 destroy : function(removeEl, keepListeners){
61693 this.loadMask.destroy();
61695 var c = this.container;
61696 c.removeAllListeners();
61697 this.view.destroy();
61698 this.colModel.purgeListeners();
61699 if(!keepListeners){
61700 this.purgeListeners();
61703 if(removeEl === true){
61709 processEvent : function(name, e){
61710 // does this fire select???
61711 //Roo.log('grid:processEvent ' + name);
61713 if (name != 'touchstart' ) {
61714 this.fireEvent(name, e);
61717 var t = e.getTarget();
61719 var header = v.findHeaderIndex(t);
61720 if(header !== false){
61721 var ename = name == 'touchstart' ? 'click' : name;
61723 this.fireEvent("header" + ename, this, header, e);
61725 var row = v.findRowIndex(t);
61726 var cell = v.findCellIndex(t);
61727 if (name == 'touchstart') {
61728 // first touch is always a click.
61729 // hopefull this happens after selection is updated.?
61732 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
61733 var cs = this.selModel.getSelectedCell();
61734 if (row == cs[0] && cell == cs[1]){
61738 if (typeof(this.selModel.getSelections) != 'undefined') {
61739 var cs = this.selModel.getSelections();
61740 var ds = this.dataSource;
61741 if (cs.length == 1 && ds.getAt(row) == cs[0]){
61752 this.fireEvent("row" + name, this, row, e);
61753 if(cell !== false){
61754 this.fireEvent("cell" + name, this, row, cell, e);
61761 onClick : function(e){
61762 this.processEvent("click", e);
61765 onTouchStart : function(e){
61766 this.processEvent("touchstart", e);
61770 onContextMenu : function(e, t){
61771 this.processEvent("contextmenu", e);
61775 onDblClick : function(e){
61776 this.processEvent("dblclick", e);
61780 walkCells : function(row, col, step, fn, scope){
61781 var cm = this.colModel, clen = cm.getColumnCount();
61782 var ds = this.dataSource, rlen = ds.getCount(), first = true;
61794 if(fn.call(scope || this, row, col, cm) === true){
61812 if(fn.call(scope || this, row, col, cm) === true){
61824 getSelections : function(){
61825 return this.selModel.getSelections();
61829 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
61830 * but if manual update is required this method will initiate it.
61832 autoSize : function(){
61834 this.view.layout();
61835 if(this.view.adjustForScroll){
61836 this.view.adjustForScroll();
61842 * Returns the grid's underlying element.
61843 * @return {Element} The element
61845 getGridEl : function(){
61846 return this.container;
61849 // private for compatibility, overridden by editor grid
61850 stopEditing : function(){},
61853 * Returns the grid's SelectionModel.
61854 * @return {SelectionModel}
61856 getSelectionModel : function(){
61857 if(!this.selModel){
61858 this.selModel = new Roo.grid.RowSelectionModel();
61860 return this.selModel;
61864 * Returns the grid's DataSource.
61865 * @return {DataSource}
61867 getDataSource : function(){
61868 return this.dataSource;
61872 * Returns the grid's ColumnModel.
61873 * @return {ColumnModel}
61875 getColumnModel : function(){
61876 return this.colModel;
61880 * Returns the grid's GridView object.
61881 * @return {GridView}
61883 getView : function(){
61885 this.view = new Roo.grid.GridView(this.viewConfig);
61886 this.relayEvents(this.view, [
61887 "beforerowremoved", "beforerowsinserted",
61888 "beforerefresh", "rowremoved",
61889 "rowsinserted", "rowupdated" ,"refresh"
61895 * Called to get grid's drag proxy text, by default returns this.ddText.
61896 * Override this to put something different in the dragged text.
61899 getDragDropText : function(){
61900 var count = this.selModel.getCount();
61901 return String.format(this.ddText, count, count == 1 ? '' : 's');
61906 * Ext JS Library 1.1.1
61907 * Copyright(c) 2006-2007, Ext JS, LLC.
61909 * Originally Released Under LGPL - original licence link has changed is not relivant.
61912 * <script type="text/javascript">
61915 * @class Roo.grid.AbstractGridView
61916 * @extends Roo.util.Observable
61918 * Abstract base class for grid Views
61921 Roo.grid.AbstractGridView = function(){
61925 "beforerowremoved" : true,
61926 "beforerowsinserted" : true,
61927 "beforerefresh" : true,
61928 "rowremoved" : true,
61929 "rowsinserted" : true,
61930 "rowupdated" : true,
61933 Roo.grid.AbstractGridView.superclass.constructor.call(this);
61936 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
61937 rowClass : "x-grid-row",
61938 cellClass : "x-grid-cell",
61939 tdClass : "x-grid-td",
61940 hdClass : "x-grid-hd",
61941 splitClass : "x-grid-hd-split",
61943 init: function(grid){
61945 var cid = this.grid.getGridEl().id;
61946 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
61947 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
61948 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
61949 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
61952 getColumnRenderers : function(){
61953 var renderers = [];
61954 var cm = this.grid.colModel;
61955 var colCount = cm.getColumnCount();
61956 for(var i = 0; i < colCount; i++){
61957 renderers[i] = cm.getRenderer(i);
61962 getColumnIds : function(){
61964 var cm = this.grid.colModel;
61965 var colCount = cm.getColumnCount();
61966 for(var i = 0; i < colCount; i++){
61967 ids[i] = cm.getColumnId(i);
61972 getDataIndexes : function(){
61973 if(!this.indexMap){
61974 this.indexMap = this.buildIndexMap();
61976 return this.indexMap.colToData;
61979 getColumnIndexByDataIndex : function(dataIndex){
61980 if(!this.indexMap){
61981 this.indexMap = this.buildIndexMap();
61983 return this.indexMap.dataToCol[dataIndex];
61987 * Set a css style for a column dynamically.
61988 * @param {Number} colIndex The index of the column
61989 * @param {String} name The css property name
61990 * @param {String} value The css value
61992 setCSSStyle : function(colIndex, name, value){
61993 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
61994 Roo.util.CSS.updateRule(selector, name, value);
61997 generateRules : function(cm){
61998 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
61999 Roo.util.CSS.removeStyleSheet(rulesId);
62000 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62001 var cid = cm.getColumnId(i);
62002 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62003 this.tdSelector, cid, " {\n}\n",
62004 this.hdSelector, cid, " {\n}\n",
62005 this.splitSelector, cid, " {\n}\n");
62007 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62011 * Ext JS Library 1.1.1
62012 * Copyright(c) 2006-2007, Ext JS, LLC.
62014 * Originally Released Under LGPL - original licence link has changed is not relivant.
62017 * <script type="text/javascript">
62021 // This is a support class used internally by the Grid components
62022 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62024 this.view = grid.getView();
62025 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62026 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62028 this.setHandleElId(Roo.id(hd));
62029 this.setOuterHandleElId(Roo.id(hd2));
62031 this.scroll = false;
62033 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62035 getDragData : function(e){
62036 var t = Roo.lib.Event.getTarget(e);
62037 var h = this.view.findHeaderCell(t);
62039 return {ddel: h.firstChild, header:h};
62044 onInitDrag : function(e){
62045 this.view.headersDisabled = true;
62046 var clone = this.dragData.ddel.cloneNode(true);
62047 clone.id = Roo.id();
62048 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62049 this.proxy.update(clone);
62053 afterValidDrop : function(){
62055 setTimeout(function(){
62056 v.headersDisabled = false;
62060 afterInvalidDrop : function(){
62062 setTimeout(function(){
62063 v.headersDisabled = false;
62069 * Ext JS Library 1.1.1
62070 * Copyright(c) 2006-2007, Ext JS, LLC.
62072 * Originally Released Under LGPL - original licence link has changed is not relivant.
62075 * <script type="text/javascript">
62078 // This is a support class used internally by the Grid components
62079 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62081 this.view = grid.getView();
62082 // split the proxies so they don't interfere with mouse events
62083 this.proxyTop = Roo.DomHelper.append(document.body, {
62084 cls:"col-move-top", html:" "
62086 this.proxyBottom = Roo.DomHelper.append(document.body, {
62087 cls:"col-move-bottom", html:" "
62089 this.proxyTop.hide = this.proxyBottom.hide = function(){
62090 this.setLeftTop(-100,-100);
62091 this.setStyle("visibility", "hidden");
62093 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62094 // temporarily disabled
62095 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62096 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62098 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62099 proxyOffsets : [-4, -9],
62100 fly: Roo.Element.fly,
62102 getTargetFromEvent : function(e){
62103 var t = Roo.lib.Event.getTarget(e);
62104 var cindex = this.view.findCellIndex(t);
62105 if(cindex !== false){
62106 return this.view.getHeaderCell(cindex);
62111 nextVisible : function(h){
62112 var v = this.view, cm = this.grid.colModel;
62115 if(!cm.isHidden(v.getCellIndex(h))){
62123 prevVisible : function(h){
62124 var v = this.view, cm = this.grid.colModel;
62127 if(!cm.isHidden(v.getCellIndex(h))){
62135 positionIndicator : function(h, n, e){
62136 var x = Roo.lib.Event.getPageX(e);
62137 var r = Roo.lib.Dom.getRegion(n.firstChild);
62138 var px, pt, py = r.top + this.proxyOffsets[1];
62139 if((r.right - x) <= (r.right-r.left)/2){
62140 px = r.right+this.view.borderWidth;
62146 var oldIndex = this.view.getCellIndex(h);
62147 var newIndex = this.view.getCellIndex(n);
62149 if(this.grid.colModel.isFixed(newIndex)){
62153 var locked = this.grid.colModel.isLocked(newIndex);
62158 if(oldIndex < newIndex){
62161 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62164 px += this.proxyOffsets[0];
62165 this.proxyTop.setLeftTop(px, py);
62166 this.proxyTop.show();
62167 if(!this.bottomOffset){
62168 this.bottomOffset = this.view.mainHd.getHeight();
62170 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62171 this.proxyBottom.show();
62175 onNodeEnter : function(n, dd, e, data){
62176 if(data.header != n){
62177 this.positionIndicator(data.header, n, e);
62181 onNodeOver : function(n, dd, e, data){
62182 var result = false;
62183 if(data.header != n){
62184 result = this.positionIndicator(data.header, n, e);
62187 this.proxyTop.hide();
62188 this.proxyBottom.hide();
62190 return result ? this.dropAllowed : this.dropNotAllowed;
62193 onNodeOut : function(n, dd, e, data){
62194 this.proxyTop.hide();
62195 this.proxyBottom.hide();
62198 onNodeDrop : function(n, dd, e, data){
62199 var h = data.header;
62201 var cm = this.grid.colModel;
62202 var x = Roo.lib.Event.getPageX(e);
62203 var r = Roo.lib.Dom.getRegion(n.firstChild);
62204 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62205 var oldIndex = this.view.getCellIndex(h);
62206 var newIndex = this.view.getCellIndex(n);
62207 var locked = cm.isLocked(newIndex);
62211 if(oldIndex < newIndex){
62214 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62217 cm.setLocked(oldIndex, locked, true);
62218 cm.moveColumn(oldIndex, newIndex);
62219 this.grid.fireEvent("columnmove", oldIndex, newIndex);
62227 * Ext JS Library 1.1.1
62228 * Copyright(c) 2006-2007, Ext JS, LLC.
62230 * Originally Released Under LGPL - original licence link has changed is not relivant.
62233 * <script type="text/javascript">
62237 * @class Roo.grid.GridView
62238 * @extends Roo.util.Observable
62241 * @param {Object} config
62243 Roo.grid.GridView = function(config){
62244 Roo.grid.GridView.superclass.constructor.call(this);
62247 Roo.apply(this, config);
62250 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62252 unselectable : 'unselectable="on"',
62253 unselectableCls : 'x-unselectable',
62256 rowClass : "x-grid-row",
62258 cellClass : "x-grid-col",
62260 tdClass : "x-grid-td",
62262 hdClass : "x-grid-hd",
62264 splitClass : "x-grid-split",
62266 sortClasses : ["sort-asc", "sort-desc"],
62268 enableMoveAnim : false,
62272 dh : Roo.DomHelper,
62274 fly : Roo.Element.fly,
62276 css : Roo.util.CSS,
62282 scrollIncrement : 22,
62284 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62286 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62288 bind : function(ds, cm){
62290 this.ds.un("load", this.onLoad, this);
62291 this.ds.un("datachanged", this.onDataChange, this);
62292 this.ds.un("add", this.onAdd, this);
62293 this.ds.un("remove", this.onRemove, this);
62294 this.ds.un("update", this.onUpdate, this);
62295 this.ds.un("clear", this.onClear, this);
62298 ds.on("load", this.onLoad, this);
62299 ds.on("datachanged", this.onDataChange, this);
62300 ds.on("add", this.onAdd, this);
62301 ds.on("remove", this.onRemove, this);
62302 ds.on("update", this.onUpdate, this);
62303 ds.on("clear", this.onClear, this);
62308 this.cm.un("widthchange", this.onColWidthChange, this);
62309 this.cm.un("headerchange", this.onHeaderChange, this);
62310 this.cm.un("hiddenchange", this.onHiddenChange, this);
62311 this.cm.un("columnmoved", this.onColumnMove, this);
62312 this.cm.un("columnlockchange", this.onColumnLock, this);
62315 this.generateRules(cm);
62316 cm.on("widthchange", this.onColWidthChange, this);
62317 cm.on("headerchange", this.onHeaderChange, this);
62318 cm.on("hiddenchange", this.onHiddenChange, this);
62319 cm.on("columnmoved", this.onColumnMove, this);
62320 cm.on("columnlockchange", this.onColumnLock, this);
62325 init: function(grid){
62326 Roo.grid.GridView.superclass.init.call(this, grid);
62328 this.bind(grid.dataSource, grid.colModel);
62330 grid.on("headerclick", this.handleHeaderClick, this);
62332 if(grid.trackMouseOver){
62333 grid.on("mouseover", this.onRowOver, this);
62334 grid.on("mouseout", this.onRowOut, this);
62336 grid.cancelTextSelection = function(){};
62337 this.gridId = grid.id;
62339 var tpls = this.templates || {};
62342 tpls.master = new Roo.Template(
62343 '<div class="x-grid" hidefocus="true">',
62344 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62345 '<div class="x-grid-topbar"></div>',
62346 '<div class="x-grid-scroller"><div></div></div>',
62347 '<div class="x-grid-locked">',
62348 '<div class="x-grid-header">{lockedHeader}</div>',
62349 '<div class="x-grid-body">{lockedBody}</div>',
62351 '<div class="x-grid-viewport">',
62352 '<div class="x-grid-header">{header}</div>',
62353 '<div class="x-grid-body">{body}</div>',
62355 '<div class="x-grid-bottombar"></div>',
62357 '<div class="x-grid-resize-proxy"> </div>',
62360 tpls.master.disableformats = true;
62364 tpls.header = new Roo.Template(
62365 '<table border="0" cellspacing="0" cellpadding="0">',
62366 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62369 tpls.header.disableformats = true;
62371 tpls.header.compile();
62374 tpls.hcell = new Roo.Template(
62375 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62376 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62379 tpls.hcell.disableFormats = true;
62381 tpls.hcell.compile();
62384 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62385 this.unselectableCls + '" ' + this.unselectable +'> </div>');
62386 tpls.hsplit.disableFormats = true;
62388 tpls.hsplit.compile();
62391 tpls.body = new Roo.Template(
62392 '<table border="0" cellspacing="0" cellpadding="0">',
62393 "<tbody>{rows}</tbody>",
62396 tpls.body.disableFormats = true;
62398 tpls.body.compile();
62401 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62402 tpls.row.disableFormats = true;
62404 tpls.row.compile();
62407 tpls.cell = new Roo.Template(
62408 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62409 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62410 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62413 tpls.cell.disableFormats = true;
62415 tpls.cell.compile();
62417 this.templates = tpls;
62420 // remap these for backwards compat
62421 onColWidthChange : function(){
62422 this.updateColumns.apply(this, arguments);
62424 onHeaderChange : function(){
62425 this.updateHeaders.apply(this, arguments);
62427 onHiddenChange : function(){
62428 this.handleHiddenChange.apply(this, arguments);
62430 onColumnMove : function(){
62431 this.handleColumnMove.apply(this, arguments);
62433 onColumnLock : function(){
62434 this.handleLockChange.apply(this, arguments);
62437 onDataChange : function(){
62439 this.updateHeaderSortState();
62442 onClear : function(){
62446 onUpdate : function(ds, record){
62447 this.refreshRow(record);
62450 refreshRow : function(record){
62451 var ds = this.ds, index;
62452 if(typeof record == 'number'){
62454 record = ds.getAt(index);
62456 index = ds.indexOf(record);
62458 this.insertRows(ds, index, index, true);
62459 this.onRemove(ds, record, index+1, true);
62460 this.syncRowHeights(index, index);
62462 this.fireEvent("rowupdated", this, index, record);
62465 onAdd : function(ds, records, index){
62466 this.insertRows(ds, index, index + (records.length-1));
62469 onRemove : function(ds, record, index, isUpdate){
62470 if(isUpdate !== true){
62471 this.fireEvent("beforerowremoved", this, index, record);
62473 var bt = this.getBodyTable(), lt = this.getLockedTable();
62474 if(bt.rows[index]){
62475 bt.firstChild.removeChild(bt.rows[index]);
62477 if(lt.rows[index]){
62478 lt.firstChild.removeChild(lt.rows[index]);
62480 if(isUpdate !== true){
62481 this.stripeRows(index);
62482 this.syncRowHeights(index, index);
62484 this.fireEvent("rowremoved", this, index, record);
62488 onLoad : function(){
62489 this.scrollToTop();
62493 * Scrolls the grid to the top
62495 scrollToTop : function(){
62497 this.scroller.dom.scrollTop = 0;
62503 * Gets a panel in the header of the grid that can be used for toolbars etc.
62504 * After modifying the contents of this panel a call to grid.autoSize() may be
62505 * required to register any changes in size.
62506 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62507 * @return Roo.Element
62509 getHeaderPanel : function(doShow){
62511 this.headerPanel.show();
62513 return this.headerPanel;
62517 * Gets a panel in the footer of the grid that can be used for toolbars etc.
62518 * After modifying the contents of this panel a call to grid.autoSize() may be
62519 * required to register any changes in size.
62520 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62521 * @return Roo.Element
62523 getFooterPanel : function(doShow){
62525 this.footerPanel.show();
62527 return this.footerPanel;
62530 initElements : function(){
62531 var E = Roo.Element;
62532 var el = this.grid.getGridEl().dom.firstChild;
62533 var cs = el.childNodes;
62535 this.el = new E(el);
62537 this.focusEl = new E(el.firstChild);
62538 this.focusEl.swallowEvent("click", true);
62540 this.headerPanel = new E(cs[1]);
62541 this.headerPanel.enableDisplayMode("block");
62543 this.scroller = new E(cs[2]);
62544 this.scrollSizer = new E(this.scroller.dom.firstChild);
62546 this.lockedWrap = new E(cs[3]);
62547 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62548 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62550 this.mainWrap = new E(cs[4]);
62551 this.mainHd = new E(this.mainWrap.dom.firstChild);
62552 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
62554 this.footerPanel = new E(cs[5]);
62555 this.footerPanel.enableDisplayMode("block");
62557 this.resizeProxy = new E(cs[6]);
62559 this.headerSelector = String.format(
62560 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
62561 this.lockedHd.id, this.mainHd.id
62564 this.splitterSelector = String.format(
62565 '#{0} div.x-grid-split, #{1} div.x-grid-split',
62566 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
62569 idToCssName : function(s)
62571 return s.replace(/[^a-z0-9]+/ig, '-');
62574 getHeaderCell : function(index){
62575 return Roo.DomQuery.select(this.headerSelector)[index];
62578 getHeaderCellMeasure : function(index){
62579 return this.getHeaderCell(index).firstChild;
62582 getHeaderCellText : function(index){
62583 return this.getHeaderCell(index).firstChild.firstChild;
62586 getLockedTable : function(){
62587 return this.lockedBody.dom.firstChild;
62590 getBodyTable : function(){
62591 return this.mainBody.dom.firstChild;
62594 getLockedRow : function(index){
62595 return this.getLockedTable().rows[index];
62598 getRow : function(index){
62599 return this.getBodyTable().rows[index];
62602 getRowComposite : function(index){
62604 this.rowEl = new Roo.CompositeElementLite();
62606 var els = [], lrow, mrow;
62607 if(lrow = this.getLockedRow(index)){
62610 if(mrow = this.getRow(index)){
62613 this.rowEl.elements = els;
62617 * Gets the 'td' of the cell
62619 * @param {Integer} rowIndex row to select
62620 * @param {Integer} colIndex column to select
62624 getCell : function(rowIndex, colIndex){
62625 var locked = this.cm.getLockedCount();
62627 if(colIndex < locked){
62628 source = this.lockedBody.dom.firstChild;
62630 source = this.mainBody.dom.firstChild;
62631 colIndex -= locked;
62633 return source.rows[rowIndex].childNodes[colIndex];
62636 getCellText : function(rowIndex, colIndex){
62637 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
62640 getCellBox : function(cell){
62641 var b = this.fly(cell).getBox();
62642 if(Roo.isOpera){ // opera fails to report the Y
62643 b.y = cell.offsetTop + this.mainBody.getY();
62648 getCellIndex : function(cell){
62649 var id = String(cell.className).match(this.cellRE);
62651 return parseInt(id[1], 10);
62656 findHeaderIndex : function(n){
62657 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62658 return r ? this.getCellIndex(r) : false;
62661 findHeaderCell : function(n){
62662 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62663 return r ? r : false;
62666 findRowIndex : function(n){
62670 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
62671 return r ? r.rowIndex : false;
62674 findCellIndex : function(node){
62675 var stop = this.el.dom;
62676 while(node && node != stop){
62677 if(this.findRE.test(node.className)){
62678 return this.getCellIndex(node);
62680 node = node.parentNode;
62685 getColumnId : function(index){
62686 return this.cm.getColumnId(index);
62689 getSplitters : function()
62691 if(this.splitterSelector){
62692 return Roo.DomQuery.select(this.splitterSelector);
62698 getSplitter : function(index){
62699 return this.getSplitters()[index];
62702 onRowOver : function(e, t){
62704 if((row = this.findRowIndex(t)) !== false){
62705 this.getRowComposite(row).addClass("x-grid-row-over");
62709 onRowOut : function(e, t){
62711 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
62712 this.getRowComposite(row).removeClass("x-grid-row-over");
62716 renderHeaders : function(){
62718 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
62719 var cb = [], lb = [], sb = [], lsb = [], p = {};
62720 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62721 p.cellId = "x-grid-hd-0-" + i;
62722 p.splitId = "x-grid-csplit-0-" + i;
62723 p.id = cm.getColumnId(i);
62724 p.value = cm.getColumnHeader(i) || "";
62725 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
62726 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
62727 if(!cm.isLocked(i)){
62728 cb[cb.length] = ct.apply(p);
62729 sb[sb.length] = st.apply(p);
62731 lb[lb.length] = ct.apply(p);
62732 lsb[lsb.length] = st.apply(p);
62735 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
62736 ht.apply({cells: cb.join(""), splits:sb.join("")})];
62739 updateHeaders : function(){
62740 var html = this.renderHeaders();
62741 this.lockedHd.update(html[0]);
62742 this.mainHd.update(html[1]);
62746 * Focuses the specified row.
62747 * @param {Number} row The row index
62749 focusRow : function(row)
62751 //Roo.log('GridView.focusRow');
62752 var x = this.scroller.dom.scrollLeft;
62753 this.focusCell(row, 0, false);
62754 this.scroller.dom.scrollLeft = x;
62758 * Focuses the specified cell.
62759 * @param {Number} row The row index
62760 * @param {Number} col The column index
62761 * @param {Boolean} hscroll false to disable horizontal scrolling
62763 focusCell : function(row, col, hscroll)
62765 //Roo.log('GridView.focusCell');
62766 var el = this.ensureVisible(row, col, hscroll);
62767 this.focusEl.alignTo(el, "tl-tl");
62769 this.focusEl.focus();
62771 this.focusEl.focus.defer(1, this.focusEl);
62776 * Scrolls the specified cell into view
62777 * @param {Number} row The row index
62778 * @param {Number} col The column index
62779 * @param {Boolean} hscroll false to disable horizontal scrolling
62781 ensureVisible : function(row, col, hscroll)
62783 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
62784 //return null; //disable for testing.
62785 if(typeof row != "number"){
62786 row = row.rowIndex;
62788 if(row < 0 && row >= this.ds.getCount()){
62791 col = (col !== undefined ? col : 0);
62792 var cm = this.grid.colModel;
62793 while(cm.isHidden(col)){
62797 var el = this.getCell(row, col);
62801 var c = this.scroller.dom;
62803 var ctop = parseInt(el.offsetTop, 10);
62804 var cleft = parseInt(el.offsetLeft, 10);
62805 var cbot = ctop + el.offsetHeight;
62806 var cright = cleft + el.offsetWidth;
62808 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
62809 var stop = parseInt(c.scrollTop, 10);
62810 var sleft = parseInt(c.scrollLeft, 10);
62811 var sbot = stop + ch;
62812 var sright = sleft + c.clientWidth;
62814 Roo.log('GridView.ensureVisible:' +
62816 ' c.clientHeight:' + c.clientHeight +
62817 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
62825 c.scrollTop = ctop;
62826 //Roo.log("set scrolltop to ctop DISABLE?");
62827 }else if(cbot > sbot){
62828 //Roo.log("set scrolltop to cbot-ch");
62829 c.scrollTop = cbot-ch;
62832 if(hscroll !== false){
62834 c.scrollLeft = cleft;
62835 }else if(cright > sright){
62836 c.scrollLeft = cright-c.clientWidth;
62843 updateColumns : function(){
62844 this.grid.stopEditing();
62845 var cm = this.grid.colModel, colIds = this.getColumnIds();
62846 //var totalWidth = cm.getTotalWidth();
62848 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62849 //if(cm.isHidden(i)) continue;
62850 var w = cm.getColumnWidth(i);
62851 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
62852 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
62854 this.updateSplitters();
62857 generateRules : function(cm){
62858 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
62859 Roo.util.CSS.removeStyleSheet(rulesId);
62860 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62861 var cid = cm.getColumnId(i);
62863 if(cm.config[i].align){
62864 align = 'text-align:'+cm.config[i].align+';';
62867 if(cm.isHidden(i)){
62868 hidden = 'display:none;';
62870 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
62872 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
62873 this.hdSelector, cid, " {\n", align, width, "}\n",
62874 this.tdSelector, cid, " {\n",hidden,"\n}\n",
62875 this.splitSelector, cid, " {\n", hidden , "\n}\n");
62877 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62880 updateSplitters : function(){
62881 var cm = this.cm, s = this.getSplitters();
62882 if(s){ // splitters not created yet
62883 var pos = 0, locked = true;
62884 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62885 if(cm.isHidden(i)) {
62888 var w = cm.getColumnWidth(i); // make sure it's a number
62889 if(!cm.isLocked(i) && locked){
62894 s[i].style.left = (pos-this.splitOffset) + "px";
62899 handleHiddenChange : function(colModel, colIndex, hidden){
62901 this.hideColumn(colIndex);
62903 this.unhideColumn(colIndex);
62907 hideColumn : function(colIndex){
62908 var cid = this.getColumnId(colIndex);
62909 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
62910 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
62912 this.updateHeaders();
62914 this.updateSplitters();
62918 unhideColumn : function(colIndex){
62919 var cid = this.getColumnId(colIndex);
62920 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
62921 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
62924 this.updateHeaders();
62926 this.updateSplitters();
62930 insertRows : function(dm, firstRow, lastRow, isUpdate){
62931 if(firstRow == 0 && lastRow == dm.getCount()-1){
62935 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
62937 var s = this.getScrollState();
62938 var markup = this.renderRows(firstRow, lastRow);
62939 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
62940 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
62941 this.restoreScroll(s);
62943 this.fireEvent("rowsinserted", this, firstRow, lastRow);
62944 this.syncRowHeights(firstRow, lastRow);
62945 this.stripeRows(firstRow);
62951 bufferRows : function(markup, target, index){
62952 var before = null, trows = target.rows, tbody = target.tBodies[0];
62953 if(index < trows.length){
62954 before = trows[index];
62956 var b = document.createElement("div");
62957 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
62958 var rows = b.firstChild.rows;
62959 for(var i = 0, len = rows.length; i < len; i++){
62961 tbody.insertBefore(rows[0], before);
62963 tbody.appendChild(rows[0]);
62970 deleteRows : function(dm, firstRow, lastRow){
62971 if(dm.getRowCount()<1){
62972 this.fireEvent("beforerefresh", this);
62973 this.mainBody.update("");
62974 this.lockedBody.update("");
62975 this.fireEvent("refresh", this);
62977 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
62978 var bt = this.getBodyTable();
62979 var tbody = bt.firstChild;
62980 var rows = bt.rows;
62981 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
62982 tbody.removeChild(rows[firstRow]);
62984 this.stripeRows(firstRow);
62985 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
62989 updateRows : function(dataSource, firstRow, lastRow){
62990 var s = this.getScrollState();
62992 this.restoreScroll(s);
62995 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
62999 this.updateHeaderSortState();
63002 getScrollState : function(){
63004 var sb = this.scroller.dom;
63005 return {left: sb.scrollLeft, top: sb.scrollTop};
63008 stripeRows : function(startRow){
63009 if(!this.grid.stripeRows || this.ds.getCount() < 1){
63012 startRow = startRow || 0;
63013 var rows = this.getBodyTable().rows;
63014 var lrows = this.getLockedTable().rows;
63015 var cls = ' x-grid-row-alt ';
63016 for(var i = startRow, len = rows.length; i < len; i++){
63017 var row = rows[i], lrow = lrows[i];
63018 var isAlt = ((i+1) % 2 == 0);
63019 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63020 if(isAlt == hasAlt){
63024 row.className += " x-grid-row-alt";
63026 row.className = row.className.replace("x-grid-row-alt", "");
63029 lrow.className = row.className;
63034 restoreScroll : function(state){
63035 //Roo.log('GridView.restoreScroll');
63036 var sb = this.scroller.dom;
63037 sb.scrollLeft = state.left;
63038 sb.scrollTop = state.top;
63042 syncScroll : function(){
63043 //Roo.log('GridView.syncScroll');
63044 var sb = this.scroller.dom;
63045 var sh = this.mainHd.dom;
63046 var bs = this.mainBody.dom;
63047 var lv = this.lockedBody.dom;
63048 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63049 lv.scrollTop = bs.scrollTop = sb.scrollTop;
63052 handleScroll : function(e){
63054 var sb = this.scroller.dom;
63055 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63059 handleWheel : function(e){
63060 var d = e.getWheelDelta();
63061 this.scroller.dom.scrollTop -= d*22;
63062 // set this here to prevent jumpy scrolling on large tables
63063 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63067 renderRows : function(startRow, endRow){
63068 // pull in all the crap needed to render rows
63069 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63070 var colCount = cm.getColumnCount();
63072 if(ds.getCount() < 1){
63076 // build a map for all the columns
63078 for(var i = 0; i < colCount; i++){
63079 var name = cm.getDataIndex(i);
63081 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63082 renderer : cm.getRenderer(i),
63083 id : cm.getColumnId(i),
63084 locked : cm.isLocked(i),
63085 has_editor : cm.isCellEditable(i)
63089 startRow = startRow || 0;
63090 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63092 // records to render
63093 var rs = ds.getRange(startRow, endRow);
63095 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63098 // As much as I hate to duplicate code, this was branched because FireFox really hates
63099 // [].join("") on strings. The performance difference was substantial enough to
63100 // branch this function
63101 doRender : Roo.isGecko ?
63102 function(cs, rs, ds, startRow, colCount, stripe){
63103 var ts = this.templates, ct = ts.cell, rt = ts.row;
63105 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63107 var hasListener = this.grid.hasListener('rowclass');
63109 for(var j = 0, len = rs.length; j < len; j++){
63110 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63111 for(var i = 0; i < colCount; i++){
63113 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63115 p.css = p.attr = "";
63116 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63117 if(p.value == undefined || p.value === "") {
63118 p.value = " ";
63121 p.css += ' x-grid-editable-cell';
63123 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63124 p.css += ' x-grid-dirty-cell';
63126 var markup = ct.apply(p);
63134 if(stripe && ((rowIndex+1) % 2 == 0)){
63135 alt.push("x-grid-row-alt")
63138 alt.push( " x-grid-dirty-row");
63141 if(this.getRowClass){
63142 alt.push(this.getRowClass(r, rowIndex));
63148 rowIndex : rowIndex,
63151 this.grid.fireEvent('rowclass', this, rowcfg);
63152 alt.push(rowcfg.rowClass);
63154 rp.alt = alt.join(" ");
63155 lbuf+= rt.apply(rp);
63157 buf+= rt.apply(rp);
63159 return [lbuf, buf];
63161 function(cs, rs, ds, startRow, colCount, stripe){
63162 var ts = this.templates, ct = ts.cell, rt = ts.row;
63164 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63165 var hasListener = this.grid.hasListener('rowclass');
63168 for(var j = 0, len = rs.length; j < len; j++){
63169 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63170 for(var i = 0; i < colCount; i++){
63172 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63174 p.css = p.attr = "";
63175 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63176 if(p.value == undefined || p.value === "") {
63177 p.value = " ";
63181 p.css += ' x-grid-editable-cell';
63183 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63184 p.css += ' x-grid-dirty-cell'
63187 var markup = ct.apply(p);
63189 cb[cb.length] = markup;
63191 lcb[lcb.length] = markup;
63195 if(stripe && ((rowIndex+1) % 2 == 0)){
63196 alt.push( "x-grid-row-alt");
63199 alt.push(" x-grid-dirty-row");
63202 if(this.getRowClass){
63203 alt.push( this.getRowClass(r, rowIndex));
63209 rowIndex : rowIndex,
63212 this.grid.fireEvent('rowclass', this, rowcfg);
63213 alt.push(rowcfg.rowClass);
63216 rp.alt = alt.join(" ");
63217 rp.cells = lcb.join("");
63218 lbuf[lbuf.length] = rt.apply(rp);
63219 rp.cells = cb.join("");
63220 buf[buf.length] = rt.apply(rp);
63222 return [lbuf.join(""), buf.join("")];
63225 renderBody : function(){
63226 var markup = this.renderRows();
63227 var bt = this.templates.body;
63228 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63232 * Refreshes the grid
63233 * @param {Boolean} headersToo
63235 refresh : function(headersToo){
63236 this.fireEvent("beforerefresh", this);
63237 this.grid.stopEditing();
63238 var result = this.renderBody();
63239 this.lockedBody.update(result[0]);
63240 this.mainBody.update(result[1]);
63241 if(headersToo === true){
63242 this.updateHeaders();
63243 this.updateColumns();
63244 this.updateSplitters();
63245 this.updateHeaderSortState();
63247 this.syncRowHeights();
63249 this.fireEvent("refresh", this);
63252 handleColumnMove : function(cm, oldIndex, newIndex){
63253 this.indexMap = null;
63254 var s = this.getScrollState();
63255 this.refresh(true);
63256 this.restoreScroll(s);
63257 this.afterMove(newIndex);
63260 afterMove : function(colIndex){
63261 if(this.enableMoveAnim && Roo.enableFx){
63262 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63264 // if multisort - fix sortOrder, and reload..
63265 if (this.grid.dataSource.multiSort) {
63266 // the we can call sort again..
63267 var dm = this.grid.dataSource;
63268 var cm = this.grid.colModel;
63270 for(var i = 0; i < cm.config.length; i++ ) {
63272 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63273 continue; // dont' bother, it's not in sort list or being set.
63276 so.push(cm.config[i].dataIndex);
63279 dm.load(dm.lastOptions);
63286 updateCell : function(dm, rowIndex, dataIndex){
63287 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63288 if(typeof colIndex == "undefined"){ // not present in grid
63291 var cm = this.grid.colModel;
63292 var cell = this.getCell(rowIndex, colIndex);
63293 var cellText = this.getCellText(rowIndex, colIndex);
63296 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63297 id : cm.getColumnId(colIndex),
63298 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63300 var renderer = cm.getRenderer(colIndex);
63301 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63302 if(typeof val == "undefined" || val === "") {
63305 cellText.innerHTML = val;
63306 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63307 this.syncRowHeights(rowIndex, rowIndex);
63310 calcColumnWidth : function(colIndex, maxRowsToMeasure){
63312 if(this.grid.autoSizeHeaders){
63313 var h = this.getHeaderCellMeasure(colIndex);
63314 maxWidth = Math.max(maxWidth, h.scrollWidth);
63317 if(this.cm.isLocked(colIndex)){
63318 tb = this.getLockedTable();
63321 tb = this.getBodyTable();
63322 index = colIndex - this.cm.getLockedCount();
63325 var rows = tb.rows;
63326 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63327 for(var i = 0; i < stopIndex; i++){
63328 var cell = rows[i].childNodes[index].firstChild;
63329 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63332 return maxWidth + /*margin for error in IE*/ 5;
63335 * Autofit a column to its content.
63336 * @param {Number} colIndex
63337 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63339 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63340 if(this.cm.isHidden(colIndex)){
63341 return; // can't calc a hidden column
63344 var cid = this.cm.getColumnId(colIndex);
63345 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63346 if(this.grid.autoSizeHeaders){
63347 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63350 var newWidth = this.calcColumnWidth(colIndex);
63351 this.cm.setColumnWidth(colIndex,
63352 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63353 if(!suppressEvent){
63354 this.grid.fireEvent("columnresize", colIndex, newWidth);
63359 * Autofits all columns to their content and then expands to fit any extra space in the grid
63361 autoSizeColumns : function(){
63362 var cm = this.grid.colModel;
63363 var colCount = cm.getColumnCount();
63364 for(var i = 0; i < colCount; i++){
63365 this.autoSizeColumn(i, true, true);
63367 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63370 this.updateColumns();
63376 * Autofits all columns to the grid's width proportionate with their current size
63377 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63379 fitColumns : function(reserveScrollSpace){
63380 var cm = this.grid.colModel;
63381 var colCount = cm.getColumnCount();
63385 for (i = 0; i < colCount; i++){
63386 if(!cm.isHidden(i) && !cm.isFixed(i)){
63387 w = cm.getColumnWidth(i);
63393 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63394 if(reserveScrollSpace){
63397 var frac = (avail - cm.getTotalWidth())/width;
63398 while (cols.length){
63401 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63403 this.updateColumns();
63407 onRowSelect : function(rowIndex){
63408 var row = this.getRowComposite(rowIndex);
63409 row.addClass("x-grid-row-selected");
63412 onRowDeselect : function(rowIndex){
63413 var row = this.getRowComposite(rowIndex);
63414 row.removeClass("x-grid-row-selected");
63417 onCellSelect : function(row, col){
63418 var cell = this.getCell(row, col);
63420 Roo.fly(cell).addClass("x-grid-cell-selected");
63424 onCellDeselect : function(row, col){
63425 var cell = this.getCell(row, col);
63427 Roo.fly(cell).removeClass("x-grid-cell-selected");
63431 updateHeaderSortState : function(){
63433 // sort state can be single { field: xxx, direction : yyy}
63434 // or { xxx=>ASC , yyy : DESC ..... }
63437 if (!this.ds.multiSort) {
63438 var state = this.ds.getSortState();
63442 mstate[state.field] = state.direction;
63443 // FIXME... - this is not used here.. but might be elsewhere..
63444 this.sortState = state;
63447 mstate = this.ds.sortToggle;
63449 //remove existing sort classes..
63451 var sc = this.sortClasses;
63452 var hds = this.el.select(this.headerSelector).removeClass(sc);
63454 for(var f in mstate) {
63456 var sortColumn = this.cm.findColumnIndex(f);
63458 if(sortColumn != -1){
63459 var sortDir = mstate[f];
63460 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63469 handleHeaderClick : function(g, index,e){
63471 Roo.log("header click");
63474 // touch events on header are handled by context
63475 this.handleHdCtx(g,index,e);
63480 if(this.headersDisabled){
63483 var dm = g.dataSource, cm = g.colModel;
63484 if(!cm.isSortable(index)){
63489 if (dm.multiSort) {
63490 // update the sortOrder
63492 for(var i = 0; i < cm.config.length; i++ ) {
63494 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63495 continue; // dont' bother, it's not in sort list or being set.
63498 so.push(cm.config[i].dataIndex);
63504 dm.sort(cm.getDataIndex(index));
63508 destroy : function(){
63510 this.colMenu.removeAll();
63511 Roo.menu.MenuMgr.unregister(this.colMenu);
63512 this.colMenu.getEl().remove();
63513 delete this.colMenu;
63516 this.hmenu.removeAll();
63517 Roo.menu.MenuMgr.unregister(this.hmenu);
63518 this.hmenu.getEl().remove();
63521 if(this.grid.enableColumnMove){
63522 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63524 for(var dd in dds){
63525 if(!dds[dd].config.isTarget && dds[dd].dragElId){
63526 var elid = dds[dd].dragElId;
63528 Roo.get(elid).remove();
63529 } else if(dds[dd].config.isTarget){
63530 dds[dd].proxyTop.remove();
63531 dds[dd].proxyBottom.remove();
63534 if(Roo.dd.DDM.locationCache[dd]){
63535 delete Roo.dd.DDM.locationCache[dd];
63538 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63541 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63542 this.bind(null, null);
63543 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63546 handleLockChange : function(){
63547 this.refresh(true);
63550 onDenyColumnLock : function(){
63554 onDenyColumnHide : function(){
63558 handleHdMenuClick : function(item){
63559 var index = this.hdCtxIndex;
63560 var cm = this.cm, ds = this.ds;
63563 ds.sort(cm.getDataIndex(index), "ASC");
63566 ds.sort(cm.getDataIndex(index), "DESC");
63569 var lc = cm.getLockedCount();
63570 if(cm.getColumnCount(true) <= lc+1){
63571 this.onDenyColumnLock();
63575 cm.setLocked(index, true, true);
63576 cm.moveColumn(index, lc);
63577 this.grid.fireEvent("columnmove", index, lc);
63579 cm.setLocked(index, true);
63583 var lc = cm.getLockedCount();
63584 if((lc-1) != index){
63585 cm.setLocked(index, false, true);
63586 cm.moveColumn(index, lc-1);
63587 this.grid.fireEvent("columnmove", index, lc-1);
63589 cm.setLocked(index, false);
63592 case 'wider': // used to expand cols on touch..
63594 var cw = cm.getColumnWidth(index);
63595 cw += (item.id == 'wider' ? 1 : -1) * 50;
63596 cw = Math.max(0, cw);
63597 cw = Math.min(cw,4000);
63598 cm.setColumnWidth(index, cw);
63602 index = cm.getIndexById(item.id.substr(4));
63604 if(item.checked && cm.getColumnCount(true) <= 1){
63605 this.onDenyColumnHide();
63608 cm.setHidden(index, item.checked);
63614 beforeColMenuShow : function(){
63615 var cm = this.cm, colCount = cm.getColumnCount();
63616 this.colMenu.removeAll();
63619 for(var i = 0; i < colCount; i++){
63621 id: "col-"+cm.getColumnId(i),
63622 text: cm.getColumnHeader(i),
63623 checked: !cm.isHidden(i),
63628 if (this.grid.sortColMenu) {
63629 items.sort(function(a,b) {
63630 if (a.text == b.text) {
63633 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
63637 for(var i = 0; i < colCount; i++){
63638 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
63642 handleHdCtx : function(g, index, e){
63644 var hd = this.getHeaderCell(index);
63645 this.hdCtxIndex = index;
63646 var ms = this.hmenu.items, cm = this.cm;
63647 ms.get("asc").setDisabled(!cm.isSortable(index));
63648 ms.get("desc").setDisabled(!cm.isSortable(index));
63649 if(this.grid.enableColLock !== false){
63650 ms.get("lock").setDisabled(cm.isLocked(index));
63651 ms.get("unlock").setDisabled(!cm.isLocked(index));
63653 this.hmenu.show(hd, "tl-bl");
63656 handleHdOver : function(e){
63657 var hd = this.findHeaderCell(e.getTarget());
63658 if(hd && !this.headersDisabled){
63659 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
63660 this.fly(hd).addClass("x-grid-hd-over");
63665 handleHdOut : function(e){
63666 var hd = this.findHeaderCell(e.getTarget());
63668 this.fly(hd).removeClass("x-grid-hd-over");
63672 handleSplitDblClick : function(e, t){
63673 var i = this.getCellIndex(t);
63674 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
63675 this.autoSizeColumn(i, true);
63680 render : function(){
63683 var colCount = cm.getColumnCount();
63685 if(this.grid.monitorWindowResize === true){
63686 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
63688 var header = this.renderHeaders();
63689 var body = this.templates.body.apply({rows:""});
63690 var html = this.templates.master.apply({
63693 lockedHeader: header[0],
63697 //this.updateColumns();
63699 this.grid.getGridEl().dom.innerHTML = html;
63701 this.initElements();
63703 // a kludge to fix the random scolling effect in webkit
63704 this.el.on("scroll", function() {
63705 this.el.dom.scrollTop=0; // hopefully not recursive..
63708 this.scroller.on("scroll", this.handleScroll, this);
63709 this.lockedBody.on("mousewheel", this.handleWheel, this);
63710 this.mainBody.on("mousewheel", this.handleWheel, this);
63712 this.mainHd.on("mouseover", this.handleHdOver, this);
63713 this.mainHd.on("mouseout", this.handleHdOut, this);
63714 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
63715 {delegate: "."+this.splitClass});
63717 this.lockedHd.on("mouseover", this.handleHdOver, this);
63718 this.lockedHd.on("mouseout", this.handleHdOut, this);
63719 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
63720 {delegate: "."+this.splitClass});
63722 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
63723 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63726 this.updateSplitters();
63728 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
63729 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63730 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63733 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
63734 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
63736 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
63737 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
63739 if(this.grid.enableColLock !== false){
63740 this.hmenu.add('-',
63741 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
63742 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
63746 this.hmenu.add('-',
63747 {id:"wider", text: this.columnsWiderText},
63748 {id:"narrow", text: this.columnsNarrowText }
63754 if(this.grid.enableColumnHide !== false){
63756 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
63757 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
63758 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
63760 this.hmenu.add('-',
63761 {id:"columns", text: this.columnsText, menu: this.colMenu}
63764 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
63766 this.grid.on("headercontextmenu", this.handleHdCtx, this);
63769 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
63770 this.dd = new Roo.grid.GridDragZone(this.grid, {
63771 ddGroup : this.grid.ddGroup || 'GridDD'
63777 for(var i = 0; i < colCount; i++){
63778 if(cm.isHidden(i)){
63779 this.hideColumn(i);
63781 if(cm.config[i].align){
63782 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
63783 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
63787 this.updateHeaderSortState();
63789 this.beforeInitialResize();
63792 // two part rendering gives faster view to the user
63793 this.renderPhase2.defer(1, this);
63796 renderPhase2 : function(){
63797 // render the rows now
63799 if(this.grid.autoSizeColumns){
63800 this.autoSizeColumns();
63804 beforeInitialResize : function(){
63808 onColumnSplitterMoved : function(i, w){
63809 this.userResized = true;
63810 var cm = this.grid.colModel;
63811 cm.setColumnWidth(i, w, true);
63812 var cid = cm.getColumnId(i);
63813 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63814 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63815 this.updateSplitters();
63817 this.grid.fireEvent("columnresize", i, w);
63820 syncRowHeights : function(startIndex, endIndex){
63821 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
63822 startIndex = startIndex || 0;
63823 var mrows = this.getBodyTable().rows;
63824 var lrows = this.getLockedTable().rows;
63825 var len = mrows.length-1;
63826 endIndex = Math.min(endIndex || len, len);
63827 for(var i = startIndex; i <= endIndex; i++){
63828 var m = mrows[i], l = lrows[i];
63829 var h = Math.max(m.offsetHeight, l.offsetHeight);
63830 m.style.height = l.style.height = h + "px";
63835 layout : function(initialRender, is2ndPass)
63838 var auto = g.autoHeight;
63839 var scrollOffset = 16;
63840 var c = g.getGridEl(), cm = this.cm,
63841 expandCol = g.autoExpandColumn,
63843 //c.beginMeasure();
63845 if(!c.dom.offsetWidth){ // display:none?
63847 this.lockedWrap.show();
63848 this.mainWrap.show();
63853 var hasLock = this.cm.isLocked(0);
63855 var tbh = this.headerPanel.getHeight();
63856 var bbh = this.footerPanel.getHeight();
63859 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
63860 var newHeight = ch + c.getBorderWidth("tb");
63862 newHeight = Math.min(g.maxHeight, newHeight);
63864 c.setHeight(newHeight);
63868 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
63871 var s = this.scroller;
63873 var csize = c.getSize(true);
63875 this.el.setSize(csize.width, csize.height);
63877 this.headerPanel.setWidth(csize.width);
63878 this.footerPanel.setWidth(csize.width);
63880 var hdHeight = this.mainHd.getHeight();
63881 var vw = csize.width;
63882 var vh = csize.height - (tbh + bbh);
63886 var bt = this.getBodyTable();
63888 if(cm.getLockedCount() == cm.config.length){
63889 bt = this.getLockedTable();
63892 var ltWidth = hasLock ?
63893 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
63895 var scrollHeight = bt.offsetHeight;
63896 var scrollWidth = ltWidth + bt.offsetWidth;
63897 var vscroll = false, hscroll = false;
63899 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
63901 var lw = this.lockedWrap, mw = this.mainWrap;
63902 var lb = this.lockedBody, mb = this.mainBody;
63904 setTimeout(function(){
63905 var t = s.dom.offsetTop;
63906 var w = s.dom.clientWidth,
63907 h = s.dom.clientHeight;
63910 lw.setSize(ltWidth, h);
63912 mw.setLeftTop(ltWidth, t);
63913 mw.setSize(w-ltWidth, h);
63915 lb.setHeight(h-hdHeight);
63916 mb.setHeight(h-hdHeight);
63918 if(is2ndPass !== true && !gv.userResized && expandCol){
63919 // high speed resize without full column calculation
63921 var ci = cm.getIndexById(expandCol);
63923 ci = cm.findColumnIndex(expandCol);
63925 ci = Math.max(0, ci); // make sure it's got at least the first col.
63926 var expandId = cm.getColumnId(ci);
63927 var tw = cm.getTotalWidth(false);
63928 var currentWidth = cm.getColumnWidth(ci);
63929 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
63930 if(currentWidth != cw){
63931 cm.setColumnWidth(ci, cw, true);
63932 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
63933 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
63934 gv.updateSplitters();
63935 gv.layout(false, true);
63947 onWindowResize : function(){
63948 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
63954 appendFooter : function(parentEl){
63958 sortAscText : "Sort Ascending",
63959 sortDescText : "Sort Descending",
63960 lockText : "Lock Column",
63961 unlockText : "Unlock Column",
63962 columnsText : "Columns",
63964 columnsWiderText : "Wider",
63965 columnsNarrowText : "Thinner"
63969 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
63970 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
63971 this.proxy.el.addClass('x-grid3-col-dd');
63974 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
63975 handleMouseDown : function(e){
63979 callHandleMouseDown : function(e){
63980 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
63985 * Ext JS Library 1.1.1
63986 * Copyright(c) 2006-2007, Ext JS, LLC.
63988 * Originally Released Under LGPL - original licence link has changed is not relivant.
63991 * <script type="text/javascript">
63994 * @extends Roo.dd.DDProxy
63995 * @class Roo.grid.SplitDragZone
63996 * Support for Column Header resizing
63998 * @param {Object} config
64001 // This is a support class used internally by the Grid components
64002 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64004 this.view = grid.getView();
64005 this.proxy = this.view.resizeProxy;
64006 Roo.grid.SplitDragZone.superclass.constructor.call(
64009 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64011 dragElId : Roo.id(this.proxy.dom),
64016 this.setHandleElId(Roo.id(hd));
64017 if (hd2 !== false) {
64018 this.setOuterHandleElId(Roo.id(hd2));
64021 this.scroll = false;
64023 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64024 fly: Roo.Element.fly,
64026 b4StartDrag : function(x, y){
64027 this.view.headersDisabled = true;
64028 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64029 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64031 this.proxy.setHeight(h);
64033 // for old system colWidth really stored the actual width?
64034 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64035 // which in reality did not work.. - it worked only for fixed sizes
64036 // for resizable we need to use actual sizes.
64037 var w = this.cm.getColumnWidth(this.cellIndex);
64038 if (!this.view.mainWrap) {
64040 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64045 // this was w-this.grid.minColumnWidth;
64046 // doesnt really make sense? - w = thie curren width or the rendered one?
64047 var minw = Math.max(w-this.grid.minColumnWidth, 0);
64048 this.resetConstraints();
64049 this.setXConstraint(minw, 1000);
64050 this.setYConstraint(0, 0);
64051 this.minX = x - minw;
64052 this.maxX = x + 1000;
64054 if (!this.view.mainWrap) { // this is Bootstrap code..
64055 this.getDragEl().style.display='block';
64058 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64062 handleMouseDown : function(e){
64063 ev = Roo.EventObject.setEvent(e);
64064 var t = this.fly(ev.getTarget());
64065 if(t.hasClass("x-grid-split")){
64066 this.cellIndex = this.view.getCellIndex(t.dom);
64067 this.split = t.dom;
64068 this.cm = this.grid.colModel;
64069 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64070 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64075 endDrag : function(e){
64076 this.view.headersDisabled = false;
64077 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64078 var diff = endX - this.startPos;
64080 var w = this.cm.getColumnWidth(this.cellIndex);
64081 if (!this.view.mainWrap) {
64084 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64087 autoOffset : function(){
64088 this.setDelta(0,0);
64092 * Ext JS Library 1.1.1
64093 * Copyright(c) 2006-2007, Ext JS, LLC.
64095 * Originally Released Under LGPL - original licence link has changed is not relivant.
64098 * <script type="text/javascript">
64102 // This is a support class used internally by the Grid components
64103 Roo.grid.GridDragZone = function(grid, config){
64104 this.view = grid.getView();
64105 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64106 if(this.view.lockedBody){
64107 this.setHandleElId(Roo.id(this.view.mainBody.dom));
64108 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64110 this.scroll = false;
64112 this.ddel = document.createElement('div');
64113 this.ddel.className = 'x-grid-dd-wrap';
64116 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64117 ddGroup : "GridDD",
64119 getDragData : function(e){
64120 var t = Roo.lib.Event.getTarget(e);
64121 var rowIndex = this.view.findRowIndex(t);
64122 var sm = this.grid.selModel;
64124 //Roo.log(rowIndex);
64126 if (sm.getSelectedCell) {
64127 // cell selection..
64128 if (!sm.getSelectedCell()) {
64131 if (rowIndex != sm.getSelectedCell()[0]) {
64136 if (sm.getSelections && sm.getSelections().length < 1) {
64141 // before it used to all dragging of unseleted... - now we dont do that.
64142 if(rowIndex !== false){
64147 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64149 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64152 if (e.hasModifier()){
64153 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64156 Roo.log("getDragData");
64161 rowIndex: rowIndex,
64162 selections: sm.getSelections ? sm.getSelections() : (
64163 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64170 onInitDrag : function(e){
64171 var data = this.dragData;
64172 this.ddel.innerHTML = this.grid.getDragDropText();
64173 this.proxy.update(this.ddel);
64174 // fire start drag?
64177 afterRepair : function(){
64178 this.dragging = false;
64181 getRepairXY : function(e, data){
64185 onEndDrag : function(data, e){
64189 onValidDrop : function(dd, e, id){
64194 beforeInvalidDrop : function(e, id){
64199 * Ext JS Library 1.1.1
64200 * Copyright(c) 2006-2007, Ext JS, LLC.
64202 * Originally Released Under LGPL - original licence link has changed is not relivant.
64205 * <script type="text/javascript">
64210 * @class Roo.grid.ColumnModel
64211 * @extends Roo.util.Observable
64212 * This is the default implementation of a ColumnModel used by the Grid. It defines
64213 * the columns in the grid.
64216 var colModel = new Roo.grid.ColumnModel([
64217 {header: "Ticker", width: 60, sortable: true, locked: true},
64218 {header: "Company Name", width: 150, sortable: true},
64219 {header: "Market Cap.", width: 100, sortable: true},
64220 {header: "$ Sales", width: 100, sortable: true, renderer: money},
64221 {header: "Employees", width: 100, sortable: true, resizable: false}
64226 * The config options listed for this class are options which may appear in each
64227 * individual column definition.
64228 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64230 * @param {Object} config An Array of column config objects. See this class's
64231 * config objects for details.
64233 Roo.grid.ColumnModel = function(config){
64235 * The config passed into the constructor
64237 this.config = []; //config;
64240 // if no id, create one
64241 // if the column does not have a dataIndex mapping,
64242 // map it to the order it is in the config
64243 for(var i = 0, len = config.length; i < len; i++){
64244 this.addColumn(config[i]);
64249 * The width of columns which have no width specified (defaults to 100)
64252 this.defaultWidth = 100;
64255 * Default sortable of columns which have no sortable specified (defaults to false)
64258 this.defaultSortable = false;
64262 * @event widthchange
64263 * Fires when the width of a column changes.
64264 * @param {ColumnModel} this
64265 * @param {Number} columnIndex The column index
64266 * @param {Number} newWidth The new width
64268 "widthchange": true,
64270 * @event headerchange
64271 * Fires when the text of a header changes.
64272 * @param {ColumnModel} this
64273 * @param {Number} columnIndex The column index
64274 * @param {Number} newText The new header text
64276 "headerchange": true,
64278 * @event hiddenchange
64279 * Fires when a column is hidden or "unhidden".
64280 * @param {ColumnModel} this
64281 * @param {Number} columnIndex The column index
64282 * @param {Boolean} hidden true if hidden, false otherwise
64284 "hiddenchange": true,
64286 * @event columnmoved
64287 * Fires when a column is moved.
64288 * @param {ColumnModel} this
64289 * @param {Number} oldIndex
64290 * @param {Number} newIndex
64292 "columnmoved" : true,
64294 * @event columlockchange
64295 * Fires when a column's locked state is changed
64296 * @param {ColumnModel} this
64297 * @param {Number} colIndex
64298 * @param {Boolean} locked true if locked
64300 "columnlockchange" : true
64302 Roo.grid.ColumnModel.superclass.constructor.call(this);
64304 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64306 * @cfg {String} header The header text to display in the Grid view.
64309 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64312 * @cfg {String} smHeader Header at Bootsrap Small width
64315 * @cfg {String} mdHeader Header at Bootsrap Medium width
64318 * @cfg {String} lgHeader Header at Bootsrap Large width
64321 * @cfg {String} xlHeader Header at Bootsrap extra Large width
64324 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
64325 * {@link Roo.data.Record} definition from which to draw the column's value. If not
64326 * specified, the column's index is used as an index into the Record's data Array.
64329 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
64330 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64333 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
64334 * Defaults to the value of the {@link #defaultSortable} property.
64335 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64338 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
64341 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
64344 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
64347 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
64350 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
64351 * given the cell's data value. See {@link #setRenderer}. If not specified, the
64352 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64353 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64356 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
64359 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
64362 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
64365 * @cfg {String} cursor (Optional)
64368 * @cfg {String} tooltip (Optional)
64371 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
64374 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
64377 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
64380 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
64383 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
64386 * Returns the id of the column at the specified index.
64387 * @param {Number} index The column index
64388 * @return {String} the id
64390 getColumnId : function(index){
64391 return this.config[index].id;
64395 * Returns the column for a specified id.
64396 * @param {String} id The column id
64397 * @return {Object} the column
64399 getColumnById : function(id){
64400 return this.lookup[id];
64405 * Returns the column Object for a specified dataIndex.
64406 * @param {String} dataIndex The column dataIndex
64407 * @return {Object|Boolean} the column or false if not found
64409 getColumnByDataIndex: function(dataIndex){
64410 var index = this.findColumnIndex(dataIndex);
64411 return index > -1 ? this.config[index] : false;
64415 * Returns the index for a specified column id.
64416 * @param {String} id The column id
64417 * @return {Number} the index, or -1 if not found
64419 getIndexById : function(id){
64420 for(var i = 0, len = this.config.length; i < len; i++){
64421 if(this.config[i].id == id){
64429 * Returns the index for a specified column dataIndex.
64430 * @param {String} dataIndex The column dataIndex
64431 * @return {Number} the index, or -1 if not found
64434 findColumnIndex : function(dataIndex){
64435 for(var i = 0, len = this.config.length; i < len; i++){
64436 if(this.config[i].dataIndex == dataIndex){
64444 moveColumn : function(oldIndex, newIndex){
64445 var c = this.config[oldIndex];
64446 this.config.splice(oldIndex, 1);
64447 this.config.splice(newIndex, 0, c);
64448 this.dataMap = null;
64449 this.fireEvent("columnmoved", this, oldIndex, newIndex);
64452 isLocked : function(colIndex){
64453 return this.config[colIndex].locked === true;
64456 setLocked : function(colIndex, value, suppressEvent){
64457 if(this.isLocked(colIndex) == value){
64460 this.config[colIndex].locked = value;
64461 if(!suppressEvent){
64462 this.fireEvent("columnlockchange", this, colIndex, value);
64466 getTotalLockedWidth : function(){
64467 var totalWidth = 0;
64468 for(var i = 0; i < this.config.length; i++){
64469 if(this.isLocked(i) && !this.isHidden(i)){
64470 this.totalWidth += this.getColumnWidth(i);
64476 getLockedCount : function(){
64477 for(var i = 0, len = this.config.length; i < len; i++){
64478 if(!this.isLocked(i)){
64483 return this.config.length;
64487 * Returns the number of columns.
64490 getColumnCount : function(visibleOnly){
64491 if(visibleOnly === true){
64493 for(var i = 0, len = this.config.length; i < len; i++){
64494 if(!this.isHidden(i)){
64500 return this.config.length;
64504 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64505 * @param {Function} fn
64506 * @param {Object} scope (optional)
64507 * @return {Array} result
64509 getColumnsBy : function(fn, scope){
64511 for(var i = 0, len = this.config.length; i < len; i++){
64512 var c = this.config[i];
64513 if(fn.call(scope||this, c, i) === true){
64521 * Returns true if the specified column is sortable.
64522 * @param {Number} col The column index
64523 * @return {Boolean}
64525 isSortable : function(col){
64526 if(typeof this.config[col].sortable == "undefined"){
64527 return this.defaultSortable;
64529 return this.config[col].sortable;
64533 * Returns the rendering (formatting) function defined for the column.
64534 * @param {Number} col The column index.
64535 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64537 getRenderer : function(col){
64538 if(!this.config[col].renderer){
64539 return Roo.grid.ColumnModel.defaultRenderer;
64541 return this.config[col].renderer;
64545 * Sets the rendering (formatting) function for a column.
64546 * @param {Number} col The column index
64547 * @param {Function} fn The function to use to process the cell's raw data
64548 * to return HTML markup for the grid view. The render function is called with
64549 * the following parameters:<ul>
64550 * <li>Data value.</li>
64551 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64552 * <li>css A CSS style string to apply to the table cell.</li>
64553 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
64554 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
64555 * <li>Row index</li>
64556 * <li>Column index</li>
64557 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
64559 setRenderer : function(col, fn){
64560 this.config[col].renderer = fn;
64564 * Returns the width for the specified column.
64565 * @param {Number} col The column index
64566 * @param (optional) {String} gridSize bootstrap width size.
64569 getColumnWidth : function(col, gridSize)
64571 var cfg = this.config[col];
64573 if (typeof(gridSize) == 'undefined') {
64574 return cfg.width * 1 || this.defaultWidth;
64576 if (gridSize === false) { // if we set it..
64577 return cfg.width || false;
64579 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
64581 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
64582 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
64585 return cfg[ sizes[i] ];
64592 * Sets the width for a column.
64593 * @param {Number} col The column index
64594 * @param {Number} width The new width
64596 setColumnWidth : function(col, width, suppressEvent){
64597 this.config[col].width = width;
64598 this.totalWidth = null;
64599 if(!suppressEvent){
64600 this.fireEvent("widthchange", this, col, width);
64605 * Returns the total width of all columns.
64606 * @param {Boolean} includeHidden True to include hidden column widths
64609 getTotalWidth : function(includeHidden){
64610 if(!this.totalWidth){
64611 this.totalWidth = 0;
64612 for(var i = 0, len = this.config.length; i < len; i++){
64613 if(includeHidden || !this.isHidden(i)){
64614 this.totalWidth += this.getColumnWidth(i);
64618 return this.totalWidth;
64622 * Returns the header for the specified column.
64623 * @param {Number} col The column index
64626 getColumnHeader : function(col){
64627 return this.config[col].header;
64631 * Sets the header for a column.
64632 * @param {Number} col The column index
64633 * @param {String} header The new header
64635 setColumnHeader : function(col, header){
64636 this.config[col].header = header;
64637 this.fireEvent("headerchange", this, col, header);
64641 * Returns the tooltip for the specified column.
64642 * @param {Number} col The column index
64645 getColumnTooltip : function(col){
64646 return this.config[col].tooltip;
64649 * Sets the tooltip for a column.
64650 * @param {Number} col The column index
64651 * @param {String} tooltip The new tooltip
64653 setColumnTooltip : function(col, tooltip){
64654 this.config[col].tooltip = tooltip;
64658 * Returns the dataIndex for the specified column.
64659 * @param {Number} col The column index
64662 getDataIndex : function(col){
64663 return this.config[col].dataIndex;
64667 * Sets the dataIndex for a column.
64668 * @param {Number} col The column index
64669 * @param {Number} dataIndex The new dataIndex
64671 setDataIndex : function(col, dataIndex){
64672 this.config[col].dataIndex = dataIndex;
64678 * Returns true if the cell is editable.
64679 * @param {Number} colIndex The column index
64680 * @param {Number} rowIndex The row index - this is nto actually used..?
64681 * @return {Boolean}
64683 isCellEditable : function(colIndex, rowIndex){
64684 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
64688 * Returns the editor defined for the cell/column.
64689 * return false or null to disable editing.
64690 * @param {Number} colIndex The column index
64691 * @param {Number} rowIndex The row index
64694 getCellEditor : function(colIndex, rowIndex){
64695 return this.config[colIndex].editor;
64699 * Sets if a column is editable.
64700 * @param {Number} col The column index
64701 * @param {Boolean} editable True if the column is editable
64703 setEditable : function(col, editable){
64704 this.config[col].editable = editable;
64709 * Returns true if the column is hidden.
64710 * @param {Number} colIndex The column index
64711 * @return {Boolean}
64713 isHidden : function(colIndex){
64714 return this.config[colIndex].hidden;
64719 * Returns true if the column width cannot be changed
64721 isFixed : function(colIndex){
64722 return this.config[colIndex].fixed;
64726 * Returns true if the column can be resized
64727 * @return {Boolean}
64729 isResizable : function(colIndex){
64730 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
64733 * Sets if a column is hidden.
64734 * @param {Number} colIndex The column index
64735 * @param {Boolean} hidden True if the column is hidden
64737 setHidden : function(colIndex, hidden){
64738 this.config[colIndex].hidden = hidden;
64739 this.totalWidth = null;
64740 this.fireEvent("hiddenchange", this, colIndex, hidden);
64744 * Sets the editor for a column.
64745 * @param {Number} col The column index
64746 * @param {Object} editor The editor object
64748 setEditor : function(col, editor){
64749 this.config[col].editor = editor;
64752 * Add a column (experimental...) - defaults to adding to the end..
64753 * @param {Object} config
64755 addColumn : function(c)
64758 var i = this.config.length;
64759 this.config[i] = c;
64761 if(typeof c.dataIndex == "undefined"){
64764 if(typeof c.renderer == "string"){
64765 c.renderer = Roo.util.Format[c.renderer];
64767 if(typeof c.id == "undefined"){
64770 if(c.editor && c.editor.xtype){
64771 c.editor = Roo.factory(c.editor, Roo.grid);
64773 if(c.editor && c.editor.isFormField){
64774 c.editor = new Roo.grid.GridEditor(c.editor);
64776 this.lookup[c.id] = c;
64781 Roo.grid.ColumnModel.defaultRenderer = function(value)
64783 if(typeof value == "object") {
64786 if(typeof value == "string" && value.length < 1){
64790 return String.format("{0}", value);
64793 // Alias for backwards compatibility
64794 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
64797 * Ext JS Library 1.1.1
64798 * Copyright(c) 2006-2007, Ext JS, LLC.
64800 * Originally Released Under LGPL - original licence link has changed is not relivant.
64803 * <script type="text/javascript">
64807 * @class Roo.grid.AbstractSelectionModel
64808 * @extends Roo.util.Observable
64810 * Abstract base class for grid SelectionModels. It provides the interface that should be
64811 * implemented by descendant classes. This class should not be directly instantiated.
64814 Roo.grid.AbstractSelectionModel = function(){
64815 this.locked = false;
64816 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
64819 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
64820 /** @ignore Called by the grid automatically. Do not call directly. */
64821 init : function(grid){
64827 * Locks the selections.
64830 this.locked = true;
64834 * Unlocks the selections.
64836 unlock : function(){
64837 this.locked = false;
64841 * Returns true if the selections are locked.
64842 * @return {Boolean}
64844 isLocked : function(){
64845 return this.locked;
64849 * Ext JS Library 1.1.1
64850 * Copyright(c) 2006-2007, Ext JS, LLC.
64852 * Originally Released Under LGPL - original licence link has changed is not relivant.
64855 * <script type="text/javascript">
64858 * @extends Roo.grid.AbstractSelectionModel
64859 * @class Roo.grid.RowSelectionModel
64860 * The default SelectionModel used by {@link Roo.grid.Grid}.
64861 * It supports multiple selections and keyboard selection/navigation.
64863 * @param {Object} config
64865 Roo.grid.RowSelectionModel = function(config){
64866 Roo.apply(this, config);
64867 this.selections = new Roo.util.MixedCollection(false, function(o){
64872 this.lastActive = false;
64876 * @event selectionchange
64877 * Fires when the selection changes
64878 * @param {SelectionModel} this
64880 "selectionchange" : true,
64882 * @event afterselectionchange
64883 * Fires after the selection changes (eg. by key press or clicking)
64884 * @param {SelectionModel} this
64886 "afterselectionchange" : true,
64888 * @event beforerowselect
64889 * Fires when a row is selected being selected, return false to cancel.
64890 * @param {SelectionModel} this
64891 * @param {Number} rowIndex The selected index
64892 * @param {Boolean} keepExisting False if other selections will be cleared
64894 "beforerowselect" : true,
64897 * Fires when a row is selected.
64898 * @param {SelectionModel} this
64899 * @param {Number} rowIndex The selected index
64900 * @param {Roo.data.Record} r The record
64902 "rowselect" : true,
64904 * @event rowdeselect
64905 * Fires when a row is deselected.
64906 * @param {SelectionModel} this
64907 * @param {Number} rowIndex The selected index
64909 "rowdeselect" : true
64911 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
64912 this.locked = false;
64915 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
64917 * @cfg {Boolean} singleSelect
64918 * True to allow selection of only one row at a time (defaults to false)
64920 singleSelect : false,
64923 initEvents : function(){
64925 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
64926 this.grid.on("mousedown", this.handleMouseDown, this);
64927 }else{ // allow click to work like normal
64928 this.grid.on("rowclick", this.handleDragableRowClick, this);
64930 // bootstrap does not have a view..
64931 var view = this.grid.view ? this.grid.view : this.grid;
64932 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
64933 "up" : function(e){
64935 this.selectPrevious(e.shiftKey);
64936 }else if(this.last !== false && this.lastActive !== false){
64937 var last = this.last;
64938 this.selectRange(this.last, this.lastActive-1);
64939 view.focusRow(this.lastActive);
64940 if(last !== false){
64944 this.selectFirstRow();
64946 this.fireEvent("afterselectionchange", this);
64948 "down" : function(e){
64950 this.selectNext(e.shiftKey);
64951 }else if(this.last !== false && this.lastActive !== false){
64952 var last = this.last;
64953 this.selectRange(this.last, this.lastActive+1);
64954 view.focusRow(this.lastActive);
64955 if(last !== false){
64959 this.selectFirstRow();
64961 this.fireEvent("afterselectionchange", this);
64967 view.on("refresh", this.onRefresh, this);
64968 view.on("rowupdated", this.onRowUpdated, this);
64969 view.on("rowremoved", this.onRemove, this);
64973 onRefresh : function(){
64974 var ds = this.grid.ds, i, v = this.grid.view;
64975 var s = this.selections;
64976 s.each(function(r){
64977 if((i = ds.indexOfId(r.id)) != -1){
64979 s.add(ds.getAt(i)); // updating the selection relate data
64987 onRemove : function(v, index, r){
64988 this.selections.remove(r);
64992 onRowUpdated : function(v, index, r){
64993 if(this.isSelected(r)){
64994 v.onRowSelect(index);
65000 * @param {Array} records The records to select
65001 * @param {Boolean} keepExisting (optional) True to keep existing selections
65003 selectRecords : function(records, keepExisting){
65005 this.clearSelections();
65007 var ds = this.grid.ds;
65008 for(var i = 0, len = records.length; i < len; i++){
65009 this.selectRow(ds.indexOf(records[i]), true);
65014 * Gets the number of selected rows.
65017 getCount : function(){
65018 return this.selections.length;
65022 * Selects the first row in the grid.
65024 selectFirstRow : function(){
65029 * Select the last row.
65030 * @param {Boolean} keepExisting (optional) True to keep existing selections
65032 selectLastRow : function(keepExisting){
65033 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65037 * Selects the row immediately following the last selected row.
65038 * @param {Boolean} keepExisting (optional) True to keep existing selections
65040 selectNext : function(keepExisting){
65041 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65042 this.selectRow(this.last+1, keepExisting);
65043 var view = this.grid.view ? this.grid.view : this.grid;
65044 view.focusRow(this.last);
65049 * Selects the row that precedes the last selected row.
65050 * @param {Boolean} keepExisting (optional) True to keep existing selections
65052 selectPrevious : function(keepExisting){
65054 this.selectRow(this.last-1, keepExisting);
65055 var view = this.grid.view ? this.grid.view : this.grid;
65056 view.focusRow(this.last);
65061 * Returns the selected records
65062 * @return {Array} Array of selected records
65064 getSelections : function(){
65065 return [].concat(this.selections.items);
65069 * Returns the first selected record.
65072 getSelected : function(){
65073 return this.selections.itemAt(0);
65078 * Clears all selections.
65080 clearSelections : function(fast){
65085 var ds = this.grid.ds;
65086 var s = this.selections;
65087 s.each(function(r){
65088 this.deselectRow(ds.indexOfId(r.id));
65092 this.selections.clear();
65099 * Selects all rows.
65101 selectAll : function(){
65105 this.selections.clear();
65106 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65107 this.selectRow(i, true);
65112 * Returns True if there is a selection.
65113 * @return {Boolean}
65115 hasSelection : function(){
65116 return this.selections.length > 0;
65120 * Returns True if the specified row is selected.
65121 * @param {Number/Record} record The record or index of the record to check
65122 * @return {Boolean}
65124 isSelected : function(index){
65125 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65126 return (r && this.selections.key(r.id) ? true : false);
65130 * Returns True if the specified record id is selected.
65131 * @param {String} id The id of record to check
65132 * @return {Boolean}
65134 isIdSelected : function(id){
65135 return (this.selections.key(id) ? true : false);
65139 handleMouseDown : function(e, t)
65141 var view = this.grid.view ? this.grid.view : this.grid;
65143 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65146 if(e.shiftKey && this.last !== false){
65147 var last = this.last;
65148 this.selectRange(last, rowIndex, e.ctrlKey);
65149 this.last = last; // reset the last
65150 view.focusRow(rowIndex);
65152 var isSelected = this.isSelected(rowIndex);
65153 if(e.button !== 0 && isSelected){
65154 view.focusRow(rowIndex);
65155 }else if(e.ctrlKey && isSelected){
65156 this.deselectRow(rowIndex);
65157 }else if(!isSelected){
65158 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65159 view.focusRow(rowIndex);
65162 this.fireEvent("afterselectionchange", this);
65165 handleDragableRowClick : function(grid, rowIndex, e)
65167 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65168 this.selectRow(rowIndex, false);
65169 var view = this.grid.view ? this.grid.view : this.grid;
65170 view.focusRow(rowIndex);
65171 this.fireEvent("afterselectionchange", this);
65176 * Selects multiple rows.
65177 * @param {Array} rows Array of the indexes of the row to select
65178 * @param {Boolean} keepExisting (optional) True to keep existing selections
65180 selectRows : function(rows, keepExisting){
65182 this.clearSelections();
65184 for(var i = 0, len = rows.length; i < len; i++){
65185 this.selectRow(rows[i], true);
65190 * Selects a range of rows. All rows in between startRow and endRow are also selected.
65191 * @param {Number} startRow The index of the first row in the range
65192 * @param {Number} endRow The index of the last row in the range
65193 * @param {Boolean} keepExisting (optional) True to retain existing selections
65195 selectRange : function(startRow, endRow, keepExisting){
65200 this.clearSelections();
65202 if(startRow <= endRow){
65203 for(var i = startRow; i <= endRow; i++){
65204 this.selectRow(i, true);
65207 for(var i = startRow; i >= endRow; i--){
65208 this.selectRow(i, true);
65214 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65215 * @param {Number} startRow The index of the first row in the range
65216 * @param {Number} endRow The index of the last row in the range
65218 deselectRange : function(startRow, endRow, preventViewNotify){
65222 for(var i = startRow; i <= endRow; i++){
65223 this.deselectRow(i, preventViewNotify);
65229 * @param {Number} row The index of the row to select
65230 * @param {Boolean} keepExisting (optional) True to keep existing selections
65232 selectRow : function(index, keepExisting, preventViewNotify){
65233 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65236 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65237 if(!keepExisting || this.singleSelect){
65238 this.clearSelections();
65240 var r = this.grid.ds.getAt(index);
65241 this.selections.add(r);
65242 this.last = this.lastActive = index;
65243 if(!preventViewNotify){
65244 var view = this.grid.view ? this.grid.view : this.grid;
65245 view.onRowSelect(index);
65247 this.fireEvent("rowselect", this, index, r);
65248 this.fireEvent("selectionchange", this);
65254 * @param {Number} row The index of the row to deselect
65256 deselectRow : function(index, preventViewNotify){
65260 if(this.last == index){
65263 if(this.lastActive == index){
65264 this.lastActive = false;
65266 var r = this.grid.ds.getAt(index);
65267 this.selections.remove(r);
65268 if(!preventViewNotify){
65269 var view = this.grid.view ? this.grid.view : this.grid;
65270 view.onRowDeselect(index);
65272 this.fireEvent("rowdeselect", this, index);
65273 this.fireEvent("selectionchange", this);
65277 restoreLast : function(){
65279 this.last = this._last;
65284 acceptsNav : function(row, col, cm){
65285 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65289 onEditorKey : function(field, e){
65290 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65295 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65297 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65299 }else if(k == e.ENTER && !e.ctrlKey){
65303 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65305 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65307 }else if(k == e.ESC){
65311 g.startEditing(newCell[0], newCell[1]);
65316 * Ext JS Library 1.1.1
65317 * Copyright(c) 2006-2007, Ext JS, LLC.
65319 * Originally Released Under LGPL - original licence link has changed is not relivant.
65322 * <script type="text/javascript">
65325 * @class Roo.grid.CellSelectionModel
65326 * @extends Roo.grid.AbstractSelectionModel
65327 * This class provides the basic implementation for cell selection in a grid.
65329 * @param {Object} config The object containing the configuration of this model.
65330 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65332 Roo.grid.CellSelectionModel = function(config){
65333 Roo.apply(this, config);
65335 this.selection = null;
65339 * @event beforerowselect
65340 * Fires before a cell is selected.
65341 * @param {SelectionModel} this
65342 * @param {Number} rowIndex The selected row index
65343 * @param {Number} colIndex The selected cell index
65345 "beforecellselect" : true,
65347 * @event cellselect
65348 * Fires when a cell is selected.
65349 * @param {SelectionModel} this
65350 * @param {Number} rowIndex The selected row index
65351 * @param {Number} colIndex The selected cell index
65353 "cellselect" : true,
65355 * @event selectionchange
65356 * Fires when the active selection changes.
65357 * @param {SelectionModel} this
65358 * @param {Object} selection null for no selection or an object (o) with two properties
65360 <li>o.record: the record object for the row the selection is in</li>
65361 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65364 "selectionchange" : true,
65367 * Fires when the tab (or enter) was pressed on the last editable cell
65368 * You can use this to trigger add new row.
65369 * @param {SelectionModel} this
65373 * @event beforeeditnext
65374 * Fires before the next editable sell is made active
65375 * You can use this to skip to another cell or fire the tabend
65376 * if you set cell to false
65377 * @param {Object} eventdata object : { cell : [ row, col ] }
65379 "beforeeditnext" : true
65381 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65384 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
65386 enter_is_tab: false,
65389 initEvents : function(){
65390 this.grid.on("mousedown", this.handleMouseDown, this);
65391 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65392 var view = this.grid.view;
65393 view.on("refresh", this.onViewChange, this);
65394 view.on("rowupdated", this.onRowUpdated, this);
65395 view.on("beforerowremoved", this.clearSelections, this);
65396 view.on("beforerowsinserted", this.clearSelections, this);
65397 if(this.grid.isEditor){
65398 this.grid.on("beforeedit", this.beforeEdit, this);
65403 beforeEdit : function(e){
65404 this.select(e.row, e.column, false, true, e.record);
65408 onRowUpdated : function(v, index, r){
65409 if(this.selection && this.selection.record == r){
65410 v.onCellSelect(index, this.selection.cell[1]);
65415 onViewChange : function(){
65416 this.clearSelections(true);
65420 * Returns the currently selected cell,.
65421 * @return {Array} The selected cell (row, column) or null if none selected.
65423 getSelectedCell : function(){
65424 return this.selection ? this.selection.cell : null;
65428 * Clears all selections.
65429 * @param {Boolean} true to prevent the gridview from being notified about the change.
65431 clearSelections : function(preventNotify){
65432 var s = this.selection;
65434 if(preventNotify !== true){
65435 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65437 this.selection = null;
65438 this.fireEvent("selectionchange", this, null);
65443 * Returns true if there is a selection.
65444 * @return {Boolean}
65446 hasSelection : function(){
65447 return this.selection ? true : false;
65451 handleMouseDown : function(e, t){
65452 var v = this.grid.getView();
65453 if(this.isLocked()){
65456 var row = v.findRowIndex(t);
65457 var cell = v.findCellIndex(t);
65458 if(row !== false && cell !== false){
65459 this.select(row, cell);
65465 * @param {Number} rowIndex
65466 * @param {Number} collIndex
65468 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65469 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65470 this.clearSelections();
65471 r = r || this.grid.dataSource.getAt(rowIndex);
65474 cell : [rowIndex, colIndex]
65476 if(!preventViewNotify){
65477 var v = this.grid.getView();
65478 v.onCellSelect(rowIndex, colIndex);
65479 if(preventFocus !== true){
65480 v.focusCell(rowIndex, colIndex);
65483 this.fireEvent("cellselect", this, rowIndex, colIndex);
65484 this.fireEvent("selectionchange", this, this.selection);
65489 isSelectable : function(rowIndex, colIndex, cm){
65490 return !cm.isHidden(colIndex);
65494 handleKeyDown : function(e){
65495 //Roo.log('Cell Sel Model handleKeyDown');
65496 if(!e.isNavKeyPress()){
65499 var g = this.grid, s = this.selection;
65502 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
65504 this.select(cell[0], cell[1]);
65509 var walk = function(row, col, step){
65510 return g.walkCells(row, col, step, sm.isSelectable, sm);
65512 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65519 // handled by onEditorKey
65520 if (g.isEditor && g.editing) {
65524 newCell = walk(r, c-1, -1);
65526 newCell = walk(r, c+1, 1);
65531 newCell = walk(r+1, c, 1);
65535 newCell = walk(r-1, c, -1);
65539 newCell = walk(r, c+1, 1);
65543 newCell = walk(r, c-1, -1);
65548 if(g.isEditor && !g.editing){
65549 g.startEditing(r, c);
65558 this.select(newCell[0], newCell[1]);
65564 acceptsNav : function(row, col, cm){
65565 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65569 * @param {Number} field (not used) - as it's normally used as a listener
65570 * @param {Number} e - event - fake it by using
65572 * var e = Roo.EventObjectImpl.prototype;
65573 * e.keyCode = e.TAB
65577 onEditorKey : function(field, e){
65579 var k = e.getKey(),
65582 ed = g.activeEditor,
65584 ///Roo.log('onEditorKey' + k);
65587 if (this.enter_is_tab && k == e.ENTER) {
65593 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65595 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65601 } else if(k == e.ENTER && !e.ctrlKey){
65604 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65606 } else if(k == e.ESC){
65611 var ecall = { cell : newCell, forward : forward };
65612 this.fireEvent('beforeeditnext', ecall );
65613 newCell = ecall.cell;
65614 forward = ecall.forward;
65618 //Roo.log('next cell after edit');
65619 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
65620 } else if (forward) {
65621 // tabbed past last
65622 this.fireEvent.defer(100, this, ['tabend',this]);
65627 * Ext JS Library 1.1.1
65628 * Copyright(c) 2006-2007, Ext JS, LLC.
65630 * Originally Released Under LGPL - original licence link has changed is not relivant.
65633 * <script type="text/javascript">
65637 * @class Roo.grid.EditorGrid
65638 * @extends Roo.grid.Grid
65639 * Class for creating and editable grid.
65640 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
65641 * The container MUST have some type of size defined for the grid to fill. The container will be
65642 * automatically set to position relative if it isn't already.
65643 * @param {Object} dataSource The data model to bind to
65644 * @param {Object} colModel The column model with info about this grid's columns
65646 Roo.grid.EditorGrid = function(container, config){
65647 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
65648 this.getGridEl().addClass("xedit-grid");
65650 if(!this.selModel){
65651 this.selModel = new Roo.grid.CellSelectionModel();
65654 this.activeEditor = null;
65658 * @event beforeedit
65659 * Fires before cell editing is triggered. The edit event object has the following properties <br />
65660 * <ul style="padding:5px;padding-left:16px;">
65661 * <li>grid - This grid</li>
65662 * <li>record - The record being edited</li>
65663 * <li>field - The field name being edited</li>
65664 * <li>value - The value for the field being edited.</li>
65665 * <li>row - The grid row index</li>
65666 * <li>column - The grid column index</li>
65667 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65669 * @param {Object} e An edit event (see above for description)
65671 "beforeedit" : true,
65674 * Fires after a cell is edited. <br />
65675 * <ul style="padding:5px;padding-left:16px;">
65676 * <li>grid - This grid</li>
65677 * <li>record - The record being edited</li>
65678 * <li>field - The field name being edited</li>
65679 * <li>value - The value being set</li>
65680 * <li>originalValue - The original value for the field, before the edit.</li>
65681 * <li>row - The grid row index</li>
65682 * <li>column - The grid column index</li>
65684 * @param {Object} e An edit event (see above for description)
65686 "afteredit" : true,
65688 * @event validateedit
65689 * Fires after a cell is edited, but before the value is set in the record.
65690 * You can use this to modify the value being set in the field, Return false
65691 * to cancel the change. The edit event object has the following properties <br />
65692 * <ul style="padding:5px;padding-left:16px;">
65693 * <li>editor - This editor</li>
65694 * <li>grid - This grid</li>
65695 * <li>record - The record being edited</li>
65696 * <li>field - The field name being edited</li>
65697 * <li>value - The value being set</li>
65698 * <li>originalValue - The original value for the field, before the edit.</li>
65699 * <li>row - The grid row index</li>
65700 * <li>column - The grid column index</li>
65701 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65703 * @param {Object} e An edit event (see above for description)
65705 "validateedit" : true
65707 this.on("bodyscroll", this.stopEditing, this);
65708 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
65711 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
65713 * @cfg {Number} clicksToEdit
65714 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
65721 trackMouseOver: false, // causes very odd FF errors
65723 onCellDblClick : function(g, row, col){
65724 this.startEditing(row, col);
65727 onEditComplete : function(ed, value, startValue){
65728 this.editing = false;
65729 this.activeEditor = null;
65730 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
65732 var field = this.colModel.getDataIndex(ed.col);
65737 originalValue: startValue,
65744 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
65747 if(String(value) !== String(startValue)){
65749 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
65750 r.set(field, e.value);
65751 // if we are dealing with a combo box..
65752 // then we also set the 'name' colum to be the displayField
65753 if (ed.field.displayField && ed.field.name) {
65754 r.set(ed.field.name, ed.field.el.dom.value);
65757 delete e.cancel; //?? why!!!
65758 this.fireEvent("afteredit", e);
65761 this.fireEvent("afteredit", e); // always fire it!
65763 this.view.focusCell(ed.row, ed.col);
65767 * Starts editing the specified for the specified row/column
65768 * @param {Number} rowIndex
65769 * @param {Number} colIndex
65771 startEditing : function(row, col){
65772 this.stopEditing();
65773 if(this.colModel.isCellEditable(col, row)){
65774 this.view.ensureVisible(row, col, true);
65776 var r = this.dataSource.getAt(row);
65777 var field = this.colModel.getDataIndex(col);
65778 var cell = Roo.get(this.view.getCell(row,col));
65783 value: r.data[field],
65788 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
65789 this.editing = true;
65790 var ed = this.colModel.getCellEditor(col, row);
65796 ed.render(ed.parentEl || document.body);
65802 (function(){ // complex but required for focus issues in safari, ie and opera
65806 ed.on("complete", this.onEditComplete, this, {single: true});
65807 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
65808 this.activeEditor = ed;
65809 var v = r.data[field];
65810 ed.startEdit(this.view.getCell(row, col), v);
65811 // combo's with 'displayField and name set
65812 if (ed.field.displayField && ed.field.name) {
65813 ed.field.el.dom.value = r.data[ed.field.name];
65817 }).defer(50, this);
65823 * Stops any active editing
65825 stopEditing : function(){
65826 if(this.activeEditor){
65827 this.activeEditor.completeEdit();
65829 this.activeEditor = null;
65833 * Called to get grid's drag proxy text, by default returns this.ddText.
65836 getDragDropText : function(){
65837 var count = this.selModel.getSelectedCell() ? 1 : 0;
65838 return String.format(this.ddText, count, count == 1 ? '' : 's');
65843 * Ext JS Library 1.1.1
65844 * Copyright(c) 2006-2007, Ext JS, LLC.
65846 * Originally Released Under LGPL - original licence link has changed is not relivant.
65849 * <script type="text/javascript">
65852 // private - not really -- you end up using it !
65853 // This is a support class used internally by the Grid components
65856 * @class Roo.grid.GridEditor
65857 * @extends Roo.Editor
65858 * Class for creating and editable grid elements.
65859 * @param {Object} config any settings (must include field)
65861 Roo.grid.GridEditor = function(field, config){
65862 if (!config && field.field) {
65864 field = Roo.factory(config.field, Roo.form);
65866 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
65867 field.monitorTab = false;
65870 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
65873 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
65876 alignment: "tl-tl",
65879 cls: "x-small-editor x-grid-editor",
65884 * Ext JS Library 1.1.1
65885 * Copyright(c) 2006-2007, Ext JS, LLC.
65887 * Originally Released Under LGPL - original licence link has changed is not relivant.
65890 * <script type="text/javascript">
65895 Roo.grid.PropertyRecord = Roo.data.Record.create([
65896 {name:'name',type:'string'}, 'value'
65900 Roo.grid.PropertyStore = function(grid, source){
65902 this.store = new Roo.data.Store({
65903 recordType : Roo.grid.PropertyRecord
65905 this.store.on('update', this.onUpdate, this);
65907 this.setSource(source);
65909 Roo.grid.PropertyStore.superclass.constructor.call(this);
65914 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
65915 setSource : function(o){
65917 this.store.removeAll();
65920 if(this.isEditableValue(o[k])){
65921 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
65924 this.store.loadRecords({records: data}, {}, true);
65927 onUpdate : function(ds, record, type){
65928 if(type == Roo.data.Record.EDIT){
65929 var v = record.data['value'];
65930 var oldValue = record.modified['value'];
65931 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
65932 this.source[record.id] = v;
65934 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
65941 getProperty : function(row){
65942 return this.store.getAt(row);
65945 isEditableValue: function(val){
65946 if(val && val instanceof Date){
65948 }else if(typeof val == 'object' || typeof val == 'function'){
65954 setValue : function(prop, value){
65955 this.source[prop] = value;
65956 this.store.getById(prop).set('value', value);
65959 getSource : function(){
65960 return this.source;
65964 Roo.grid.PropertyColumnModel = function(grid, store){
65967 g.PropertyColumnModel.superclass.constructor.call(this, [
65968 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
65969 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
65971 this.store = store;
65972 this.bselect = Roo.DomHelper.append(document.body, {
65973 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
65974 {tag: 'option', value: 'true', html: 'true'},
65975 {tag: 'option', value: 'false', html: 'false'}
65978 Roo.id(this.bselect);
65981 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
65982 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
65983 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
65984 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
65985 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
65987 this.renderCellDelegate = this.renderCell.createDelegate(this);
65988 this.renderPropDelegate = this.renderProp.createDelegate(this);
65991 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
65995 valueText : 'Value',
65997 dateFormat : 'm/j/Y',
66000 renderDate : function(dateVal){
66001 return dateVal.dateFormat(this.dateFormat);
66004 renderBool : function(bVal){
66005 return bVal ? 'true' : 'false';
66008 isCellEditable : function(colIndex, rowIndex){
66009 return colIndex == 1;
66012 getRenderer : function(col){
66014 this.renderCellDelegate : this.renderPropDelegate;
66017 renderProp : function(v){
66018 return this.getPropertyName(v);
66021 renderCell : function(val){
66023 if(val instanceof Date){
66024 rv = this.renderDate(val);
66025 }else if(typeof val == 'boolean'){
66026 rv = this.renderBool(val);
66028 return Roo.util.Format.htmlEncode(rv);
66031 getPropertyName : function(name){
66032 var pn = this.grid.propertyNames;
66033 return pn && pn[name] ? pn[name] : name;
66036 getCellEditor : function(colIndex, rowIndex){
66037 var p = this.store.getProperty(rowIndex);
66038 var n = p.data['name'], val = p.data['value'];
66040 if(typeof(this.grid.customEditors[n]) == 'string'){
66041 return this.editors[this.grid.customEditors[n]];
66043 if(typeof(this.grid.customEditors[n]) != 'undefined'){
66044 return this.grid.customEditors[n];
66046 if(val instanceof Date){
66047 return this.editors['date'];
66048 }else if(typeof val == 'number'){
66049 return this.editors['number'];
66050 }else if(typeof val == 'boolean'){
66051 return this.editors['boolean'];
66053 return this.editors['string'];
66059 * @class Roo.grid.PropertyGrid
66060 * @extends Roo.grid.EditorGrid
66061 * This class represents the interface of a component based property grid control.
66062 * <br><br>Usage:<pre><code>
66063 var grid = new Roo.grid.PropertyGrid("my-container-id", {
66071 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66072 * The container MUST have some type of size defined for the grid to fill. The container will be
66073 * automatically set to position relative if it isn't already.
66074 * @param {Object} config A config object that sets properties on this grid.
66076 Roo.grid.PropertyGrid = function(container, config){
66077 config = config || {};
66078 var store = new Roo.grid.PropertyStore(this);
66079 this.store = store;
66080 var cm = new Roo.grid.PropertyColumnModel(this, store);
66081 store.store.sort('name', 'ASC');
66082 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66085 enableColLock:false,
66086 enableColumnMove:false,
66088 trackMouseOver: false,
66091 this.getGridEl().addClass('x-props-grid');
66092 this.lastEditRow = null;
66093 this.on('columnresize', this.onColumnResize, this);
66096 * @event beforepropertychange
66097 * Fires before a property changes (return false to stop?)
66098 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66099 * @param {String} id Record Id
66100 * @param {String} newval New Value
66101 * @param {String} oldval Old Value
66103 "beforepropertychange": true,
66105 * @event propertychange
66106 * Fires after a property changes
66107 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66108 * @param {String} id Record Id
66109 * @param {String} newval New Value
66110 * @param {String} oldval Old Value
66112 "propertychange": true
66114 this.customEditors = this.customEditors || {};
66116 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66119 * @cfg {Object} customEditors map of colnames=> custom editors.
66120 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66121 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66122 * false disables editing of the field.
66126 * @cfg {Object} propertyNames map of property Names to their displayed value
66129 render : function(){
66130 Roo.grid.PropertyGrid.superclass.render.call(this);
66131 this.autoSize.defer(100, this);
66134 autoSize : function(){
66135 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66137 this.view.fitColumns();
66141 onColumnResize : function(){
66142 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66146 * Sets the data for the Grid
66147 * accepts a Key => Value object of all the elements avaiable.
66148 * @param {Object} data to appear in grid.
66150 setSource : function(source){
66151 this.store.setSource(source);
66155 * Gets all the data from the grid.
66156 * @return {Object} data data stored in grid
66158 getSource : function(){
66159 return this.store.getSource();
66168 * @class Roo.grid.Calendar
66169 * @extends Roo.grid.Grid
66170 * This class extends the Grid to provide a calendar widget
66171 * <br><br>Usage:<pre><code>
66172 var grid = new Roo.grid.Calendar("my-container-id", {
66175 selModel: mySelectionModel,
66176 autoSizeColumns: true,
66177 monitorWindowResize: false,
66178 trackMouseOver: true
66179 eventstore : real data store..
66185 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66186 * The container MUST have some type of size defined for the grid to fill. The container will be
66187 * automatically set to position relative if it isn't already.
66188 * @param {Object} config A config object that sets properties on this grid.
66190 Roo.grid.Calendar = function(container, config){
66191 // initialize the container
66192 this.container = Roo.get(container);
66193 this.container.update("");
66194 this.container.setStyle("overflow", "hidden");
66195 this.container.addClass('x-grid-container');
66197 this.id = this.container.id;
66199 Roo.apply(this, config);
66200 // check and correct shorthanded configs
66204 for (var r = 0;r < 6;r++) {
66207 for (var c =0;c < 7;c++) {
66211 if (this.eventStore) {
66212 this.eventStore= Roo.factory(this.eventStore, Roo.data);
66213 this.eventStore.on('load',this.onLoad, this);
66214 this.eventStore.on('beforeload',this.clearEvents, this);
66218 this.dataSource = new Roo.data.Store({
66219 proxy: new Roo.data.MemoryProxy(rows),
66220 reader: new Roo.data.ArrayReader({}, [
66221 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66224 this.dataSource.load();
66225 this.ds = this.dataSource;
66226 this.ds.xmodule = this.xmodule || false;
66229 var cellRender = function(v,x,r)
66231 return String.format(
66232 '<div class="fc-day fc-widget-content"><div>' +
66233 '<div class="fc-event-container"></div>' +
66234 '<div class="fc-day-number">{0}</div>'+
66236 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66237 '</div></div>', v);
66242 this.colModel = new Roo.grid.ColumnModel( [
66244 xtype: 'ColumnModel',
66246 dataIndex : 'weekday0',
66248 renderer : cellRender
66251 xtype: 'ColumnModel',
66253 dataIndex : 'weekday1',
66255 renderer : cellRender
66258 xtype: 'ColumnModel',
66260 dataIndex : 'weekday2',
66261 header : 'Tuesday',
66262 renderer : cellRender
66265 xtype: 'ColumnModel',
66267 dataIndex : 'weekday3',
66268 header : 'Wednesday',
66269 renderer : cellRender
66272 xtype: 'ColumnModel',
66274 dataIndex : 'weekday4',
66275 header : 'Thursday',
66276 renderer : cellRender
66279 xtype: 'ColumnModel',
66281 dataIndex : 'weekday5',
66283 renderer : cellRender
66286 xtype: 'ColumnModel',
66288 dataIndex : 'weekday6',
66289 header : 'Saturday',
66290 renderer : cellRender
66293 this.cm = this.colModel;
66294 this.cm.xmodule = this.xmodule || false;
66298 //this.selModel = new Roo.grid.CellSelectionModel();
66299 //this.sm = this.selModel;
66300 //this.selModel.init(this);
66304 this.container.setWidth(this.width);
66308 this.container.setHeight(this.height);
66315 * The raw click event for the entire grid.
66316 * @param {Roo.EventObject} e
66321 * The raw dblclick event for the entire grid.
66322 * @param {Roo.EventObject} e
66326 * @event contextmenu
66327 * The raw contextmenu event for the entire grid.
66328 * @param {Roo.EventObject} e
66330 "contextmenu" : true,
66333 * The raw mousedown event for the entire grid.
66334 * @param {Roo.EventObject} e
66336 "mousedown" : true,
66339 * The raw mouseup event for the entire grid.
66340 * @param {Roo.EventObject} e
66345 * The raw mouseover event for the entire grid.
66346 * @param {Roo.EventObject} e
66348 "mouseover" : true,
66351 * The raw mouseout event for the entire grid.
66352 * @param {Roo.EventObject} e
66357 * The raw keypress event for the entire grid.
66358 * @param {Roo.EventObject} e
66363 * The raw keydown event for the entire grid.
66364 * @param {Roo.EventObject} e
66372 * Fires when a cell is clicked
66373 * @param {Grid} this
66374 * @param {Number} rowIndex
66375 * @param {Number} columnIndex
66376 * @param {Roo.EventObject} e
66378 "cellclick" : true,
66380 * @event celldblclick
66381 * Fires when a cell is double clicked
66382 * @param {Grid} this
66383 * @param {Number} rowIndex
66384 * @param {Number} columnIndex
66385 * @param {Roo.EventObject} e
66387 "celldblclick" : true,
66390 * Fires when a row is clicked
66391 * @param {Grid} this
66392 * @param {Number} rowIndex
66393 * @param {Roo.EventObject} e
66397 * @event rowdblclick
66398 * Fires when a row is double clicked
66399 * @param {Grid} this
66400 * @param {Number} rowIndex
66401 * @param {Roo.EventObject} e
66403 "rowdblclick" : true,
66405 * @event headerclick
66406 * Fires when a header is clicked
66407 * @param {Grid} this
66408 * @param {Number} columnIndex
66409 * @param {Roo.EventObject} e
66411 "headerclick" : true,
66413 * @event headerdblclick
66414 * Fires when a header cell is double clicked
66415 * @param {Grid} this
66416 * @param {Number} columnIndex
66417 * @param {Roo.EventObject} e
66419 "headerdblclick" : true,
66421 * @event rowcontextmenu
66422 * Fires when a row is right clicked
66423 * @param {Grid} this
66424 * @param {Number} rowIndex
66425 * @param {Roo.EventObject} e
66427 "rowcontextmenu" : true,
66429 * @event cellcontextmenu
66430 * Fires when a cell is right clicked
66431 * @param {Grid} this
66432 * @param {Number} rowIndex
66433 * @param {Number} cellIndex
66434 * @param {Roo.EventObject} e
66436 "cellcontextmenu" : true,
66438 * @event headercontextmenu
66439 * Fires when a header is right clicked
66440 * @param {Grid} this
66441 * @param {Number} columnIndex
66442 * @param {Roo.EventObject} e
66444 "headercontextmenu" : true,
66446 * @event bodyscroll
66447 * Fires when the body element is scrolled
66448 * @param {Number} scrollLeft
66449 * @param {Number} scrollTop
66451 "bodyscroll" : true,
66453 * @event columnresize
66454 * Fires when the user resizes a column
66455 * @param {Number} columnIndex
66456 * @param {Number} newSize
66458 "columnresize" : true,
66460 * @event columnmove
66461 * Fires when the user moves a column
66462 * @param {Number} oldIndex
66463 * @param {Number} newIndex
66465 "columnmove" : true,
66468 * Fires when row(s) start being dragged
66469 * @param {Grid} this
66470 * @param {Roo.GridDD} dd The drag drop object
66471 * @param {event} e The raw browser event
66473 "startdrag" : true,
66476 * Fires when a drag operation is complete
66477 * @param {Grid} this
66478 * @param {Roo.GridDD} dd The drag drop object
66479 * @param {event} e The raw browser event
66484 * Fires when dragged row(s) are dropped on a valid DD target
66485 * @param {Grid} this
66486 * @param {Roo.GridDD} dd The drag drop object
66487 * @param {String} targetId The target drag drop object
66488 * @param {event} e The raw browser event
66493 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66494 * @param {Grid} this
66495 * @param {Roo.GridDD} dd The drag drop object
66496 * @param {String} targetId The target drag drop object
66497 * @param {event} e The raw browser event
66502 * Fires when the dragged row(s) first cross another DD target while being dragged
66503 * @param {Grid} this
66504 * @param {Roo.GridDD} dd The drag drop object
66505 * @param {String} targetId The target drag drop object
66506 * @param {event} e The raw browser event
66508 "dragenter" : true,
66511 * Fires when the dragged row(s) leave another DD target while being dragged
66512 * @param {Grid} this
66513 * @param {Roo.GridDD} dd The drag drop object
66514 * @param {String} targetId The target drag drop object
66515 * @param {event} e The raw browser event
66520 * Fires when a row is rendered, so you can change add a style to it.
66521 * @param {GridView} gridview The grid view
66522 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
66528 * Fires when the grid is rendered
66529 * @param {Grid} grid
66534 * Fires when a date is selected
66535 * @param {DatePicker} this
66536 * @param {Date} date The selected date
66540 * @event monthchange
66541 * Fires when the displayed month changes
66542 * @param {DatePicker} this
66543 * @param {Date} date The selected month
66545 'monthchange': true,
66547 * @event evententer
66548 * Fires when mouse over an event
66549 * @param {Calendar} this
66550 * @param {event} Event
66552 'evententer': true,
66554 * @event eventleave
66555 * Fires when the mouse leaves an
66556 * @param {Calendar} this
66559 'eventleave': true,
66561 * @event eventclick
66562 * Fires when the mouse click an
66563 * @param {Calendar} this
66566 'eventclick': true,
66568 * @event eventrender
66569 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
66570 * @param {Calendar} this
66571 * @param {data} data to be modified
66573 'eventrender': true
66577 Roo.grid.Grid.superclass.constructor.call(this);
66578 this.on('render', function() {
66579 this.view.el.addClass('x-grid-cal');
66581 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
66585 if (!Roo.grid.Calendar.style) {
66586 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
66589 '.x-grid-cal .x-grid-col' : {
66590 height: 'auto !important',
66591 'vertical-align': 'top'
66593 '.x-grid-cal .fc-event-hori' : {
66604 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
66606 * @cfg {Store} eventStore The store that loads events.
66611 activeDate : false,
66614 monitorWindowResize : false,
66617 resizeColumns : function() {
66618 var col = (this.view.el.getWidth() / 7) - 3;
66619 // loop through cols, and setWidth
66620 for(var i =0 ; i < 7 ; i++){
66621 this.cm.setColumnWidth(i, col);
66624 setDate :function(date) {
66626 Roo.log('setDate?');
66628 this.resizeColumns();
66629 var vd = this.activeDate;
66630 this.activeDate = date;
66631 // if(vd && this.el){
66632 // var t = date.getTime();
66633 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
66634 // Roo.log('using add remove');
66636 // this.fireEvent('monthchange', this, date);
66638 // this.cells.removeClass("fc-state-highlight");
66639 // this.cells.each(function(c){
66640 // if(c.dateValue == t){
66641 // c.addClass("fc-state-highlight");
66642 // setTimeout(function(){
66643 // try{c.dom.firstChild.focus();}catch(e){}
66653 var days = date.getDaysInMonth();
66655 var firstOfMonth = date.getFirstDateOfMonth();
66656 var startingPos = firstOfMonth.getDay()-this.startDay;
66658 if(startingPos < this.startDay){
66662 var pm = date.add(Date.MONTH, -1);
66663 var prevStart = pm.getDaysInMonth()-startingPos;
66667 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66669 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
66670 //this.cells.addClassOnOver('fc-state-hover');
66672 var cells = this.cells.elements;
66673 var textEls = this.textNodes;
66675 //Roo.each(cells, function(cell){
66676 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
66679 days += startingPos;
66681 // convert everything to numbers so it's fast
66682 var day = 86400000;
66683 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
66686 //Roo.log(prevStart);
66688 var today = new Date().clearTime().getTime();
66689 var sel = date.clearTime().getTime();
66690 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
66691 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
66692 var ddMatch = this.disabledDatesRE;
66693 var ddText = this.disabledDatesText;
66694 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
66695 var ddaysText = this.disabledDaysText;
66696 var format = this.format;
66698 var setCellClass = function(cal, cell){
66700 //Roo.log('set Cell Class');
66702 var t = d.getTime();
66707 cell.dateValue = t;
66709 cell.className += " fc-today";
66710 cell.className += " fc-state-highlight";
66711 cell.title = cal.todayText;
66714 // disable highlight in other month..
66715 cell.className += " fc-state-highlight";
66720 //cell.className = " fc-state-disabled";
66721 cell.title = cal.minText;
66725 //cell.className = " fc-state-disabled";
66726 cell.title = cal.maxText;
66730 if(ddays.indexOf(d.getDay()) != -1){
66731 // cell.title = ddaysText;
66732 // cell.className = " fc-state-disabled";
66735 if(ddMatch && format){
66736 var fvalue = d.dateFormat(format);
66737 if(ddMatch.test(fvalue)){
66738 cell.title = ddText.replace("%0", fvalue);
66739 cell.className = " fc-state-disabled";
66743 if (!cell.initialClassName) {
66744 cell.initialClassName = cell.dom.className;
66747 cell.dom.className = cell.initialClassName + ' ' + cell.className;
66752 for(; i < startingPos; i++) {
66753 cells[i].dayName = (++prevStart);
66754 Roo.log(textEls[i]);
66755 d.setDate(d.getDate()+1);
66757 //cells[i].className = "fc-past fc-other-month";
66758 setCellClass(this, cells[i]);
66763 for(; i < days; i++){
66764 intDay = i - startingPos + 1;
66765 cells[i].dayName = (intDay);
66766 d.setDate(d.getDate()+1);
66768 cells[i].className = ''; // "x-date-active";
66769 setCellClass(this, cells[i]);
66773 for(; i < 42; i++) {
66774 //textEls[i].innerHTML = (++extraDays);
66776 d.setDate(d.getDate()+1);
66777 cells[i].dayName = (++extraDays);
66778 cells[i].className = "fc-future fc-other-month";
66779 setCellClass(this, cells[i]);
66782 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
66784 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
66786 // this will cause all the cells to mis
66789 for (var r = 0;r < 6;r++) {
66790 for (var c =0;c < 7;c++) {
66791 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
66795 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66796 for(i=0;i<cells.length;i++) {
66798 this.cells.elements[i].dayName = cells[i].dayName ;
66799 this.cells.elements[i].className = cells[i].className;
66800 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
66801 this.cells.elements[i].title = cells[i].title ;
66802 this.cells.elements[i].dateValue = cells[i].dateValue ;
66808 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
66809 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
66811 ////if(totalRows != 6){
66812 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
66813 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
66816 this.fireEvent('monthchange', this, date);
66821 * Returns the grid's SelectionModel.
66822 * @return {SelectionModel}
66824 getSelectionModel : function(){
66825 if(!this.selModel){
66826 this.selModel = new Roo.grid.CellSelectionModel();
66828 return this.selModel;
66832 this.eventStore.load()
66838 findCell : function(dt) {
66839 dt = dt.clearTime().getTime();
66841 this.cells.each(function(c){
66842 //Roo.log("check " +c.dateValue + '?=' + dt);
66843 if(c.dateValue == dt){
66853 findCells : function(rec) {
66854 var s = rec.data.start_dt.clone().clearTime().getTime();
66856 var e= rec.data.end_dt.clone().clearTime().getTime();
66859 this.cells.each(function(c){
66860 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
66862 if(c.dateValue > e){
66865 if(c.dateValue < s){
66874 findBestRow: function(cells)
66878 for (var i =0 ; i < cells.length;i++) {
66879 ret = Math.max(cells[i].rows || 0,ret);
66886 addItem : function(rec)
66888 // look for vertical location slot in
66889 var cells = this.findCells(rec);
66891 rec.row = this.findBestRow(cells);
66893 // work out the location.
66897 for(var i =0; i < cells.length; i++) {
66905 if (crow.start.getY() == cells[i].getY()) {
66907 crow.end = cells[i];
66923 for (var i = 0; i < cells.length;i++) {
66924 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
66931 clearEvents: function() {
66933 if (!this.eventStore.getCount()) {
66936 // reset number of rows in cells.
66937 Roo.each(this.cells.elements, function(c){
66941 this.eventStore.each(function(e) {
66942 this.clearEvent(e);
66947 clearEvent : function(ev)
66950 Roo.each(ev.els, function(el) {
66951 el.un('mouseenter' ,this.onEventEnter, this);
66952 el.un('mouseleave' ,this.onEventLeave, this);
66960 renderEvent : function(ev,ctr) {
66962 ctr = this.view.el.select('.fc-event-container',true).first();
66966 this.clearEvent(ev);
66972 var cells = ev.cells;
66973 var rows = ev.rows;
66974 this.fireEvent('eventrender', this, ev);
66976 for(var i =0; i < rows.length; i++) {
66980 cls += ' fc-event-start';
66982 if ((i+1) == rows.length) {
66983 cls += ' fc-event-end';
66986 //Roo.log(ev.data);
66987 // how many rows should it span..
66988 var cg = this.eventTmpl.append(ctr,Roo.apply({
66991 }, ev.data) , true);
66994 cg.on('mouseenter' ,this.onEventEnter, this, ev);
66995 cg.on('mouseleave' ,this.onEventLeave, this, ev);
66996 cg.on('click', this.onEventClick, this, ev);
67000 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67001 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67004 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
67005 cg.setWidth(ebox.right - sbox.x -2);
67009 renderEvents: function()
67011 // first make sure there is enough space..
67013 if (!this.eventTmpl) {
67014 this.eventTmpl = new Roo.Template(
67015 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
67016 '<div class="fc-event-inner">' +
67017 '<span class="fc-event-time">{time}</span>' +
67018 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67020 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
67028 this.cells.each(function(c) {
67029 //Roo.log(c.select('.fc-day-content div',true).first());
67030 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67033 var ctr = this.view.el.select('.fc-event-container',true).first();
67036 this.eventStore.each(function(ev){
67038 this.renderEvent(ev);
67042 this.view.layout();
67046 onEventEnter: function (e, el,event,d) {
67047 this.fireEvent('evententer', this, el, event);
67050 onEventLeave: function (e, el,event,d) {
67051 this.fireEvent('eventleave', this, el, event);
67054 onEventClick: function (e, el,event,d) {
67055 this.fireEvent('eventclick', this, el, event);
67058 onMonthChange: function () {
67062 onLoad: function () {
67064 //Roo.log('calendar onload');
67066 if(this.eventStore.getCount() > 0){
67070 this.eventStore.each(function(d){
67075 if (typeof(add.end_dt) == 'undefined') {
67076 Roo.log("Missing End time in calendar data: ");
67080 if (typeof(add.start_dt) == 'undefined') {
67081 Roo.log("Missing Start time in calendar data: ");
67085 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67086 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67087 add.id = add.id || d.id;
67088 add.title = add.title || '??';
67096 this.renderEvents();
67106 render : function ()
67110 if (!this.view.el.hasClass('course-timesheet')) {
67111 this.view.el.addClass('course-timesheet');
67113 if (this.tsStyle) {
67118 Roo.log(_this.grid.view.el.getWidth());
67121 this.tsStyle = Roo.util.CSS.createStyleSheet({
67122 '.course-timesheet .x-grid-row' : {
67125 '.x-grid-row td' : {
67126 'vertical-align' : 0
67128 '.course-edit-link' : {
67130 'text-overflow' : 'ellipsis',
67131 'overflow' : 'hidden',
67132 'white-space' : 'nowrap',
67133 'cursor' : 'pointer'
67138 '.de-act-sup-link' : {
67139 'color' : 'purple',
67140 'text-decoration' : 'line-through'
67144 'text-decoration' : 'line-through'
67146 '.course-timesheet .course-highlight' : {
67147 'border-top-style': 'dashed !important',
67148 'border-bottom-bottom': 'dashed !important'
67150 '.course-timesheet .course-item' : {
67151 'font-family' : 'tahoma, arial, helvetica',
67152 'font-size' : '11px',
67153 'overflow' : 'hidden',
67154 'padding-left' : '10px',
67155 'padding-right' : '10px',
67156 'padding-top' : '10px'
67164 monitorWindowResize : false,
67165 cellrenderer : function(v,x,r)
67170 xtype: 'CellSelectionModel',
67177 beforeload : function (_self, options)
67179 options.params = options.params || {};
67180 options.params._month = _this.monthField.getValue();
67181 options.params.limit = 9999;
67182 options.params['sort'] = 'when_dt';
67183 options.params['dir'] = 'ASC';
67184 this.proxy.loadResponse = this.loadResponse;
67186 //this.addColumns();
67188 load : function (_self, records, options)
67190 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67191 // if you click on the translation.. you can edit it...
67192 var el = Roo.get(this);
67193 var id = el.dom.getAttribute('data-id');
67194 var d = el.dom.getAttribute('data-date');
67195 var t = el.dom.getAttribute('data-time');
67196 //var id = this.child('span').dom.textContent;
67199 Pman.Dialog.CourseCalendar.show({
67203 productitem_active : id ? 1 : 0
67205 _this.grid.ds.load({});
67210 _this.panel.fireEvent('resize', [ '', '' ]);
67213 loadResponse : function(o, success, response){
67214 // this is overridden on before load..
67216 Roo.log("our code?");
67217 //Roo.log(success);
67218 //Roo.log(response)
67219 delete this.activeRequest;
67221 this.fireEvent("loadexception", this, o, response);
67222 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67227 result = o.reader.read(response);
67229 Roo.log("load exception?");
67230 this.fireEvent("loadexception", this, o, response, e);
67231 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67234 Roo.log("ready...");
67235 // loop through result.records;
67236 // and set this.tdate[date] = [] << array of records..
67238 Roo.each(result.records, function(r){
67240 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67241 _this.tdata[r.data.when_dt.format('j')] = [];
67243 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67246 //Roo.log(_this.tdata);
67248 result.records = [];
67249 result.totalRecords = 6;
67251 // let's generate some duumy records for the rows.
67252 //var st = _this.dateField.getValue();
67254 // work out monday..
67255 //st = st.add(Date.DAY, -1 * st.format('w'));
67257 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67259 var firstOfMonth = date.getFirstDayOfMonth();
67260 var days = date.getDaysInMonth();
67262 var firstAdded = false;
67263 for (var i = 0; i < result.totalRecords ; i++) {
67264 //var d= st.add(Date.DAY, i);
67267 for(var w = 0 ; w < 7 ; w++){
67268 if(!firstAdded && firstOfMonth != w){
67275 var dd = (d > 0 && d < 10) ? "0"+d : d;
67276 row['weekday'+w] = String.format(
67277 '<span style="font-size: 16px;"><b>{0}</b></span>'+
67278 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67280 date.format('Y-m-')+dd
67283 if(typeof(_this.tdata[d]) != 'undefined'){
67284 Roo.each(_this.tdata[d], function(r){
67288 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67289 if(r.parent_id*1>0){
67290 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67293 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67294 deactive = 'de-act-link';
67297 row['weekday'+w] += String.format(
67298 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67300 r.product_id_name, //1
67301 r.when_dt.format('h:ia'), //2
67311 // only do this if something added..
67313 result.records.push(_this.grid.dataSource.reader.newRow(row));
67317 // push it twice. (second one with an hour..
67321 this.fireEvent("load", this, o, o.request.arg);
67322 o.request.callback.call(o.request.scope, result, o.request.arg, true);
67324 sortInfo : {field: 'when_dt', direction : 'ASC' },
67326 xtype: 'HttpProxy',
67329 url : baseURL + '/Roo/Shop_course.php'
67332 xtype: 'JsonReader',
67349 'name': 'parent_id',
67353 'name': 'product_id',
67357 'name': 'productitem_id',
67375 click : function (_self, e)
67377 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67378 sd.setMonth(sd.getMonth()-1);
67379 _this.monthField.setValue(sd.format('Y-m-d'));
67380 _this.grid.ds.load({});
67386 xtype: 'Separator',
67390 xtype: 'MonthField',
67393 render : function (_self)
67395 _this.monthField = _self;
67396 // _this.monthField.set today
67398 select : function (combo, date)
67400 _this.grid.ds.load({});
67403 value : (function() { return new Date(); })()
67406 xtype: 'Separator',
67412 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67422 click : function (_self, e)
67424 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67425 sd.setMonth(sd.getMonth()+1);
67426 _this.monthField.setValue(sd.format('Y-m-d'));
67427 _this.grid.ds.load({});
67440 * Ext JS Library 1.1.1
67441 * Copyright(c) 2006-2007, Ext JS, LLC.
67443 * Originally Released Under LGPL - original licence link has changed is not relivant.
67446 * <script type="text/javascript">
67450 * @class Roo.LoadMask
67451 * A simple utility class for generically masking elements while loading data. If the element being masked has
67452 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67453 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
67454 * element's UpdateManager load indicator and will be destroyed after the initial load.
67456 * Create a new LoadMask
67457 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67458 * @param {Object} config The config object
67460 Roo.LoadMask = function(el, config){
67461 this.el = Roo.get(el);
67462 Roo.apply(this, config);
67464 this.store.on('beforeload', this.onBeforeLoad, this);
67465 this.store.on('load', this.onLoad, this);
67466 this.store.on('loadexception', this.onLoadException, this);
67467 this.removeMask = false;
67469 var um = this.el.getUpdateManager();
67470 um.showLoadIndicator = false; // disable the default indicator
67471 um.on('beforeupdate', this.onBeforeLoad, this);
67472 um.on('update', this.onLoad, this);
67473 um.on('failure', this.onLoad, this);
67474 this.removeMask = true;
67478 Roo.LoadMask.prototype = {
67480 * @cfg {Boolean} removeMask
67481 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67482 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
67484 removeMask : false,
67486 * @cfg {String} msg
67487 * The text to display in a centered loading message box (defaults to 'Loading...')
67489 msg : 'Loading...',
67491 * @cfg {String} msgCls
67492 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67494 msgCls : 'x-mask-loading',
67497 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67503 * Disables the mask to prevent it from being displayed
67505 disable : function(){
67506 this.disabled = true;
67510 * Enables the mask so that it can be displayed
67512 enable : function(){
67513 this.disabled = false;
67516 onLoadException : function()
67518 Roo.log(arguments);
67520 if (typeof(arguments[3]) != 'undefined') {
67521 Roo.MessageBox.alert("Error loading",arguments[3]);
67525 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67526 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67533 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67536 onLoad : function()
67538 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67542 onBeforeLoad : function(){
67543 if(!this.disabled){
67544 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67549 destroy : function(){
67551 this.store.un('beforeload', this.onBeforeLoad, this);
67552 this.store.un('load', this.onLoad, this);
67553 this.store.un('loadexception', this.onLoadException, this);
67555 var um = this.el.getUpdateManager();
67556 um.un('beforeupdate', this.onBeforeLoad, this);
67557 um.un('update', this.onLoad, this);
67558 um.un('failure', this.onLoad, this);
67563 * Ext JS Library 1.1.1
67564 * Copyright(c) 2006-2007, Ext JS, LLC.
67566 * Originally Released Under LGPL - original licence link has changed is not relivant.
67569 * <script type="text/javascript">
67574 * @class Roo.XTemplate
67575 * @extends Roo.Template
67576 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
67578 var t = new Roo.XTemplate(
67579 '<select name="{name}">',
67580 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
67584 // then append, applying the master template values
67587 * Supported features:
67592 {a_variable} - output encoded.
67593 {a_variable.format:("Y-m-d")} - call a method on the variable
67594 {a_variable:raw} - unencoded output
67595 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
67596 {a_variable:this.method_on_template(...)} - call a method on the template object.
67601 <tpl for="a_variable or condition.."></tpl>
67602 <tpl if="a_variable or condition"></tpl>
67603 <tpl exec="some javascript"></tpl>
67604 <tpl name="named_template"></tpl> (experimental)
67606 <tpl for="."></tpl> - just iterate the property..
67607 <tpl for=".."></tpl> - iterates with the parent (probably the template)
67611 Roo.XTemplate = function()
67613 Roo.XTemplate.superclass.constructor.apply(this, arguments);
67620 Roo.extend(Roo.XTemplate, Roo.Template, {
67623 * The various sub templates
67628 * basic tag replacing syntax
67631 * // you can fake an object call by doing this
67635 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
67638 * compile the template
67640 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
67643 compile: function()
67647 s = ['<tpl>', s, '</tpl>'].join('');
67649 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
67650 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
67651 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
67652 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
67653 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
67658 while(true == !!(m = s.match(re))){
67659 var forMatch = m[0].match(nameRe),
67660 ifMatch = m[0].match(ifRe),
67661 execMatch = m[0].match(execRe),
67662 namedMatch = m[0].match(namedRe),
67667 name = forMatch && forMatch[1] ? forMatch[1] : '';
67670 // if - puts fn into test..
67671 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
67673 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
67678 // exec - calls a function... returns empty if true is returned.
67679 exp = execMatch && execMatch[1] ? execMatch[1] : null;
67681 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
67689 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
67690 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
67691 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
67694 var uid = namedMatch ? namedMatch[1] : id;
67698 id: namedMatch ? namedMatch[1] : id,
67705 s = s.replace(m[0], '');
67707 s = s.replace(m[0], '{xtpl'+ id + '}');
67712 for(var i = tpls.length-1; i >= 0; --i){
67713 this.compileTpl(tpls[i]);
67714 this.tpls[tpls[i].id] = tpls[i];
67716 this.master = tpls[tpls.length-1];
67720 * same as applyTemplate, except it's done to one of the subTemplates
67721 * when using named templates, you can do:
67723 * var str = pl.applySubTemplate('your-name', values);
67726 * @param {Number} id of the template
67727 * @param {Object} values to apply to template
67728 * @param {Object} parent (normaly the instance of this object)
67730 applySubTemplate : function(id, values, parent)
67734 var t = this.tpls[id];
67738 if(t.test && !t.test.call(this, values, parent)){
67742 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
67743 Roo.log(e.toString());
67749 if(t.exec && t.exec.call(this, values, parent)){
67753 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
67754 Roo.log(e.toString());
67759 var vs = t.target ? t.target.call(this, values, parent) : values;
67760 parent = t.target ? values : parent;
67761 if(t.target && vs instanceof Array){
67763 for(var i = 0, len = vs.length; i < len; i++){
67764 buf[buf.length] = t.compiled.call(this, vs[i], parent);
67766 return buf.join('');
67768 return t.compiled.call(this, vs, parent);
67770 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
67771 Roo.log(e.toString());
67772 Roo.log(t.compiled);
67777 compileTpl : function(tpl)
67779 var fm = Roo.util.Format;
67780 var useF = this.disableFormats !== true;
67781 var sep = Roo.isGecko ? "+" : ",";
67782 var undef = function(str) {
67783 Roo.log("Property not found :" + str);
67787 var fn = function(m, name, format, args)
67789 //Roo.log(arguments);
67790 args = args ? args.replace(/\\'/g,"'") : args;
67791 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
67792 if (typeof(format) == 'undefined') {
67793 format= 'htmlEncode';
67795 if (format == 'raw' ) {
67799 if(name.substr(0, 4) == 'xtpl'){
67800 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
67803 // build an array of options to determine if value is undefined..
67805 // basically get 'xxxx.yyyy' then do
67806 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
67807 // (function () { Roo.log("Property not found"); return ''; })() :
67812 Roo.each(name.split('.'), function(st) {
67813 lookfor += (lookfor.length ? '.': '') + st;
67814 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
67817 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
67820 if(format && useF){
67822 args = args ? ',' + args : "";
67824 if(format.substr(0, 5) != "this."){
67825 format = "fm." + format + '(';
67827 format = 'this.call("'+ format.substr(5) + '", ';
67831 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
67835 // called with xxyx.yuu:(test,test)
67837 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
67839 // raw.. - :raw modifier..
67840 return "'"+ sep + udef_st + name + ")"+sep+"'";
67844 // branched to use + in gecko and [].join() in others
67846 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
67847 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
67850 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
67851 body.push(tpl.body.replace(/(\r\n|\n)/g,
67852 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
67853 body.push("'].join('');};};");
67854 body = body.join('');
67857 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
67859 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
67865 applyTemplate : function(values){
67866 return this.master.compiled.call(this, values, {});
67867 //var s = this.subs;
67870 apply : function(){
67871 return this.applyTemplate.apply(this, arguments);
67876 Roo.XTemplate.from = function(el){
67877 el = Roo.getDom(el);
67878 return new Roo.XTemplate(el.value || el.innerHTML);