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 {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY)
1209 @return {Number} The diff in milliseconds or units of interval
1210 @member Date getElapsed
1212 Date.prototype.getElapsed = function(date, interval)
1214 date = date || new Date();
1215 var ret = Math.abs(date.getTime()-this.getTime());
1219 return Math.floor(ret / (1000));
1221 return Math.floor(ret / (1000*60));
1223 return Math.floor(ret / (1000*60*60));
1225 return Math.floor(ret / (1000*60*60*24));
1226 case Date.MONTH: // this does not give exact number...??
1227 return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1228 case Date.YEAR: // this does not give exact number...??
1229 return (date.format("Y") - this.format("Y"));
1237 // was in date file..
1241 Date.parseFunctions = {count:0};
1243 Date.parseRegexes = [];
1245 Date.formatFunctions = {count:0};
1248 Date.prototype.dateFormat = function(format) {
1249 if (Date.formatFunctions[format] == null) {
1250 Date.createNewFormat(format);
1252 var func = Date.formatFunctions[format];
1253 return this[func]();
1258 * Formats a date given the supplied format string
1259 * @param {String} format The format string
1260 * @return {String} The formatted date
1263 Date.prototype.format = Date.prototype.dateFormat;
1266 Date.createNewFormat = function(format) {
1267 var funcName = "format" + Date.formatFunctions.count++;
1268 Date.formatFunctions[format] = funcName;
1269 var code = "Date.prototype." + funcName + " = function(){return ";
1270 var special = false;
1272 for (var i = 0; i < format.length; ++i) {
1273 ch = format.charAt(i);
1274 if (!special && ch == "\\") {
1279 code += "'" + String.escape(ch) + "' + ";
1282 code += Date.getFormatCode(ch);
1285 /** eval:var:zzzzzzzzzzzzz */
1286 eval(code.substring(0, code.length - 3) + ";}");
1290 Date.getFormatCode = function(character) {
1291 switch (character) {
1293 return "String.leftPad(this.getDate(), 2, '0') + ";
1295 return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1297 return "this.getDate() + ";
1299 return "Date.dayNames[this.getDay()] + ";
1301 return "this.getSuffix() + ";
1303 return "this.getDay() + ";
1305 return "this.getDayOfYear() + ";
1307 return "this.getWeekOfYear() + ";
1309 return "Date.monthNames[this.getMonth()] + ";
1311 return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1313 return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1315 return "(this.getMonth() + 1) + ";
1317 return "this.getDaysInMonth() + ";
1319 return "(this.isLeapYear() ? 1 : 0) + ";
1321 return "this.getFullYear() + ";
1323 return "('' + this.getFullYear()).substring(2, 4) + ";
1325 return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1327 return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1329 return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1331 return "this.getHours() + ";
1333 return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1335 return "String.leftPad(this.getHours(), 2, '0') + ";
1337 return "String.leftPad(this.getMinutes(), 2, '0') + ";
1339 return "String.leftPad(this.getSeconds(), 2, '0') + ";
1341 return "this.getGMTOffset() + ";
1343 return "this.getGMTColonOffset() + ";
1345 return "this.getTimezone() + ";
1347 return "(this.getTimezoneOffset() * -60) + ";
1349 return "'" + String.escape(character) + "' + ";
1354 * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1355 * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates. Any part of
1356 * the date format that is not specified will default to the current date value for that part. Time parts can also
1357 * be specified, but default to 0. Keep in mind that the input date string must precisely match the specified format
1358 * string or the parse operation will fail.
1361 //dt = Fri May 25 2007 (current date)
1362 var dt = new Date();
1364 //dt = Thu May 25 2006 (today's month/day in 2006)
1365 dt = Date.parseDate("2006", "Y");
1367 //dt = Sun Jan 15 2006 (all date parts specified)
1368 dt = Date.parseDate("2006-1-15", "Y-m-d");
1370 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1371 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1373 * @param {String} input The unparsed date as a string
1374 * @param {String} format The format the date is in
1375 * @return {Date} The parsed date
1378 Date.parseDate = function(input, format) {
1379 if (Date.parseFunctions[format] == null) {
1380 Date.createParser(format);
1382 var func = Date.parseFunctions[format];
1383 return Date[func](input);
1389 Date.createParser = function(format) {
1390 var funcName = "parse" + Date.parseFunctions.count++;
1391 var regexNum = Date.parseRegexes.length;
1392 var currentGroup = 1;
1393 Date.parseFunctions[format] = funcName;
1395 var code = "Date." + funcName + " = function(input){\n"
1396 + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1397 + "var d = new Date();\n"
1398 + "y = d.getFullYear();\n"
1399 + "m = d.getMonth();\n"
1400 + "d = d.getDate();\n"
1401 + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1402 + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1403 + "if (results && results.length > 0) {";
1406 var special = false;
1408 for (var i = 0; i < format.length; ++i) {
1409 ch = format.charAt(i);
1410 if (!special && ch == "\\") {
1415 regex += String.escape(ch);
1418 var obj = Date.formatCodeToRegex(ch, currentGroup);
1419 currentGroup += obj.g;
1421 if (obj.g && obj.c) {
1427 code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1428 + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1429 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1430 + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1431 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1432 + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1433 + "else if (y >= 0 && m >= 0 && d > 0)\n"
1434 + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1435 + "else if (y >= 0 && m >= 0)\n"
1436 + "{v = new Date(y, m); v.setFullYear(y);}\n"
1437 + "else if (y >= 0)\n"
1438 + "{v = new Date(y); v.setFullYear(y);}\n"
1439 + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1440 + " ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1441 + " v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1444 Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1445 /** eval:var:zzzzzzzzzzzzz */
1450 Date.formatCodeToRegex = function(character, currentGroup) {
1451 switch (character) {
1455 s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1458 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1459 s:"(\\d{1,2})"}; // day of month without leading zeroes
1462 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1463 s:"(\\d{2})"}; // day of month with leading zeroes
1467 s:"(?:" + Date.dayNames.join("|") + ")"};
1471 s:"(?:st|nd|rd|th)"};
1486 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1487 s:"(" + Date.monthNames.join("|") + ")"};
1490 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1491 s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1494 c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1495 s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1498 c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1499 s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1510 c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1514 c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1515 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1519 c:"if (results[" + currentGroup + "] == 'am') {\n"
1520 + "if (h == 12) { h = 0; }\n"
1521 + "} else { if (h < 12) { h += 12; }}",
1525 c:"if (results[" + currentGroup + "] == 'AM') {\n"
1526 + "if (h == 12) { h = 0; }\n"
1527 + "} else { if (h < 12) { h += 12; }}",
1532 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1533 s:"(\\d{1,2})"}; // 12/24-hr format format of an hour without leading zeroes
1537 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1538 s:"(\\d{2})"}; // 12/24-hr format format of an hour with leading zeroes
1541 c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1545 c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1550 "o = results[", currentGroup, "];\n",
1551 "var sn = o.substring(0,1);\n", // get + / - sign
1552 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1553 "var mn = o.substring(3,5) % 60;\n", // get minutes
1554 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1555 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1557 s:"([+\-]\\d{2,4})"};
1563 "o = results[", currentGroup, "];\n",
1564 "var sn = o.substring(0,1);\n",
1565 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1566 "var mn = o.substring(4,6) % 60;\n",
1567 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1568 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1574 s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1577 c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1578 + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1579 s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1583 s:String.escape(character)};
1588 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1589 * @return {String} The abbreviated timezone name (e.g. 'CST')
1591 Date.prototype.getTimezone = function() {
1592 return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1596 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1597 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1599 Date.prototype.getGMTOffset = function() {
1600 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1601 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1602 + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1606 * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1607 * @return {String} 2-characters representing hours and 2-characters representing minutes
1608 * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1610 Date.prototype.getGMTColonOffset = function() {
1611 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1612 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1614 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1618 * Get the numeric day number of the year, adjusted for leap year.
1619 * @return {Number} 0 through 364 (365 in leap years)
1621 Date.prototype.getDayOfYear = function() {
1623 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1624 for (var i = 0; i < this.getMonth(); ++i) {
1625 num += Date.daysInMonth[i];
1627 return num + this.getDate() - 1;
1631 * Get the string representation of the numeric week number of the year
1632 * (equivalent to the format specifier 'W').
1633 * @return {String} '00' through '52'
1635 Date.prototype.getWeekOfYear = function() {
1636 // Skip to Thursday of this week
1637 var now = this.getDayOfYear() + (4 - this.getDay());
1638 // Find the first Thursday of the year
1639 var jan1 = new Date(this.getFullYear(), 0, 1);
1640 var then = (7 - jan1.getDay() + 4);
1641 return String.leftPad(((now - then) / 7) + 1, 2, "0");
1645 * Whether or not the current date is in a leap year.
1646 * @return {Boolean} True if the current date is in a leap year, else false
1648 Date.prototype.isLeapYear = function() {
1649 var year = this.getFullYear();
1650 return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1654 * Get the first day of the current month, adjusted for leap year. The returned value
1655 * is the numeric day index within the week (0-6) which can be used in conjunction with
1656 * the {@link #monthNames} array to retrieve the textual day name.
1659 var dt = new Date('1/10/2007');
1660 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1662 * @return {Number} The day number (0-6)
1664 Date.prototype.getFirstDayOfMonth = function() {
1665 var day = (this.getDay() - (this.getDate() - 1)) % 7;
1666 return (day < 0) ? (day + 7) : day;
1670 * Get the last day of the current month, adjusted for leap year. The returned value
1671 * is the numeric day index within the week (0-6) which can be used in conjunction with
1672 * the {@link #monthNames} array to retrieve the textual day name.
1675 var dt = new Date('1/10/2007');
1676 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1678 * @return {Number} The day number (0-6)
1680 Date.prototype.getLastDayOfMonth = function() {
1681 var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1682 return (day < 0) ? (day + 7) : day;
1687 * Get the first date of this date's month
1690 Date.prototype.getFirstDateOfMonth = function() {
1691 return new Date(this.getFullYear(), this.getMonth(), 1);
1695 * Get the last date of this date's month
1698 Date.prototype.getLastDateOfMonth = function() {
1699 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1702 * Get the number of days in the current month, adjusted for leap year.
1703 * @return {Number} The number of days in the month
1705 Date.prototype.getDaysInMonth = function() {
1706 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1707 return Date.daysInMonth[this.getMonth()];
1711 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1712 * @return {String} 'st, 'nd', 'rd' or 'th'
1714 Date.prototype.getSuffix = function() {
1715 switch (this.getDate()) {
1732 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1735 * An array of textual month names.
1736 * Override these values for international dates, for example...
1737 * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1756 * An array of textual day names.
1757 * Override these values for international dates, for example...
1758 * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1774 Date.monthNumbers = {
1789 * Creates and returns a new Date instance with the exact same date value as the called instance.
1790 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1791 * variable will also be changed. When the intention is to create a new variable that will not
1792 * modify the original instance, you should create a clone.
1794 * Example of correctly cloning a date:
1797 var orig = new Date('10/1/2006');
1800 document.write(orig); //returns 'Thu Oct 05 2006'!
1803 var orig = new Date('10/1/2006');
1804 var copy = orig.clone();
1806 document.write(orig); //returns 'Thu Oct 01 2006'
1808 * @return {Date} The new Date instance
1810 Date.prototype.clone = function() {
1811 return new Date(this.getTime());
1815 * Clears any time information from this date
1816 @param {Boolean} clone true to create a clone of this date, clear the time and return it
1817 @return {Date} this or the clone
1819 Date.prototype.clearTime = function(clone){
1821 return this.clone().clearTime();
1826 this.setMilliseconds(0);
1831 // safari setMonth is broken -- check that this is only donw once...
1832 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1833 Date.brokenSetMonth = Date.prototype.setMonth;
1834 Date.prototype.setMonth = function(num){
1836 var n = Math.ceil(-num);
1837 var back_year = Math.ceil(n/12);
1838 var month = (n % 12) ? 12 - n % 12 : 0 ;
1839 this.setFullYear(this.getFullYear() - back_year);
1840 return Date.brokenSetMonth.call(this, month);
1842 return Date.brokenSetMonth.apply(this, arguments);
1847 /** Date interval constant
1851 /** Date interval constant
1855 /** Date interval constant
1859 /** Date interval constant
1863 /** Date interval constant
1867 /** Date interval constant
1871 /** Date interval constant
1877 * Provides a convenient method of performing basic date arithmetic. This method
1878 * does not modify the Date instance being called - it creates and returns
1879 * a new Date instance containing the resulting date value.
1884 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1885 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1887 //Negative values will subtract correctly:
1888 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1889 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1891 //You can even chain several calls together in one line!
1892 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1893 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1896 * @param {String} interval A valid date interval enum value
1897 * @param {Number} value The amount to add to the current date
1898 * @return {Date} The new Date instance
1900 Date.prototype.add = function(interval, value){
1901 var d = this.clone();
1902 if (!interval || value === 0) { return d; }
1903 switch(interval.toLowerCase()){
1905 d.setMilliseconds(this.getMilliseconds() + value);
1908 d.setSeconds(this.getSeconds() + value);
1911 d.setMinutes(this.getMinutes() + value);
1914 d.setHours(this.getHours() + value);
1917 d.setDate(this.getDate() + value);
1920 var day = this.getDate();
1922 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1925 d.setMonth(this.getMonth() + value);
1928 d.setFullYear(this.getFullYear() + value);
1934 * @class Roo.lib.Dom
1938 * Dom utils (from YIU afaik)
1944 * Get the view width
1945 * @param {Boolean} full True will get the full document, otherwise it's the view width
1946 * @return {Number} The width
1949 getViewWidth : function(full) {
1950 return full ? this.getDocumentWidth() : this.getViewportWidth();
1953 * Get the view height
1954 * @param {Boolean} full True will get the full document, otherwise it's the view height
1955 * @return {Number} The height
1957 getViewHeight : function(full) {
1958 return full ? this.getDocumentHeight() : this.getViewportHeight();
1961 * Get the Full Document height
1962 * @return {Number} The height
1964 getDocumentHeight: function() {
1965 var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1966 return Math.max(scrollHeight, this.getViewportHeight());
1969 * Get the Full Document width
1970 * @return {Number} The width
1972 getDocumentWidth: function() {
1973 var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1974 return Math.max(scrollWidth, this.getViewportWidth());
1977 * Get the Window Viewport height
1978 * @return {Number} The height
1980 getViewportHeight: function() {
1981 var height = self.innerHeight;
1982 var mode = document.compatMode;
1984 if ((mode || Roo.isIE) && !Roo.isOpera) {
1985 height = (mode == "CSS1Compat") ?
1986 document.documentElement.clientHeight :
1987 document.body.clientHeight;
1993 * Get the Window Viewport width
1994 * @return {Number} The width
1996 getViewportWidth: function() {
1997 var width = self.innerWidth;
1998 var mode = document.compatMode;
2000 if (mode || Roo.isIE) {
2001 width = (mode == "CSS1Compat") ?
2002 document.documentElement.clientWidth :
2003 document.body.clientWidth;
2008 isAncestor : function(p, c) {
2015 if (p.contains && !Roo.isSafari) {
2016 return p.contains(c);
2017 } else if (p.compareDocumentPosition) {
2018 return !!(p.compareDocumentPosition(c) & 16);
2020 var parent = c.parentNode;
2025 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2028 parent = parent.parentNode;
2034 getRegion : function(el) {
2035 return Roo.lib.Region.getRegion(el);
2038 getY : function(el) {
2039 return this.getXY(el)[1];
2042 getX : function(el) {
2043 return this.getXY(el)[0];
2046 getXY : function(el) {
2047 var p, pe, b, scroll, bd = document.body;
2048 el = Roo.getDom(el);
2049 var fly = Roo.lib.AnimBase.fly;
2050 if (el.getBoundingClientRect) {
2051 b = el.getBoundingClientRect();
2052 scroll = fly(document).getScroll();
2053 return [b.left + scroll.left, b.top + scroll.top];
2059 var hasAbsolute = fly(el).getStyle("position") == "absolute";
2066 if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2073 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2074 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2081 if (p != el && pe.getStyle('overflow') != 'visible') {
2089 if (Roo.isSafari && hasAbsolute) {
2094 if (Roo.isGecko && !hasAbsolute) {
2096 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2097 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2101 while (p && p != bd) {
2102 if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2114 setXY : function(el, xy) {
2115 el = Roo.fly(el, '_setXY');
2117 var pts = el.translatePoints(xy);
2118 if (xy[0] !== false) {
2119 el.dom.style.left = pts.left + "px";
2121 if (xy[1] !== false) {
2122 el.dom.style.top = pts.top + "px";
2126 setX : function(el, x) {
2127 this.setXY(el, [x, false]);
2130 setY : function(el, y) {
2131 this.setXY(el, [false, y]);
2135 * Portions of this file are based on pieces of Yahoo User Interface Library
2136 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2137 * YUI licensed under the BSD License:
2138 * http://developer.yahoo.net/yui/license.txt
2139 * <script type="text/javascript">
2143 Roo.lib.Event = function() {
2144 var loadComplete = false;
2146 var unloadListeners = [];
2148 var onAvailStack = [];
2150 var lastError = null;
2163 startInterval: function() {
2164 if (!this._interval) {
2166 var callback = function() {
2167 self._tryPreloadAttach();
2169 this._interval = setInterval(callback, this.POLL_INTERVAL);
2174 onAvailable: function(p_id, p_fn, p_obj, p_override) {
2175 onAvailStack.push({ id: p_id,
2178 override: p_override,
2179 checkReady: false });
2181 retryCount = this.POLL_RETRYS;
2182 this.startInterval();
2186 addListener: function(el, eventName, fn) {
2187 el = Roo.getDom(el);
2192 if ("unload" == eventName) {
2193 unloadListeners[unloadListeners.length] =
2194 [el, eventName, fn];
2198 var wrappedFn = function(e) {
2199 return fn(Roo.lib.Event.getEvent(e));
2202 var li = [el, eventName, fn, wrappedFn];
2204 var index = listeners.length;
2205 listeners[index] = li;
2207 this.doAdd(el, eventName, wrappedFn, false);
2213 removeListener: function(el, eventName, fn) {
2216 el = Roo.getDom(el);
2219 return this.purgeElement(el, false, eventName);
2223 if ("unload" == eventName) {
2225 for (i = 0,len = unloadListeners.length; i < len; i++) {
2226 var li = unloadListeners[i];
2229 li[1] == eventName &&
2231 unloadListeners.splice(i, 1);
2239 var cacheItem = null;
2242 var index = arguments[3];
2244 if ("undefined" == typeof index) {
2245 index = this._getCacheIndex(el, eventName, fn);
2249 cacheItem = listeners[index];
2252 if (!el || !cacheItem) {
2256 this.doRemove(el, eventName, cacheItem[this.WFN], false);
2258 delete listeners[index][this.WFN];
2259 delete listeners[index][this.FN];
2260 listeners.splice(index, 1);
2267 getTarget: function(ev, resolveTextNode) {
2268 ev = ev.browserEvent || ev;
2269 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2270 var t = ev.target || ev.srcElement;
2271 return this.resolveTextNode(t);
2275 resolveTextNode: function(node) {
2276 if (Roo.isSafari && node && 3 == node.nodeType) {
2277 return node.parentNode;
2284 getPageX: function(ev) {
2285 ev = ev.browserEvent || ev;
2286 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2288 if (!x && 0 !== x) {
2289 x = ev.clientX || 0;
2292 x += this.getScroll()[1];
2300 getPageY: function(ev) {
2301 ev = ev.browserEvent || ev;
2302 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2304 if (!y && 0 !== y) {
2305 y = ev.clientY || 0;
2308 y += this.getScroll()[0];
2317 getXY: function(ev) {
2318 ev = ev.browserEvent || ev;
2319 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2320 return [this.getPageX(ev), this.getPageY(ev)];
2324 getRelatedTarget: function(ev) {
2325 ev = ev.browserEvent || ev;
2326 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2327 var t = ev.relatedTarget;
2329 if (ev.type == "mouseout") {
2331 } else if (ev.type == "mouseover") {
2336 return this.resolveTextNode(t);
2340 getTime: function(ev) {
2341 ev = ev.browserEvent || ev;
2342 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2344 var t = new Date().getTime();
2348 this.lastError = ex;
2357 stopEvent: function(ev) {
2358 this.stopPropagation(ev);
2359 this.preventDefault(ev);
2363 stopPropagation: function(ev) {
2364 ev = ev.browserEvent || ev;
2365 if (ev.stopPropagation) {
2366 ev.stopPropagation();
2368 ev.cancelBubble = true;
2373 preventDefault: function(ev) {
2374 ev = ev.browserEvent || ev;
2375 if(ev.preventDefault) {
2376 ev.preventDefault();
2378 ev.returnValue = false;
2383 getEvent: function(e) {
2384 var ev = e || window.event;
2386 var c = this.getEvent.caller;
2388 ev = c.arguments[0];
2389 if (ev && Event == ev.constructor) {
2399 getCharCode: function(ev) {
2400 ev = ev.browserEvent || ev;
2401 return ev.charCode || ev.keyCode || 0;
2405 _getCacheIndex: function(el, eventName, fn) {
2406 for (var i = 0,len = listeners.length; i < len; ++i) {
2407 var li = listeners[i];
2409 li[this.FN] == fn &&
2410 li[this.EL] == el &&
2411 li[this.TYPE] == eventName) {
2423 getEl: function(id) {
2424 return document.getElementById(id);
2428 clearCache: function() {
2432 _load: function(e) {
2433 loadComplete = true;
2434 var EU = Roo.lib.Event;
2438 EU.doRemove(window, "load", EU._load);
2443 _tryPreloadAttach: function() {
2452 var tryAgain = !loadComplete;
2454 tryAgain = (retryCount > 0);
2459 for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2460 var item = onAvailStack[i];
2462 var el = this.getEl(item.id);
2465 if (!item.checkReady ||
2468 (document && document.body)) {
2471 if (item.override) {
2472 if (item.override === true) {
2475 scope = item.override;
2478 item.fn.call(scope, item.obj);
2479 onAvailStack[i] = null;
2482 notAvail.push(item);
2487 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2491 this.startInterval();
2493 clearInterval(this._interval);
2494 this._interval = null;
2497 this.locked = false;
2504 purgeElement: function(el, recurse, eventName) {
2505 var elListeners = this.getListeners(el, eventName);
2507 for (var i = 0,len = elListeners.length; i < len; ++i) {
2508 var l = elListeners[i];
2509 this.removeListener(el, l.type, l.fn);
2513 if (recurse && el && el.childNodes) {
2514 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2515 this.purgeElement(el.childNodes[i], recurse, eventName);
2521 getListeners: function(el, eventName) {
2522 var results = [], searchLists;
2524 searchLists = [listeners, unloadListeners];
2525 } else if (eventName == "unload") {
2526 searchLists = [unloadListeners];
2528 searchLists = [listeners];
2531 for (var j = 0; j < searchLists.length; ++j) {
2532 var searchList = searchLists[j];
2533 if (searchList && searchList.length > 0) {
2534 for (var i = 0,len = searchList.length; i < len; ++i) {
2535 var l = searchList[i];
2536 if (l && l[this.EL] === el &&
2537 (!eventName || eventName === l[this.TYPE])) {
2542 adjust: l[this.ADJ_SCOPE],
2550 return (results.length) ? results : null;
2554 _unload: function(e) {
2556 var EU = Roo.lib.Event, i, j, l, len, index;
2558 for (i = 0,len = unloadListeners.length; i < len; ++i) {
2559 l = unloadListeners[i];
2562 if (l[EU.ADJ_SCOPE]) {
2563 if (l[EU.ADJ_SCOPE] === true) {
2566 scope = l[EU.ADJ_SCOPE];
2569 l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2570 unloadListeners[i] = null;
2576 unloadListeners = null;
2578 if (listeners && listeners.length > 0) {
2579 j = listeners.length;
2582 l = listeners[index];
2584 EU.removeListener(l[EU.EL], l[EU.TYPE],
2594 EU.doRemove(window, "unload", EU._unload);
2599 getScroll: function() {
2600 var dd = document.documentElement, db = document.body;
2601 if (dd && (dd.scrollTop || dd.scrollLeft)) {
2602 return [dd.scrollTop, dd.scrollLeft];
2604 return [db.scrollTop, db.scrollLeft];
2611 doAdd: function () {
2612 if (window.addEventListener) {
2613 return function(el, eventName, fn, capture) {
2614 el.addEventListener(eventName, fn, (capture));
2616 } else if (window.attachEvent) {
2617 return function(el, eventName, fn, capture) {
2618 el.attachEvent("on" + eventName, fn);
2627 doRemove: function() {
2628 if (window.removeEventListener) {
2629 return function (el, eventName, fn, capture) {
2630 el.removeEventListener(eventName, fn, (capture));
2632 } else if (window.detachEvent) {
2633 return function (el, eventName, fn) {
2634 el.detachEvent("on" + eventName, fn);
2646 var E = Roo.lib.Event;
2647 E.on = E.addListener;
2648 E.un = E.removeListener;
2650 if (document && document.body) {
2653 E.doAdd(window, "load", E._load);
2655 E.doAdd(window, "unload", E._unload);
2656 E._tryPreloadAttach();
2663 * @class Roo.lib.Ajax
2665 * provide a simple Ajax request utility functions
2667 * Portions of this file are based on pieces of Yahoo User Interface Library
2668 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2669 * YUI licensed under the BSD License:
2670 * http://developer.yahoo.net/yui/license.txt
2671 * <script type="text/javascript">
2679 request : function(method, uri, cb, data, options) {
2681 var hs = options.headers;
2684 if(hs.hasOwnProperty(h)){
2685 this.initHeader(h, hs[h], false);
2689 if(options.xmlData){
2690 this.initHeader('Content-Type', 'text/xml', false);
2692 data = options.xmlData;
2696 return this.asyncRequest(method, uri, cb, data);
2702 * @param {DomForm} form element
2703 * @return {String} urlencode form output.
2705 serializeForm : function(form) {
2706 if(typeof form == 'string') {
2707 form = (document.getElementById(form) || document.forms[form]);
2710 var el, name, val, disabled, data = '', hasSubmit = false;
2711 for (var i = 0; i < form.elements.length; i++) {
2712 el = form.elements[i];
2713 disabled = form.elements[i].disabled;
2714 name = form.elements[i].name;
2715 val = form.elements[i].value;
2717 if (!disabled && name){
2721 case 'select-multiple':
2722 for (var j = 0; j < el.options.length; j++) {
2723 if (el.options[j].selected) {
2725 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2728 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2736 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2749 if(hasSubmit == false) {
2750 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2755 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2760 data = data.substr(0, data.length - 1);
2768 useDefaultHeader:true,
2770 defaultPostHeader:'application/x-www-form-urlencoded',
2772 useDefaultXhrHeader:true,
2774 defaultXhrHeader:'XMLHttpRequest',
2776 hasDefaultHeaders:true,
2788 setProgId:function(id)
2790 this.activeX.unshift(id);
2793 setDefaultPostHeader:function(b)
2795 this.useDefaultHeader = b;
2798 setDefaultXhrHeader:function(b)
2800 this.useDefaultXhrHeader = b;
2803 setPollingInterval:function(i)
2805 if (typeof i == 'number' && isFinite(i)) {
2806 this.pollInterval = i;
2810 createXhrObject:function(transactionId)
2816 http = new XMLHttpRequest();
2818 obj = { conn:http, tId:transactionId };
2822 for (var i = 0; i < this.activeX.length; ++i) {
2826 http = new ActiveXObject(this.activeX[i]);
2828 obj = { conn:http, tId:transactionId };
2841 getConnectionObject:function()
2844 var tId = this.transactionId;
2848 o = this.createXhrObject(tId);
2850 this.transactionId++;
2861 asyncRequest:function(method, uri, callback, postData)
2863 var o = this.getConnectionObject();
2869 o.conn.open(method, uri, true);
2871 if (this.useDefaultXhrHeader) {
2872 if (!this.defaultHeaders['X-Requested-With']) {
2873 this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2877 if(postData && this.useDefaultHeader){
2878 this.initHeader('Content-Type', this.defaultPostHeader);
2881 if (this.hasDefaultHeaders || this.hasHeaders) {
2885 this.handleReadyState(o, callback);
2886 o.conn.send(postData || null);
2892 handleReadyState:function(o, callback)
2896 if (callback && callback.timeout) {
2898 this.timeout[o.tId] = window.setTimeout(function() {
2899 oConn.abort(o, callback, true);
2900 }, callback.timeout);
2903 this.poll[o.tId] = window.setInterval(
2905 if (o.conn && o.conn.readyState == 4) {
2906 window.clearInterval(oConn.poll[o.tId]);
2907 delete oConn.poll[o.tId];
2909 if(callback && callback.timeout) {
2910 window.clearTimeout(oConn.timeout[o.tId]);
2911 delete oConn.timeout[o.tId];
2914 oConn.handleTransactionResponse(o, callback);
2917 , this.pollInterval);
2920 handleTransactionResponse:function(o, callback, isAbort)
2924 this.releaseObject(o);
2928 var httpStatus, responseObject;
2932 if (o.conn.status !== undefined && o.conn.status != 0) {
2933 httpStatus = o.conn.status;
2945 if (httpStatus >= 200 && httpStatus < 300) {
2946 responseObject = this.createResponseObject(o, callback.argument);
2947 if (callback.success) {
2948 if (!callback.scope) {
2949 callback.success(responseObject);
2954 callback.success.apply(callback.scope, [responseObject]);
2959 switch (httpStatus) {
2967 responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2968 if (callback.failure) {
2969 if (!callback.scope) {
2970 callback.failure(responseObject);
2973 callback.failure.apply(callback.scope, [responseObject]);
2978 responseObject = this.createResponseObject(o, callback.argument);
2979 if (callback.failure) {
2980 if (!callback.scope) {
2981 callback.failure(responseObject);
2984 callback.failure.apply(callback.scope, [responseObject]);
2990 this.releaseObject(o);
2991 responseObject = null;
2994 createResponseObject:function(o, callbackArg)
3001 var headerStr = o.conn.getAllResponseHeaders();
3002 var header = headerStr.split('\n');
3003 for (var i = 0; i < header.length; i++) {
3004 var delimitPos = header[i].indexOf(':');
3005 if (delimitPos != -1) {
3006 headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3014 obj.status = o.conn.status;
3015 obj.statusText = o.conn.statusText;
3016 obj.getResponseHeader = headerObj;
3017 obj.getAllResponseHeaders = headerStr;
3018 obj.responseText = o.conn.responseText;
3019 obj.responseXML = o.conn.responseXML;
3021 if (typeof callbackArg !== undefined) {
3022 obj.argument = callbackArg;
3028 createExceptionObject:function(tId, callbackArg, isAbort)
3031 var COMM_ERROR = 'communication failure';
3032 var ABORT_CODE = -1;
3033 var ABORT_ERROR = 'transaction aborted';
3039 obj.status = ABORT_CODE;
3040 obj.statusText = ABORT_ERROR;
3043 obj.status = COMM_CODE;
3044 obj.statusText = COMM_ERROR;
3048 obj.argument = callbackArg;
3054 initHeader:function(label, value, isDefault)
3056 var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3058 if (headerObj[label] === undefined) {
3059 headerObj[label] = value;
3064 headerObj[label] = value + "," + headerObj[label];
3068 this.hasDefaultHeaders = true;
3071 this.hasHeaders = true;
3076 setHeader:function(o)
3078 if (this.hasDefaultHeaders) {
3079 for (var prop in this.defaultHeaders) {
3080 if (this.defaultHeaders.hasOwnProperty(prop)) {
3081 o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3086 if (this.hasHeaders) {
3087 for (var prop in this.headers) {
3088 if (this.headers.hasOwnProperty(prop)) {
3089 o.conn.setRequestHeader(prop, this.headers[prop]);
3093 this.hasHeaders = false;
3097 resetDefaultHeaders:function() {
3098 delete this.defaultHeaders;
3099 this.defaultHeaders = {};
3100 this.hasDefaultHeaders = false;
3103 abort:function(o, callback, isTimeout)
3105 if(this.isCallInProgress(o)) {
3107 window.clearInterval(this.poll[o.tId]);
3108 delete this.poll[o.tId];
3110 delete this.timeout[o.tId];
3113 this.handleTransactionResponse(o, callback, true);
3123 isCallInProgress:function(o)
3126 return o.conn.readyState != 4 && o.conn.readyState != 0;
3135 releaseObject:function(o)
3144 'MSXML2.XMLHTTP.3.0',
3152 * Portions of this file are based on pieces of Yahoo User Interface Library
3153 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3154 * YUI licensed under the BSD License:
3155 * http://developer.yahoo.net/yui/license.txt
3156 * <script type="text/javascript">
3160 Roo.lib.Region = function(t, r, b, l) {
3170 Roo.lib.Region.prototype = {
3171 contains : function(region) {
3172 return ( region.left >= this.left &&
3173 region.right <= this.right &&
3174 region.top >= this.top &&
3175 region.bottom <= this.bottom );
3179 getArea : function() {
3180 return ( (this.bottom - this.top) * (this.right - this.left) );
3183 intersect : function(region) {
3184 var t = Math.max(this.top, region.top);
3185 var r = Math.min(this.right, region.right);
3186 var b = Math.min(this.bottom, region.bottom);
3187 var l = Math.max(this.left, region.left);
3189 if (b >= t && r >= l) {
3190 return new Roo.lib.Region(t, r, b, l);
3195 union : function(region) {
3196 var t = Math.min(this.top, region.top);
3197 var r = Math.max(this.right, region.right);
3198 var b = Math.max(this.bottom, region.bottom);
3199 var l = Math.min(this.left, region.left);
3201 return new Roo.lib.Region(t, r, b, l);
3204 adjust : function(t, l, b, r) {
3213 Roo.lib.Region.getRegion = function(el) {
3214 var p = Roo.lib.Dom.getXY(el);
3217 var r = p[0] + el.offsetWidth;
3218 var b = p[1] + el.offsetHeight;
3221 return new Roo.lib.Region(t, r, b, l);
3224 * Portions of this file are based on pieces of Yahoo User Interface Library
3225 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3226 * YUI licensed under the BSD License:
3227 * http://developer.yahoo.net/yui/license.txt
3228 * <script type="text/javascript">
3231 //@@dep Roo.lib.Region
3234 Roo.lib.Point = function(x, y) {
3235 if (x instanceof Array) {
3239 this.x = this.right = this.left = this[0] = x;
3240 this.y = this.top = this.bottom = this[1] = y;
3243 Roo.lib.Point.prototype = new Roo.lib.Region();
3245 * Portions of this file are based on pieces of Yahoo User Interface Library
3246 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3247 * YUI licensed under the BSD License:
3248 * http://developer.yahoo.net/yui/license.txt
3249 * <script type="text/javascript">
3256 scroll : function(el, args, duration, easing, cb, scope) {
3257 this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3260 motion : function(el, args, duration, easing, cb, scope) {
3261 this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3264 color : function(el, args, duration, easing, cb, scope) {
3265 this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3268 run : function(el, args, duration, easing, cb, scope, type) {
3269 type = type || Roo.lib.AnimBase;
3270 if (typeof easing == "string") {
3271 easing = Roo.lib.Easing[easing];
3273 var anim = new type(el, args, duration, easing);
3274 anim.animateX(function() {
3275 Roo.callback(cb, scope);
3281 * Portions of this file are based on pieces of Yahoo User Interface Library
3282 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3283 * YUI licensed under the BSD License:
3284 * http://developer.yahoo.net/yui/license.txt
3285 * <script type="text/javascript">
3293 if (!libFlyweight) {
3294 libFlyweight = new Roo.Element.Flyweight();
3296 libFlyweight.dom = el;
3297 return libFlyweight;
3300 // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3304 Roo.lib.AnimBase = function(el, attributes, duration, method) {
3306 this.init(el, attributes, duration, method);
3310 Roo.lib.AnimBase.fly = fly;
3314 Roo.lib.AnimBase.prototype = {
3316 toString: function() {
3317 var el = this.getEl();
3318 var id = el.id || el.tagName;
3319 return ("Anim " + id);
3323 noNegatives: /width|height|opacity|padding/i,
3324 offsetAttribute: /^((width|height)|(top|left))$/,
3325 defaultUnit: /width|height|top$|bottom$|left$|right$/i,
3326 offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3330 doMethod: function(attr, start, end) {
3331 return this.method(this.currentFrame, start, end - start, this.totalFrames);
3335 setAttribute: function(attr, val, unit) {
3336 if (this.patterns.noNegatives.test(attr)) {
3337 val = (val > 0) ? val : 0;
3340 Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3344 getAttribute: function(attr) {
3345 var el = this.getEl();
3346 var val = fly(el).getStyle(attr);
3348 if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3349 return parseFloat(val);
3352 var a = this.patterns.offsetAttribute.exec(attr) || [];
3353 var pos = !!( a[3] );
3354 var box = !!( a[2] );
3357 if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3358 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3367 getDefaultUnit: function(attr) {
3368 if (this.patterns.defaultUnit.test(attr)) {
3375 animateX : function(callback, scope) {
3376 var f = function() {
3377 this.onComplete.removeListener(f);
3378 if (typeof callback == "function") {
3379 callback.call(scope || this, this);
3382 this.onComplete.addListener(f, this);
3387 setRuntimeAttribute: function(attr) {
3390 var attributes = this.attributes;
3392 this.runtimeAttributes[attr] = {};
3394 var isset = function(prop) {
3395 return (typeof prop !== 'undefined');
3398 if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3402 start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3405 if (isset(attributes[attr]['to'])) {
3406 end = attributes[attr]['to'];
3407 } else if (isset(attributes[attr]['by'])) {
3408 if (start.constructor == Array) {
3410 for (var i = 0, len = start.length; i < len; ++i) {
3411 end[i] = start[i] + attributes[attr]['by'][i];
3414 end = start + attributes[attr]['by'];
3418 this.runtimeAttributes[attr].start = start;
3419 this.runtimeAttributes[attr].end = end;
3422 this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3426 init: function(el, attributes, duration, method) {
3428 var isAnimated = false;
3431 var startTime = null;
3434 var actualFrames = 0;
3437 el = Roo.getDom(el);
3440 this.attributes = attributes || {};
3443 this.duration = duration || 1;
3446 this.method = method || Roo.lib.Easing.easeNone;
3449 this.useSeconds = true;
3452 this.currentFrame = 0;
3455 this.totalFrames = Roo.lib.AnimMgr.fps;
3458 this.getEl = function() {
3463 this.isAnimated = function() {
3468 this.getStartTime = function() {
3472 this.runtimeAttributes = {};
3475 this.animate = function() {
3476 if (this.isAnimated()) {
3480 this.currentFrame = 0;
3482 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3484 Roo.lib.AnimMgr.registerElement(this);
3488 this.stop = function(finish) {
3490 this.currentFrame = this.totalFrames;
3491 this._onTween.fire();
3493 Roo.lib.AnimMgr.stop(this);
3496 var onStart = function() {
3497 this.onStart.fire();
3499 this.runtimeAttributes = {};
3500 for (var attr in this.attributes) {
3501 this.setRuntimeAttribute(attr);
3506 startTime = new Date();
3510 var onTween = function() {
3512 duration: new Date() - this.getStartTime(),
3513 currentFrame: this.currentFrame
3516 data.toString = function() {
3518 'duration: ' + data.duration +
3519 ', currentFrame: ' + data.currentFrame
3523 this.onTween.fire(data);
3525 var runtimeAttributes = this.runtimeAttributes;
3527 for (var attr in runtimeAttributes) {
3528 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3534 var onComplete = function() {
3535 var actual_duration = (new Date() - startTime) / 1000 ;
3538 duration: actual_duration,
3539 frames: actualFrames,
3540 fps: actualFrames / actual_duration
3543 data.toString = function() {
3545 'duration: ' + data.duration +
3546 ', frames: ' + data.frames +
3547 ', fps: ' + data.fps
3553 this.onComplete.fire(data);
3557 this._onStart = new Roo.util.Event(this);
3558 this.onStart = new Roo.util.Event(this);
3559 this.onTween = new Roo.util.Event(this);
3560 this._onTween = new Roo.util.Event(this);
3561 this.onComplete = new Roo.util.Event(this);
3562 this._onComplete = new Roo.util.Event(this);
3563 this._onStart.addListener(onStart);
3564 this._onTween.addListener(onTween);
3565 this._onComplete.addListener(onComplete);
3570 * Portions of this file are based on pieces of Yahoo User Interface Library
3571 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3572 * YUI licensed under the BSD License:
3573 * http://developer.yahoo.net/yui/license.txt
3574 * <script type="text/javascript">
3578 Roo.lib.AnimMgr = new function() {
3595 this.registerElement = function(tween) {
3596 queue[queue.length] = tween;
3598 tween._onStart.fire();
3603 this.unRegister = function(tween, index) {
3604 tween._onComplete.fire();
3605 index = index || getIndex(tween);
3607 queue.splice(index, 1);
3611 if (tweenCount <= 0) {
3617 this.start = function() {
3618 if (thread === null) {
3619 thread = setInterval(this.run, this.delay);
3624 this.stop = function(tween) {
3626 clearInterval(thread);
3628 for (var i = 0, len = queue.length; i < len; ++i) {
3629 if (queue[0].isAnimated()) {
3630 this.unRegister(queue[0], 0);
3639 this.unRegister(tween);
3644 this.run = function() {
3645 for (var i = 0, len = queue.length; i < len; ++i) {
3646 var tween = queue[i];
3647 if (!tween || !tween.isAnimated()) {
3651 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3653 tween.currentFrame += 1;
3655 if (tween.useSeconds) {
3656 correctFrame(tween);
3658 tween._onTween.fire();
3661 Roo.lib.AnimMgr.stop(tween, i);
3666 var getIndex = function(anim) {
3667 for (var i = 0, len = queue.length; i < len; ++i) {
3668 if (queue[i] == anim) {
3676 var correctFrame = function(tween) {
3677 var frames = tween.totalFrames;
3678 var frame = tween.currentFrame;
3679 var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3680 var elapsed = (new Date() - tween.getStartTime());
3683 if (elapsed < tween.duration * 1000) {
3684 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3686 tweak = frames - (frame + 1);
3688 if (tweak > 0 && isFinite(tweak)) {
3689 if (tween.currentFrame + tweak >= frames) {
3690 tweak = frames - (frame + 1);
3693 tween.currentFrame += tweak;
3699 * Portions of this file are based on pieces of Yahoo User Interface Library
3700 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701 * YUI licensed under the BSD License:
3702 * http://developer.yahoo.net/yui/license.txt
3703 * <script type="text/javascript">
3706 Roo.lib.Bezier = new function() {
3708 this.getPosition = function(points, t) {
3709 var n = points.length;
3712 for (var i = 0; i < n; ++i) {
3713 tmp[i] = [points[i][0], points[i][1]];
3716 for (var j = 1; j < n; ++j) {
3717 for (i = 0; i < n - j; ++i) {
3718 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3719 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3723 return [ tmp[0][0], tmp[0][1] ];
3729 * @class Roo.lib.Color
3731 * An abstract Color implementation. Concrete Color implementations should use
3732 * an instance of this function as their prototype, and implement the getRGB and
3733 * getHSL functions. getRGB should return an object representing the RGB
3734 * components of this Color, with the red, green, and blue components in the
3735 * range [0,255] and the alpha component in the range [0,100]. getHSL should
3736 * return an object representing the HSL components of this Color, with the hue
3737 * component in the range [0,360), the saturation and lightness components in
3738 * the range [0,100], and the alpha component in the range [0,1].
3743 * Functions for Color handling and processing.
3745 * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3747 * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3748 * rights to this program, with the intention of it becoming part of the public
3749 * domain. Because this program is released into the public domain, it comes with
3750 * no warranty either expressed or implied, to the extent permitted by law.
3752 * For more free and public domain JavaScript code by the same author, visit:
3753 * http://www.safalra.com/web-design/javascript/
3756 Roo.lib.Color = function() { }
3759 Roo.apply(Roo.lib.Color.prototype, {
3767 * @return {Object} an object representing the RGBA components of this Color. The red,
3768 * green, and blue components are converted to integers in the range [0,255].
3769 * The alpha is a value in the range [0,1].
3771 getIntegerRGB : function(){
3773 // get the RGB components of this Color
3774 var rgb = this.getRGB();
3776 // return the integer components
3778 'r' : Math.round(rgb.r),
3779 'g' : Math.round(rgb.g),
3780 'b' : Math.round(rgb.b),
3788 * @return {Object} an object representing the RGBA components of this Color. The red,
3789 * green, and blue components are converted to numbers in the range [0,100].
3790 * The alpha is a value in the range [0,1].
3792 getPercentageRGB : function(){
3794 // get the RGB components of this Color
3795 var rgb = this.getRGB();
3797 // return the percentage components
3799 'r' : 100 * rgb.r / 255,
3800 'g' : 100 * rgb.g / 255,
3801 'b' : 100 * rgb.b / 255,
3808 * getCSSHexadecimalRGB
3809 * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3810 * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3811 * are two-digit hexadecimal numbers.
3813 getCSSHexadecimalRGB : function()
3816 // get the integer RGB components
3817 var rgb = this.getIntegerRGB();
3819 // determine the hexadecimal equivalents
3820 var r16 = rgb.r.toString(16);
3821 var g16 = rgb.g.toString(16);
3822 var b16 = rgb.b.toString(16);
3824 // return the CSS RGB Color value
3826 + (r16.length == 2 ? r16 : '0' + r16)
3827 + (g16.length == 2 ? g16 : '0' + g16)
3828 + (b16.length == 2 ? b16 : '0' + b16);
3834 * @return {String} a string representing this Color as a CSS integer RGB Color
3835 * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3836 * are integers in the range [0,255].
3838 getCSSIntegerRGB : function(){
3840 // get the integer RGB components
3841 var rgb = this.getIntegerRGB();
3843 // return the CSS RGB Color value
3844 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3850 * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3851 * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3852 * b are integers in the range [0,255] and a is in the range [0,1].
3854 getCSSIntegerRGBA : function(){
3856 // get the integer RGB components
3857 var rgb = this.getIntegerRGB();
3859 // return the CSS integer RGBA Color value
3860 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3865 * getCSSPercentageRGB
3866 * @return {String} a string representing this Color as a CSS percentage RGB Color
3867 * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3868 * b are in the range [0,100].
3870 getCSSPercentageRGB : function(){
3872 // get the percentage RGB components
3873 var rgb = this.getPercentageRGB();
3875 // return the CSS RGB Color value
3876 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3881 * getCSSPercentageRGBA
3882 * @return {String} a string representing this Color as a CSS percentage RGBA Color
3883 * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3884 * and b are in the range [0,100] and a is in the range [0,1].
3886 getCSSPercentageRGBA : function(){
3888 // get the percentage RGB components
3889 var rgb = this.getPercentageRGB();
3891 // return the CSS percentage RGBA Color value
3892 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3898 * @return {String} a string representing this Color as a CSS HSL Color value - that
3899 * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3900 * s and l are in the range [0,100].
3902 getCSSHSL : function(){
3904 // get the HSL components
3905 var hsl = this.getHSL();
3907 // return the CSS HSL Color value
3908 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3914 * @return {String} a string representing this Color as a CSS HSLA Color value - that
3915 * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3916 * s and l are in the range [0,100], and a is in the range [0,1].
3918 getCSSHSLA : function(){
3920 // get the HSL components
3921 var hsl = this.getHSL();
3923 // return the CSS HSL Color value
3924 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3929 * Sets the Color of the specified node to this Color. This functions sets
3930 * the CSS 'color' property for the node. The parameter is:
3932 * @param {DomElement} node - the node whose Color should be set
3934 setNodeColor : function(node){
3936 // set the Color of the node
3937 node.style.color = this.getCSSHexadecimalRGB();
3942 * Sets the background Color of the specified node to this Color. This
3943 * functions sets the CSS 'background-color' property for the node. The
3946 * @param {DomElement} node - the node whose background Color should be set
3948 setNodeBackgroundColor : function(node){
3950 // set the background Color of the node
3951 node.style.backgroundColor = this.getCSSHexadecimalRGB();
3954 // convert between formats..
3957 var r = this.getIntegerRGB();
3958 return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3963 var hsl = this.getHSL();
3964 // return the CSS HSL Color value
3965 return new Roo.lib.HSLColor(hsl.h, hsl.s, hsl.l , hsl.a );
3971 var rgb = this.toRGB();
3972 var hsv = rgb.getHSV();
3973 // return the CSS HSL Color value
3974 return new Roo.lib.HSVColor(hsv.h, hsv.s, hsv.v , hsv.a );
3978 // modify v = 0 ... 1 (eg. 0.5)
3979 saturate : function(v)
3981 var rgb = this.toRGB();
3982 var hsv = rgb.getHSV();
3983 return new Roo.lib.HSVColor(hsv.h, hsv.s * v, hsv.v , hsv.a );
3991 * @return {Object} the RGB and alpha components of this Color as an object with r,
3992 * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3997 // return the RGB components
4009 * @return {Object} the HSV and alpha components of this Color as an object with h,
4010 * s, v, and a properties. h is in the range [0,360), s and v are in the range
4011 * [0,100], and a is in the range [0,1].
4016 // calculate the HSV components if necessary
4017 if (this.hsv == null) {
4018 this.calculateHSV();
4021 // return the HSV components
4033 * @return {Object} the HSL and alpha components of this Color as an object with h,
4034 * s, l, and a properties. h is in the range [0,360), s and l are in the range
4035 * [0,100], and a is in the range [0,1].
4037 getHSL : function(){
4040 // calculate the HSV components if necessary
4041 if (this.hsl == null) { this.calculateHSL(); }
4043 // return the HSL components
4058 * @class Roo.lib.RGBColor
4059 * @extends Roo.lib.Color
4060 * Creates a Color specified in the RGB Color space, with an optional alpha
4061 * component. The parameters are:
4065 * @param {Number} r - the red component, clipped to the range [0,255]
4066 * @param {Number} g - the green component, clipped to the range [0,255]
4067 * @param {Number} b - the blue component, clipped to the range [0,255]
4068 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4069 * optional and defaults to 1
4071 Roo.lib.RGBColor = function (r, g, b, a){
4073 // store the alpha component after clipping it if necessary
4074 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4076 // store the RGB components after clipping them if necessary
4079 'r' : Math.max(0, Math.min(255, r)),
4080 'g' : Math.max(0, Math.min(255, g)),
4081 'b' : Math.max(0, Math.min(255, b))
4084 // initialise the HSV and HSL components to null
4088 * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4089 * range [0,360). The parameters are:
4091 * maximum - the maximum of the RGB component values
4092 * range - the range of the RGB component values
4097 // this does an 'exteds'
4098 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4101 getHue : function(maximum, range)
4105 // check whether the range is zero
4108 // set the hue to zero (any hue is acceptable as the Color is grey)
4113 // determine which of the components has the highest value and set the hue
4116 // red has the highest value
4118 var hue = (rgb.g - rgb.b) / range * 60;
4119 if (hue < 0) { hue += 360; }
4122 // green has the highest value
4124 var hue = (rgb.b - rgb.r) / range * 60 + 120;
4127 // blue has the highest value
4129 var hue = (rgb.r - rgb.g) / range * 60 + 240;
4141 /* //private Calculates and stores the HSV components of this RGBColor so that they can
4142 * be returned be the getHSV function.
4144 calculateHSV : function(){
4146 // get the maximum and range of the RGB component values
4147 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4148 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4150 // store the HSV components
4153 'h' : this.getHue(maximum, range),
4154 's' : (maximum == 0 ? 0 : 100 * range / maximum),
4155 'v' : maximum / 2.55
4160 /* //private Calculates and stores the HSL components of this RGBColor so that they can
4161 * be returned be the getHSL function.
4163 calculateHSL : function(){
4165 // get the maximum and range of the RGB component values
4166 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4167 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4169 // determine the lightness in the range [0,1]
4170 var l = maximum / 255 - range / 510;
4172 // store the HSL components
4175 'h' : this.getHue(maximum, range),
4176 's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4185 * @class Roo.lib.HSVColor
4186 * @extends Roo.lib.Color
4187 * Creates a Color specified in the HSV Color space, with an optional alpha
4188 * component. The parameters are:
4191 * @param {Number} h - the hue component, wrapped to the range [0,360)
4192 * @param {Number} s - the saturation component, clipped to the range [0,100]
4193 * @param {Number} v - the value component, clipped to the range [0,100]
4194 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4195 * optional and defaults to 1
4197 Roo.lib.HSVColor = function (h, s, v, a){
4199 // store the alpha component after clipping it if necessary
4200 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4202 // store the HSV components after clipping or wrapping them if necessary
4205 'h' : (h % 360 + 360) % 360,
4206 's' : Math.max(0, Math.min(100, s)),
4207 'v' : Math.max(0, Math.min(100, v))
4210 // initialise the RGB and HSL components to null
4215 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4216 /* Calculates and stores the RGB components of this HSVColor so that they can
4217 * be returned be the getRGB function.
4219 calculateRGB: function ()
4222 // check whether the saturation is zero
4225 // set the Color to the appropriate shade of grey
4232 // set some temporary values
4233 var f = hsv.h / 60 - Math.floor(hsv.h / 60);
4234 var p = hsv.v * (1 - hsv.s / 100);
4235 var q = hsv.v * (1 - hsv.s / 100 * f);
4236 var t = hsv.v * (1 - hsv.s / 100 * (1 - f));
4238 // set the RGB Color components to their temporary values
4239 switch (Math.floor(hsv.h / 60)){
4240 case 0: var r = hsv.v; var g = t; var b = p; break;
4241 case 1: var r = q; var g = hsv.v; var b = p; break;
4242 case 2: var r = p; var g = hsv.v; var b = t; break;
4243 case 3: var r = p; var g = q; var b = hsv.v; break;
4244 case 4: var r = t; var g = p; var b = hsv.v; break;
4245 case 5: var r = hsv.v; var g = p; var b = q; break;
4250 // store the RGB components
4260 /* Calculates and stores the HSL components of this HSVColor so that they can
4261 * be returned be the getHSL function.
4263 calculateHSL : function (){
4266 // determine the lightness in the range [0,100]
4267 var l = (2 - hsv.s / 100) * hsv.v / 2;
4269 // store the HSL components
4273 's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4277 // correct a division-by-zero error
4278 if (isNaN(hsl.s)) { hsl.s = 0; }
4287 * @class Roo.lib.HSLColor
4288 * @extends Roo.lib.Color
4291 * Creates a Color specified in the HSL Color space, with an optional alpha
4292 * component. The parameters are:
4294 * @param {Number} h - the hue component, wrapped to the range [0,360)
4295 * @param {Number} s - the saturation component, clipped to the range [0,100]
4296 * @param {Number} l - the lightness component, clipped to the range [0,100]
4297 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4298 * optional and defaults to 1
4301 Roo.lib.HSLColor = function(h, s, l, a){
4303 // store the alpha component after clipping it if necessary
4304 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4306 // store the HSL components after clipping or wrapping them if necessary
4309 'h' : (h % 360 + 360) % 360,
4310 's' : Math.max(0, Math.min(100, s)),
4311 'l' : Math.max(0, Math.min(100, l))
4314 // initialise the RGB and HSV components to null
4317 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4319 /* Calculates and stores the RGB components of this HSLColor so that they can
4320 * be returned be the getRGB function.
4322 calculateRGB: function (){
4324 // check whether the saturation is zero
4325 if (this.hsl.s == 0){
4327 // store the RGB components representing the appropriate shade of grey
4330 'r' : this.hsl.l * 2.55,
4331 'g' : this.hsl.l * 2.55,
4332 'b' : this.hsl.l * 2.55
4337 // set some temporary values
4338 var p = this.hsl.l < 50
4339 ? this.hsl.l * (1 + hsl.s / 100)
4340 : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4341 var q = 2 * hsl.l - p;
4343 // initialise the RGB components
4346 'r' : (h + 120) / 60 % 6,
4348 'b' : (h + 240) / 60 % 6
4351 // loop over the RGB components
4352 for (var key in this.rgb){
4354 // ensure that the property is not inherited from the root object
4355 if (this.rgb.hasOwnProperty(key)){
4357 // set the component to its value in the range [0,100]
4358 if (this.rgb[key] < 1){
4359 this.rgb[key] = q + (p - q) * this.rgb[key];
4360 }else if (this.rgb[key] < 3){
4362 }else if (this.rgb[key] < 4){
4363 this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4368 // set the component to its value in the range [0,255]
4369 this.rgb[key] *= 2.55;
4379 /* Calculates and stores the HSV components of this HSLColor so that they can
4380 * be returned be the getHSL function.
4382 calculateHSV : function(){
4384 // set a temporary value
4385 var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4387 // store the HSV components
4391 's' : 200 * t / (this.hsl.l + t),
4392 'v' : t + this.hsl.l
4395 // correct a division-by-zero error
4396 if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4403 * Portions of this file are based on pieces of Yahoo User Interface Library
4404 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4405 * YUI licensed under the BSD License:
4406 * http://developer.yahoo.net/yui/license.txt
4407 * <script type="text/javascript">
4412 Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4413 Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4416 Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4418 var fly = Roo.lib.AnimBase.fly;
4420 var superclass = Y.ColorAnim.superclass;
4421 var proto = Y.ColorAnim.prototype;
4423 proto.toString = function() {
4424 var el = this.getEl();
4425 var id = el.id || el.tagName;
4426 return ("ColorAnim " + id);
4429 proto.patterns.color = /color$/i;
4430 proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4431 proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4432 proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4433 proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4436 proto.parseColor = function(s) {
4437 if (s.length == 3) {
4441 var c = this.patterns.hex.exec(s);
4442 if (c && c.length == 4) {
4443 return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4446 c = this.patterns.rgb.exec(s);
4447 if (c && c.length == 4) {
4448 return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4451 c = this.patterns.hex3.exec(s);
4452 if (c && c.length == 4) {
4453 return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4458 // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4459 proto.getAttribute = function(attr) {
4460 var el = this.getEl();
4461 if (this.patterns.color.test(attr)) {
4462 var val = fly(el).getStyle(attr);
4464 if (this.patterns.transparent.test(val)) {
4465 var parent = el.parentNode;
4466 val = fly(parent).getStyle(attr);
4468 while (parent && this.patterns.transparent.test(val)) {
4469 parent = parent.parentNode;
4470 val = fly(parent).getStyle(attr);
4471 if (parent.tagName.toUpperCase() == 'HTML') {
4477 val = superclass.getAttribute.call(this, attr);
4482 proto.getAttribute = function(attr) {
4483 var el = this.getEl();
4484 if (this.patterns.color.test(attr)) {
4485 var val = fly(el).getStyle(attr);
4487 if (this.patterns.transparent.test(val)) {
4488 var parent = el.parentNode;
4489 val = fly(parent).getStyle(attr);
4491 while (parent && this.patterns.transparent.test(val)) {
4492 parent = parent.parentNode;
4493 val = fly(parent).getStyle(attr);
4494 if (parent.tagName.toUpperCase() == 'HTML') {
4500 val = superclass.getAttribute.call(this, attr);
4506 proto.doMethod = function(attr, start, end) {
4509 if (this.patterns.color.test(attr)) {
4511 for (var i = 0, len = start.length; i < len; ++i) {
4512 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4515 val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4518 val = superclass.doMethod.call(this, attr, start, end);
4524 proto.setRuntimeAttribute = function(attr) {
4525 superclass.setRuntimeAttribute.call(this, attr);
4527 if (this.patterns.color.test(attr)) {
4528 var attributes = this.attributes;
4529 var start = this.parseColor(this.runtimeAttributes[attr].start);
4530 var end = this.parseColor(this.runtimeAttributes[attr].end);
4532 if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4533 end = this.parseColor(attributes[attr].by);
4535 for (var i = 0, len = start.length; i < len; ++i) {
4536 end[i] = start[i] + end[i];
4540 this.runtimeAttributes[attr].start = start;
4541 this.runtimeAttributes[attr].end = end;
4547 * Portions of this file are based on pieces of Yahoo User Interface Library
4548 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4549 * YUI licensed under the BSD License:
4550 * http://developer.yahoo.net/yui/license.txt
4551 * <script type="text/javascript">
4557 easeNone: function (t, b, c, d) {
4558 return c * t / d + b;
4562 easeIn: function (t, b, c, d) {
4563 return c * (t /= d) * t + b;
4567 easeOut: function (t, b, c, d) {
4568 return -c * (t /= d) * (t - 2) + b;
4572 easeBoth: function (t, b, c, d) {
4573 if ((t /= d / 2) < 1) {
4574 return c / 2 * t * t + b;
4577 return -c / 2 * ((--t) * (t - 2) - 1) + b;
4581 easeInStrong: function (t, b, c, d) {
4582 return c * (t /= d) * t * t * t + b;
4586 easeOutStrong: function (t, b, c, d) {
4587 return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4591 easeBothStrong: function (t, b, c, d) {
4592 if ((t /= d / 2) < 1) {
4593 return c / 2 * t * t * t * t + b;
4596 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4601 elasticIn: function (t, b, c, d, a, p) {
4605 if ((t /= d) == 1) {
4612 if (!a || a < Math.abs(c)) {
4617 var s = p / (2 * Math.PI) * Math.asin(c / a);
4620 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4624 elasticOut: function (t, b, c, d, a, p) {
4628 if ((t /= d) == 1) {
4635 if (!a || a < Math.abs(c)) {
4640 var s = p / (2 * Math.PI) * Math.asin(c / a);
4643 return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4647 elasticBoth: function (t, b, c, d, a, p) {
4652 if ((t /= d / 2) == 2) {
4660 if (!a || a < Math.abs(c)) {
4665 var s = p / (2 * Math.PI) * Math.asin(c / a);
4669 return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4670 Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4672 return a * Math.pow(2, -10 * (t -= 1)) *
4673 Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4678 backIn: function (t, b, c, d, s) {
4679 if (typeof s == 'undefined') {
4682 return c * (t /= d) * t * ((s + 1) * t - s) + b;
4686 backOut: function (t, b, c, d, s) {
4687 if (typeof s == 'undefined') {
4690 return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4694 backBoth: function (t, b, c, d, s) {
4695 if (typeof s == 'undefined') {
4699 if ((t /= d / 2 ) < 1) {
4700 return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4702 return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4706 bounceIn: function (t, b, c, d) {
4707 return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4711 bounceOut: function (t, b, c, d) {
4712 if ((t /= d) < (1 / 2.75)) {
4713 return c * (7.5625 * t * t) + b;
4714 } else if (t < (2 / 2.75)) {
4715 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4716 } else if (t < (2.5 / 2.75)) {
4717 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4719 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4723 bounceBoth: function (t, b, c, d) {
4725 return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4727 return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4730 * Portions of this file are based on pieces of Yahoo User Interface Library
4731 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4732 * YUI licensed under the BSD License:
4733 * http://developer.yahoo.net/yui/license.txt
4734 * <script type="text/javascript">
4738 Roo.lib.Motion = function(el, attributes, duration, method) {
4740 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4744 Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4748 var superclass = Y.Motion.superclass;
4749 var proto = Y.Motion.prototype;
4751 proto.toString = function() {
4752 var el = this.getEl();
4753 var id = el.id || el.tagName;
4754 return ("Motion " + id);
4757 proto.patterns.points = /^points$/i;
4759 proto.setAttribute = function(attr, val, unit) {
4760 if (this.patterns.points.test(attr)) {
4761 unit = unit || 'px';
4762 superclass.setAttribute.call(this, 'left', val[0], unit);
4763 superclass.setAttribute.call(this, 'top', val[1], unit);
4765 superclass.setAttribute.call(this, attr, val, unit);
4769 proto.getAttribute = function(attr) {
4770 if (this.patterns.points.test(attr)) {
4772 superclass.getAttribute.call(this, 'left'),
4773 superclass.getAttribute.call(this, 'top')
4776 val = superclass.getAttribute.call(this, attr);
4782 proto.doMethod = function(attr, start, end) {
4785 if (this.patterns.points.test(attr)) {
4786 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4787 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4789 val = superclass.doMethod.call(this, attr, start, end);
4794 proto.setRuntimeAttribute = function(attr) {
4795 if (this.patterns.points.test(attr)) {
4796 var el = this.getEl();
4797 var attributes = this.attributes;
4799 var control = attributes['points']['control'] || [];
4803 if (control.length > 0 && !(control[0] instanceof Array)) {
4804 control = [control];
4807 for (i = 0,len = control.length; i < len; ++i) {
4808 tmp[i] = control[i];
4813 Roo.fly(el).position();
4815 if (isset(attributes['points']['from'])) {
4816 Roo.lib.Dom.setXY(el, attributes['points']['from']);
4819 Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4822 start = this.getAttribute('points');
4825 if (isset(attributes['points']['to'])) {
4826 end = translateValues.call(this, attributes['points']['to'], start);
4828 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4829 for (i = 0,len = control.length; i < len; ++i) {
4830 control[i] = translateValues.call(this, control[i], start);
4834 } else if (isset(attributes['points']['by'])) {
4835 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4837 for (i = 0,len = control.length; i < len; ++i) {
4838 control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4842 this.runtimeAttributes[attr] = [start];
4844 if (control.length > 0) {
4845 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4848 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4851 superclass.setRuntimeAttribute.call(this, attr);
4855 var translateValues = function(val, start) {
4856 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4857 val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4862 var isset = function(prop) {
4863 return (typeof prop !== 'undefined');
4867 * Portions of this file are based on pieces of Yahoo User Interface Library
4868 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4869 * YUI licensed under the BSD License:
4870 * http://developer.yahoo.net/yui/license.txt
4871 * <script type="text/javascript">
4875 Roo.lib.Scroll = function(el, attributes, duration, method) {
4877 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4881 Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4885 var superclass = Y.Scroll.superclass;
4886 var proto = Y.Scroll.prototype;
4888 proto.toString = function() {
4889 var el = this.getEl();
4890 var id = el.id || el.tagName;
4891 return ("Scroll " + id);
4894 proto.doMethod = function(attr, start, end) {
4897 if (attr == 'scroll') {
4899 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4900 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4904 val = superclass.doMethod.call(this, attr, start, end);
4909 proto.getAttribute = function(attr) {
4911 var el = this.getEl();
4913 if (attr == 'scroll') {
4914 val = [ el.scrollLeft, el.scrollTop ];
4916 val = superclass.getAttribute.call(this, attr);
4922 proto.setAttribute = function(attr, val, unit) {
4923 var el = this.getEl();
4925 if (attr == 'scroll') {
4926 el.scrollLeft = val[0];
4927 el.scrollTop = val[1];
4929 superclass.setAttribute.call(this, attr, val, unit);
4934 * Originally based of this code... - refactored for Roo...
4935 * https://github.com/aaalsaleh/undo-manager
4938 * @author Abdulrahman Alsaleh
4939 * @copyright 2015 Abdulrahman Alsaleh
4940 * @license MIT License (c)
4942 * Hackily modifyed by alan@roojs.com
4947 * TOTALLY UNTESTED...
4949 * Documentation to be done....
4954 * @class Roo.lib.UndoManager
4955 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4956 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4962 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4966 * For more information see this blog post with examples:
4967 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4968 - Create Elements using DOM, HTML fragments and Templates</a>.
4970 * @param {Number} limit how far back to go ... use 1000?
4971 * @param {Object} scope usually use document..
4974 Roo.lib.UndoManager = function (limit, undoScopeHost)
4978 this.scope = undoScopeHost;
4979 this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4980 if (this.fireEvent) {
4987 Roo.lib.UndoManager.prototype = {
4998 * To push and execute a transaction, the method undoManager.transact
4999 * must be called by passing a transaction object as the first argument, and a merge
5000 * flag as the second argument. A transaction object has the following properties:
5004 undoManager.transact({
5006 execute: function() { ... },
5007 undo: function() { ... },
5008 // redo same as execute
5009 redo: function() { this.execute(); }
5012 // merge transaction
5013 undoManager.transact({
5015 execute: function() { ... }, // this will be run...
5016 undo: function() { ... }, // what to do when undo is run.
5017 // redo same as execute
5018 redo: function() { this.execute(); }
5023 * @param {Object} transaction The transaction to add to the stack.
5024 * @return {String} The HTML fragment
5028 transact : function (transaction, merge)
5030 if (arguments.length < 2) {
5031 throw new TypeError('Not enough arguments to UndoManager.transact.');
5034 transaction.execute();
5036 this.stack.splice(0, this.position);
5037 if (merge && this.length) {
5038 this.stack[0].push(transaction);
5040 this.stack.unshift([transaction]);
5045 if (this.limit && this.stack.length > this.limit) {
5046 this.length = this.stack.length = this.limit;
5048 this.length = this.stack.length;
5051 if (this.fireEvent) {
5052 this.scope.dispatchEvent(
5053 new CustomEvent('DOMTransaction', {
5055 transactions: this.stack[0].slice()
5063 //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5070 //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5072 if (this.position < this.length) {
5073 for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5074 this.stack[this.position][i].undo();
5078 if (this.fireEvent) {
5079 this.scope.dispatchEvent(
5080 new CustomEvent('undo', {
5082 transactions: this.stack[this.position - 1].slice()
5094 if (this.position > 0) {
5095 for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5096 this.stack[this.position - 1][i].redo();
5100 if (this.fireEvent) {
5101 this.scope.dispatchEvent(
5102 new CustomEvent('redo', {
5104 transactions: this.stack[this.position].slice()
5114 item : function (index)
5116 if (index >= 0 && index < this.length) {
5117 return this.stack[index].slice();
5122 clearUndo : function () {
5123 this.stack.length = this.length = this.position;
5126 clearRedo : function () {
5127 this.stack.splice(0, this.position);
5129 this.length = this.stack.length;
5132 * Reset the undo - probaly done on load to clear all history.
5139 this.current_html = this.scope.innerHTML;
5140 if (this.timer !== false) {
5141 clearTimeout(this.timer);
5153 // this will handle the undo/redo on the element.?
5154 bindEvents : function()
5156 var el = this.scope;
5157 el.undoManager = this;
5160 this.scope.addEventListener('keydown', function(e) {
5161 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5163 el.undoManager.redo(); // Ctrl/Command + Shift + Z
5165 el.undoManager.undo(); // Ctrl/Command + Z
5172 this.scope.addEventListener('keyup', function(e) {
5173 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5182 el.addEventListener('input', function(e) {
5183 if(el.innerHTML == t.current_html) {
5186 // only record events every second.
5187 if (t.timer !== false) {
5188 clearTimeout(t.timer);
5191 t.timer = setTimeout(function() { t.merge = false; }, 1000);
5193 t.addEvent(t.merge);
5194 t.merge = true; // ignore changes happening every second..
5198 * Manually add an event.
5199 * Normall called without arguements - and it will just get added to the stack.
5203 addEvent : function(merge)
5205 //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5206 // not sure if this should clear the timer
5207 merge = typeof(merge) == 'undefined' ? false : merge;
5209 this.scope.undoManager.transact({
5211 oldHTML: this.current_html,
5212 newHTML: this.scope.innerHTML,
5213 // nothing to execute (content already changed when input is fired)
5214 execute: function() { },
5216 this.scope.innerHTML = this.current_html = this.oldHTML;
5219 this.scope.innerHTML = this.current_html = this.newHTML;
5221 }, false); //merge);
5225 this.current_html = this.scope.innerHTML;
5235 * @class Roo.lib.Range
5237 * This is a toolkit, normally used to copy features into a Dom Range element
5238 * Roo.lib.Range.wrap(x);
5243 Roo.lib.Range = function() { };
5246 * Wrap a Dom Range object, to give it new features...
5248 * @param {Range} the range to wrap
5250 Roo.lib.Range.wrap = function(r) {
5251 return Roo.apply(r, Roo.lib.Range.prototype);
5254 * find a parent node eg. LI / OL
5255 * @param {string|Array} node name or array of nodenames
5256 * @return {DomElement|false}
5258 Roo.apply(Roo.lib.Range.prototype,
5261 closest : function(str)
5263 if (typeof(str) != 'string') {
5264 // assume it's a array.
5265 for(var i = 0;i < str.length;i++) {
5266 var r = this.closest(str[i]);
5274 str = str.toLowerCase();
5275 var n = this.commonAncestorContainer; // might not be a node
5276 while (n.nodeType != 1) {
5280 if (n.nodeName.toLowerCase() == str ) {
5283 if (n.nodeName.toLowerCase() == 'body') {
5287 return n.closest(str) || false;
5290 cloneRange : function()
5292 return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5295 * @class Roo.lib.Selection
5297 * This is a toolkit, normally used to copy features into a Dom Selection element
5298 * Roo.lib.Selection.wrap(x);
5303 Roo.lib.Selection = function() { };
5306 * Wrap a Dom Range object, to give it new features...
5308 * @param {Range} the range to wrap
5310 Roo.lib.Selection.wrap = function(r, doc) {
5311 Roo.apply(r, Roo.lib.Selection.prototype);
5312 r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5316 * find a parent node eg. LI / OL
5317 * @param {string|Array} node name or array of nodenames
5318 * @return {DomElement|false}
5320 Roo.apply(Roo.lib.Selection.prototype,
5323 * the owner document
5325 ownerDocument : false,
5327 getRangeAt : function(n)
5329 return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5333 * insert node at selection
5334 * @param {DomElement|string} node
5335 * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5337 insertNode: function(node, cursor)
5339 if (typeof(node) == 'string') {
5340 node = this.ownerDocument.createElement(node);
5341 if (cursor == 'in') {
5342 node.innerHTML = ' ';
5346 var range = this.getRangeAt(0);
5348 if (this.type != 'Caret') {
5349 range.deleteContents();
5351 var sn = node.childNodes[0]; // select the contents.
5355 range.insertNode(node);
5356 if (cursor == 'after') {
5357 node.insertAdjacentHTML('afterend', ' ');
5358 sn = node.nextSibling;
5361 if (cursor == 'none') {
5365 this.cursorText(sn);
5368 cursorText : function(n)
5371 //var range = this.getRangeAt(0);
5372 range = Roo.lib.Range.wrap(new Range());
5373 //range.selectNode(n);
5375 var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5376 range.setStart(n.parentNode,ix);
5377 range.setEnd(n.parentNode,ix+1);
5378 //range.collapse(false);
5380 this.removeAllRanges();
5381 this.addRange(range);
5383 Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5385 cursorAfter : function(n)
5387 if (!n.nextSibling || n.nextSibling.nodeValue != ' ') {
5388 n.insertAdjacentHTML('afterend', ' ');
5390 this.cursorText (n.nextSibling);
5396 * Ext JS Library 1.1.1
5397 * Copyright(c) 2006-2007, Ext JS, LLC.
5399 * Originally Released Under LGPL - original licence link has changed is not relivant.
5402 * <script type="text/javascript">
5406 // nasty IE9 hack - what a pile of crap that is..
5408 if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5409 Range.prototype.createContextualFragment = function (html) {
5410 var doc = window.document;
5411 var container = doc.createElement("div");
5412 container.innerHTML = html;
5413 var frag = doc.createDocumentFragment(), n;
5414 while ((n = container.firstChild)) {
5415 frag.appendChild(n);
5422 * @class Roo.DomHelper
5423 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5424 * 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>.
5427 Roo.DomHelper = function(){
5428 var tempTableEl = null;
5429 var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5430 var tableRe = /^table|tbody|tr|td$/i;
5432 // build as innerHTML where available
5434 var createHtml = function(o){
5435 if(typeof o == 'string'){
5444 if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5445 if(attr == "style"){
5447 if(typeof s == "function"){
5450 if(typeof s == "string"){
5451 b += ' style="' + s + '"';
5452 }else if(typeof s == "object"){
5455 if(typeof s[key] != "function"){
5456 b += key + ":" + s[key] + ";";
5463 b += ' class="' + o["cls"] + '"';
5464 }else if(attr == "htmlFor"){
5465 b += ' for="' + o["htmlFor"] + '"';
5467 b += " " + attr + '="' + o[attr] + '"';
5471 if(emptyTags.test(o.tag)){
5475 var cn = o.children || o.cn;
5477 //http://bugs.kde.org/show_bug.cgi?id=71506
5478 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5479 for(var i = 0, len = cn.length; i < len; i++) {
5480 b += createHtml(cn[i], b);
5483 b += createHtml(cn, b);
5489 b += "</" + o.tag + ">";
5496 var createDom = function(o, parentNode){
5498 // defininition craeted..
5500 if (o.ns && o.ns != 'html') {
5502 if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5503 xmlns[o.ns] = o.xmlns;
5506 if (typeof(xmlns[o.ns]) == 'undefined') {
5507 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5513 if (typeof(o) == 'string') {
5514 return parentNode.appendChild(document.createTextNode(o));
5516 o.tag = o.tag || div;
5517 if (o.ns && Roo.isIE) {
5519 o.tag = o.ns + ':' + o.tag;
5522 var el = ns ? document.createElementNS( ns, o.tag||'div') : document.createElement(o.tag||'div');
5523 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5526 if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" ||
5527 attr == "style" || typeof o[attr] == "function") { continue; }
5529 if(attr=="cls" && Roo.isIE){
5530 el.className = o["cls"];
5532 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5538 Roo.DomHelper.applyStyles(el, o.style);
5539 var cn = o.children || o.cn;
5541 //http://bugs.kde.org/show_bug.cgi?id=71506
5542 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5543 for(var i = 0, len = cn.length; i < len; i++) {
5544 createDom(cn[i], el);
5551 el.innerHTML = o.html;
5554 parentNode.appendChild(el);
5559 var ieTable = function(depth, s, h, e){
5560 tempTableEl.innerHTML = [s, h, e].join('');
5561 var i = -1, el = tempTableEl;
5562 while(++i < depth && el.firstChild){
5568 // kill repeat to save bytes
5572 tbe = '</tbody>'+te,
5578 * Nasty code for IE's broken table implementation
5580 var insertIntoTable = function(tag, where, el, html){
5582 tempTableEl = document.createElement('div');
5587 if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5590 if(where == 'beforebegin'){
5594 before = el.nextSibling;
5597 node = ieTable(4, trs, html, tre);
5599 else if(tag == 'tr'){
5600 if(where == 'beforebegin'){
5603 node = ieTable(3, tbs, html, tbe);
5604 } else if(where == 'afterend'){
5605 before = el.nextSibling;
5607 node = ieTable(3, tbs, html, tbe);
5608 } else{ // INTO a TR
5609 if(where == 'afterbegin'){
5610 before = el.firstChild;
5612 node = ieTable(4, trs, html, tre);
5614 } else if(tag == 'tbody'){
5615 if(where == 'beforebegin'){
5618 node = ieTable(2, ts, html, te);
5619 } else if(where == 'afterend'){
5620 before = el.nextSibling;
5622 node = ieTable(2, ts, html, te);
5624 if(where == 'afterbegin'){
5625 before = el.firstChild;
5627 node = ieTable(3, tbs, html, tbe);
5630 if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5633 if(where == 'afterbegin'){
5634 before = el.firstChild;
5636 node = ieTable(2, ts, html, te);
5638 el.insertBefore(node, before);
5642 // this is a bit like the react update code...
5645 var updateNode = function(from, to)
5647 // should we handle non-standard elements?
5648 Roo.log(["UpdateNode" , from, to]);
5649 if (from.nodeType != to.nodeType) {
5650 Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5651 from.parentNode.replaceChild(to, from);
5654 if (from.nodeType == 3) {
5655 // assume it's text?!
5656 if (from.data == to.data) {
5659 from.data = to.data;
5662 if (!from.parentNode) {
5663 // not sure why this is happening?
5666 // assume 'to' doesnt have '1/3 nodetypes!
5667 // not sure why, by from, parent node might not exist?
5668 if (from.nodeType !=1 || from.tagName != to.tagName) {
5669 Roo.log(["ReplaceChild" , from, to ]);
5671 from.parentNode.replaceChild(to, from);
5674 // compare attributes
5675 var ar = Array.from(from.attributes);
5676 for(var i = 0; i< ar.length;i++) {
5677 if (to.hasAttribute(ar[i].name)) {
5680 if (ar[i].name == 'id') { // always keep ids?
5683 //if (ar[i].name == 'style') {
5684 // throw "style removed?";
5686 Roo.log("removeAttribute" + ar[i].name);
5687 from.removeAttribute(ar[i].name);
5690 for(var i = 0; i< ar.length;i++) {
5691 if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5692 Roo.log("skipAttribute " + ar[i].name + '=' + to.getAttribute(ar[i].name));
5695 Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5696 from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5699 var far = Array.from(from.childNodes);
5700 var tar = Array.from(to.childNodes);
5701 // if the lengths are different.. then it's probably a editable content change, rather than
5702 // a change of the block definition..
5704 // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5705 /*if (from.innerHTML == to.innerHTML) {
5708 if (far.length != tar.length) {
5709 from.innerHTML = to.innerHTML;
5714 for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5715 if (i >= far.length) {
5716 from.appendChild(tar[i]);
5717 Roo.log(["add", tar[i]]);
5719 } else if ( i >= tar.length) {
5720 from.removeChild(far[i]);
5721 Roo.log(["remove", far[i]]);
5724 updateNode(far[i], tar[i]);
5736 /** True to force the use of DOM instead of html fragments @type Boolean */
5740 * Returns the markup for the passed Element(s) config
5741 * @param {Object} o The Dom object spec (and children)
5744 markup : function(o){
5745 return createHtml(o);
5749 * Applies a style specification to an element
5750 * @param {String/HTMLElement} el The element to apply styles to
5751 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5752 * a function which returns such a specification.
5754 applyStyles : function(el, styles){
5757 if(typeof styles == "string"){
5758 var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5760 while ((matches = re.exec(styles)) != null){
5761 el.setStyle(matches[1], matches[2]);
5763 }else if (typeof styles == "object"){
5764 for (var style in styles){
5765 el.setStyle(style, styles[style]);
5767 }else if (typeof styles == "function"){
5768 Roo.DomHelper.applyStyles(el, styles.call());
5774 * Inserts an HTML fragment into the Dom
5775 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5776 * @param {HTMLElement} el The context element
5777 * @param {String} html The HTML fragmenet
5778 * @return {HTMLElement} The new node
5780 insertHtml : function(where, el, html){
5781 where = where.toLowerCase();
5782 if(el.insertAdjacentHTML){
5783 if(tableRe.test(el.tagName)){
5785 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5791 el.insertAdjacentHTML('BeforeBegin', html);
5792 return el.previousSibling;
5794 el.insertAdjacentHTML('AfterBegin', html);
5795 return el.firstChild;
5797 el.insertAdjacentHTML('BeforeEnd', html);
5798 return el.lastChild;
5800 el.insertAdjacentHTML('AfterEnd', html);
5801 return el.nextSibling;
5803 throw 'Illegal insertion point -> "' + where + '"';
5805 var range = el.ownerDocument.createRange();
5809 range.setStartBefore(el);
5810 frag = range.createContextualFragment(html);
5811 el.parentNode.insertBefore(frag, el);
5812 return el.previousSibling;
5815 range.setStartBefore(el.firstChild);
5816 frag = range.createContextualFragment(html);
5817 el.insertBefore(frag, el.firstChild);
5818 return el.firstChild;
5820 el.innerHTML = html;
5821 return el.firstChild;
5825 range.setStartAfter(el.lastChild);
5826 frag = range.createContextualFragment(html);
5827 el.appendChild(frag);
5828 return el.lastChild;
5830 el.innerHTML = html;
5831 return el.lastChild;
5834 range.setStartAfter(el);
5835 frag = range.createContextualFragment(html);
5836 el.parentNode.insertBefore(frag, el.nextSibling);
5837 return el.nextSibling;
5839 throw 'Illegal insertion point -> "' + where + '"';
5843 * Creates new Dom element(s) and inserts them before el
5844 * @param {String/HTMLElement/Element} el The context element
5845 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5846 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5847 * @return {HTMLElement/Roo.Element} The new node
5849 insertBefore : function(el, o, returnElement){
5850 return this.doInsert(el, o, returnElement, "beforeBegin");
5854 * Creates new Dom element(s) and inserts them after el
5855 * @param {String/HTMLElement/Element} el The context element
5856 * @param {Object} o The Dom object spec (and children)
5857 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5858 * @return {HTMLElement/Roo.Element} The new node
5860 insertAfter : function(el, o, returnElement){
5861 return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5865 * Creates new Dom element(s) and inserts them as the first child of el
5866 * @param {String/HTMLElement/Element} el The context element
5867 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5868 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5869 * @return {HTMLElement/Roo.Element} The new node
5871 insertFirst : function(el, o, returnElement){
5872 return this.doInsert(el, o, returnElement, "afterBegin");
5876 doInsert : function(el, o, returnElement, pos, sibling){
5877 el = Roo.getDom(el);
5879 if(this.useDom || o.ns){
5880 newNode = createDom(o, null);
5881 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5883 var html = createHtml(o);
5884 newNode = this.insertHtml(pos, el, html);
5886 return returnElement ? Roo.get(newNode, true) : newNode;
5890 * Creates new Dom element(s) and appends them to el
5891 * @param {String/HTMLElement/Element} el The context element
5892 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5893 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5894 * @return {HTMLElement/Roo.Element} The new node
5896 append : function(el, o, returnElement){
5897 el = Roo.getDom(el);
5899 if(this.useDom || o.ns){
5900 newNode = createDom(o, null);
5901 el.appendChild(newNode);
5903 var html = createHtml(o);
5904 newNode = this.insertHtml("beforeEnd", el, html);
5906 return returnElement ? Roo.get(newNode, true) : newNode;
5910 * Creates new Dom element(s) and overwrites the contents of el with them
5911 * @param {String/HTMLElement/Element} el The context element
5912 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5913 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5914 * @return {HTMLElement/Roo.Element} The new node
5916 overwrite : function(el, o, returnElement)
5918 el = Roo.getDom(el);
5921 while (el.childNodes.length) {
5922 el.removeChild(el.firstChild);
5926 el.innerHTML = createHtml(o);
5929 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5933 * Creates a new Roo.DomHelper.Template from the Dom object spec
5934 * @param {Object} o The Dom object spec (and children)
5935 * @return {Roo.DomHelper.Template} The new template
5937 createTemplate : function(o){
5938 var html = createHtml(o);
5939 return new Roo.Template(html);
5942 * Updates the first element with the spec from the o (replacing if necessary)
5943 * This iterates through the children, and updates attributes / children etc..
5944 * @param {String/HTMLElement/Element} el The context element
5945 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5948 update : function(el, o)
5950 updateNode(Roo.getDom(el), createDom(o));
5959 * Ext JS Library 1.1.1
5960 * Copyright(c) 2006-2007, Ext JS, LLC.
5962 * Originally Released Under LGPL - original licence link has changed is not relivant.
5965 * <script type="text/javascript">
5969 * @class Roo.Template
5970 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5971 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5974 var t = new Roo.Template({
5975 html : '<div name="{id}">' +
5976 '<span class="{cls}">{name:trim} {someval:this.myformat}{value:ellipsis(10)}</span>' +
5978 myformat: function (value, allValues) {
5979 return 'XX' + value;
5982 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5984 * For more information see this blog post with examples:
5985 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5986 - Create Elements using DOM, HTML fragments and Templates</a>.
5988 * @param {Object} cfg - Configuration object.
5990 Roo.Template = function(cfg){
5992 if(cfg instanceof Array){
5994 }else if(arguments.length > 1){
5995 cfg = Array.prototype.join.call(arguments, "");
5999 if (typeof(cfg) == 'object') {
6010 Roo.Template.prototype = {
6013 * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6019 * @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..
6020 * it should be fixed so that template is observable...
6024 * @cfg {String} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6032 * Returns an HTML fragment of this template with the specified values applied.
6033 * @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'})
6034 * @return {String} The HTML fragment
6039 applyTemplate : function(values){
6040 //Roo.log(["applyTemplate", values]);
6044 return this.compiled(values);
6046 var useF = this.disableFormats !== true;
6047 var fm = Roo.util.Format, tpl = this;
6048 var fn = function(m, name, format, args){
6050 if(format.substr(0, 5) == "this."){
6051 return tpl.call(format.substr(5), values[name], values);
6054 // quoted values are required for strings in compiled templates,
6055 // but for non compiled we need to strip them
6056 // quoted reversed for jsmin
6057 var re = /^\s*['"](.*)["']\s*$/;
6058 args = args.split(',');
6059 for(var i = 0, len = args.length; i < len; i++){
6060 args[i] = args[i].replace(re, "$1");
6062 args = [values[name]].concat(args);
6064 args = [values[name]];
6066 return fm[format].apply(fm, args);
6069 return values[name] !== undefined ? values[name] : "";
6072 return this.html.replace(this.re, fn);
6090 this.loading = true;
6091 this.compiled = false;
6093 var cx = new Roo.data.Connection();
6097 success : function (response) {
6101 _t.set(response.responseText,true);
6107 failure : function(response) {
6108 Roo.log("Template failed to load from " + _t.url);
6115 * Sets the HTML used as the template and optionally compiles it.
6116 * @param {String} html
6117 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6118 * @return {Roo.Template} this
6120 set : function(html, compile){
6122 this.compiled = false;
6130 * True to disable format functions (defaults to false)
6133 disableFormats : false,
6136 * The regular expression used to match template variables
6140 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6143 * Compiles the template into an internal function, eliminating the RegEx overhead.
6144 * @return {Roo.Template} this
6146 compile : function(){
6147 var fm = Roo.util.Format;
6148 var useF = this.disableFormats !== true;
6149 var sep = Roo.isGecko ? "+" : ",";
6150 var fn = function(m, name, format, args){
6152 args = args ? ',' + args : "";
6153 if(format.substr(0, 5) != "this."){
6154 format = "fm." + format + '(';
6156 format = 'this.call("'+ format.substr(5) + '", ';
6160 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6162 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6165 // branched to use + in gecko and [].join() in others
6167 body = "this.compiled = function(values){ return '" +
6168 this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6171 body = ["this.compiled = function(values){ return ['"];
6172 body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6173 body.push("'].join('');};");
6174 body = body.join('');
6184 // private function used to call members
6185 call : function(fnName, value, allValues){
6186 return this[fnName](value, allValues);
6190 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6191 * @param {String/HTMLElement/Roo.Element} el The context element
6192 * @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'})
6193 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6194 * @return {HTMLElement/Roo.Element} The new node or Element
6196 insertFirst: function(el, values, returnElement){
6197 return this.doInsert('afterBegin', el, values, returnElement);
6201 * Applies the supplied values to the template and inserts the new node(s) before el.
6202 * @param {String/HTMLElement/Roo.Element} el The context element
6203 * @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'})
6204 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6205 * @return {HTMLElement/Roo.Element} The new node or Element
6207 insertBefore: function(el, values, returnElement){
6208 return this.doInsert('beforeBegin', el, values, returnElement);
6212 * Applies the supplied values to the template and inserts the new node(s) after el.
6213 * @param {String/HTMLElement/Roo.Element} el The context element
6214 * @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'})
6215 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6216 * @return {HTMLElement/Roo.Element} The new node or Element
6218 insertAfter : function(el, values, returnElement){
6219 return this.doInsert('afterEnd', el, values, returnElement);
6223 * Applies the supplied values to the template and appends the new node(s) to el.
6224 * @param {String/HTMLElement/Roo.Element} el The context element
6225 * @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'})
6226 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6227 * @return {HTMLElement/Roo.Element} The new node or Element
6229 append : function(el, values, returnElement){
6230 return this.doInsert('beforeEnd', el, values, returnElement);
6233 doInsert : function(where, el, values, returnEl){
6234 el = Roo.getDom(el);
6235 var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6236 return returnEl ? Roo.get(newNode, true) : newNode;
6240 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6241 * @param {String/HTMLElement/Roo.Element} el The context element
6242 * @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'})
6243 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6244 * @return {HTMLElement/Roo.Element} The new node or Element
6246 overwrite : function(el, values, returnElement){
6247 el = Roo.getDom(el);
6248 el.innerHTML = this.applyTemplate(values);
6249 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6253 * Alias for {@link #applyTemplate}
6256 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6259 Roo.DomHelper.Template = Roo.Template;
6262 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6263 * @param {String/HTMLElement} el A DOM element or its id
6264 * @returns {Roo.Template} The created template
6267 Roo.Template.from = function(el){
6268 el = Roo.getDom(el);
6269 return new Roo.Template(el.value || el.innerHTML);
6272 * Ext JS Library 1.1.1
6273 * Copyright(c) 2006-2007, Ext JS, LLC.
6275 * Originally Released Under LGPL - original licence link has changed is not relivant.
6278 * <script type="text/javascript">
6283 * This is code is also distributed under MIT license for use
6284 * with jQuery and prototype JavaScript libraries.
6287 * @class Roo.DomQuery
6288 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).
6290 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>
6293 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.
6295 <h4>Element Selectors:</h4>
6297 <li> <b>*</b> any element</li>
6298 <li> <b>E</b> an element with the tag E</li>
6299 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6300 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6301 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6302 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6304 <h4>Attribute Selectors:</h4>
6305 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6307 <li> <b>E[foo]</b> has an attribute "foo"</li>
6308 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6309 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6310 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6311 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6312 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6313 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6315 <h4>Pseudo Classes:</h4>
6317 <li> <b>E:first-child</b> E is the first child of its parent</li>
6318 <li> <b>E:last-child</b> E is the last child of its parent</li>
6319 <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>
6320 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6321 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6322 <li> <b>E:only-child</b> E is the only child of its parent</li>
6323 <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>
6324 <li> <b>E:first</b> the first E in the resultset</li>
6325 <li> <b>E:last</b> the last E in the resultset</li>
6326 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6327 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6328 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6329 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6330 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6331 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6332 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6333 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6334 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6336 <h4>CSS Value Selectors:</h4>
6338 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6339 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6340 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6341 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6342 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6343 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6347 Roo.DomQuery = function(){
6348 var cache = {}, simpleCache = {}, valueCache = {};
6349 var nonSpace = /\S/;
6350 var trimRe = /^\s+|\s+$/g;
6351 var tplRe = /\{(\d+)\}/g;
6352 var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6353 var tagTokenRe = /^(#)?([\w-\*]+)/;
6354 var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6356 function child(p, index){
6358 var n = p.firstChild;
6360 if(n.nodeType == 1){
6371 while((n = n.nextSibling) && n.nodeType != 1);
6376 while((n = n.previousSibling) && n.nodeType != 1);
6380 function children(d){
6381 var n = d.firstChild, ni = -1;
6383 var nx = n.nextSibling;
6384 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6394 function byClassName(c, a, v){
6398 var r = [], ri = -1, cn;
6399 for(var i = 0, ci; ci = c[i]; i++){
6403 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6404 +' ').indexOf(v) != -1){
6411 function attrValue(n, attr){
6412 if(!n.tagName && typeof n.length != "undefined"){
6421 if(attr == "class" || attr == "className"){
6422 return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6424 return n.getAttribute(attr) || n[attr];
6428 function getNodes(ns, mode, tagName){
6429 var result = [], ri = -1, cs;
6433 tagName = tagName || "*";
6434 if(typeof ns.getElementsByTagName != "undefined"){
6438 for(var i = 0, ni; ni = ns[i]; i++){
6439 cs = ni.getElementsByTagName(tagName);
6440 for(var j = 0, ci; ci = cs[j]; j++){
6444 }else if(mode == "/" || mode == ">"){
6445 var utag = tagName.toUpperCase();
6446 for(var i = 0, ni, cn; ni = ns[i]; i++){
6447 cn = ni.children || ni.childNodes;
6448 for(var j = 0, cj; cj = cn[j]; j++){
6449 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
6454 }else if(mode == "+"){
6455 var utag = tagName.toUpperCase();
6456 for(var i = 0, n; n = ns[i]; i++){
6457 while((n = n.nextSibling) && n.nodeType != 1);
6458 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6462 }else if(mode == "~"){
6463 for(var i = 0, n; n = ns[i]; i++){
6464 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6473 function concat(a, b){
6477 for(var i = 0, l = b.length; i < l; i++){
6483 function byTag(cs, tagName){
6484 if(cs.tagName || cs == document){
6490 var r = [], ri = -1;
6491 tagName = tagName.toLowerCase();
6492 for(var i = 0, ci; ci = cs[i]; i++){
6493 if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6500 function byId(cs, attr, id){
6501 if(cs.tagName || cs == document){
6507 var r = [], ri = -1;
6508 for(var i = 0,ci; ci = cs[i]; i++){
6509 if(ci && ci.id == id){
6517 function byAttribute(cs, attr, value, op, custom){
6518 var r = [], ri = -1, st = custom=="{";
6519 var f = Roo.DomQuery.operators[op];
6520 for(var i = 0, ci; ci = cs[i]; i++){
6523 a = Roo.DomQuery.getStyle(ci, attr);
6525 else if(attr == "class" || attr == "className"){
6526 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6527 }else if(attr == "for"){
6529 }else if(attr == "href"){
6530 a = ci.getAttribute("href", 2);
6532 a = ci.getAttribute(attr);
6534 if((f && f(a, value)) || (!f && a)){
6541 function byPseudo(cs, name, value){
6542 return Roo.DomQuery.pseudos[name](cs, value);
6545 // This is for IE MSXML which does not support expandos.
6546 // IE runs the same speed using setAttribute, however FF slows way down
6547 // and Safari completely fails so they need to continue to use expandos.
6548 var isIE = window.ActiveXObject ? true : false;
6550 // this eval is stop the compressor from
6551 // renaming the variable to something shorter
6553 /** eval:var:batch */
6558 function nodupIEXml(cs){
6560 cs[0].setAttribute("_nodup", d);
6562 for(var i = 1, len = cs.length; i < len; i++){
6564 if(!c.getAttribute("_nodup") != d){
6565 c.setAttribute("_nodup", d);
6569 for(var i = 0, len = cs.length; i < len; i++){
6570 cs[i].removeAttribute("_nodup");
6579 var len = cs.length, c, i, r = cs, cj, ri = -1;
6580 if(!len || typeof cs.nodeType != "undefined" || len == 1){
6583 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6584 return nodupIEXml(cs);
6588 for(i = 1; c = cs[i]; i++){
6593 for(var j = 0; j < i; j++){
6596 for(j = i+1; cj = cs[j]; j++){
6608 function quickDiffIEXml(c1, c2){
6610 for(var i = 0, len = c1.length; i < len; i++){
6611 c1[i].setAttribute("_qdiff", d);
6614 for(var i = 0, len = c2.length; i < len; i++){
6615 if(c2[i].getAttribute("_qdiff") != d){
6616 r[r.length] = c2[i];
6619 for(var i = 0, len = c1.length; i < len; i++){
6620 c1[i].removeAttribute("_qdiff");
6625 function quickDiff(c1, c2){
6626 var len1 = c1.length;
6630 if(isIE && c1[0].selectSingleNode){
6631 return quickDiffIEXml(c1, c2);
6634 for(var i = 0; i < len1; i++){
6638 for(var i = 0, len = c2.length; i < len; i++){
6639 if(c2[i]._qdiff != d){
6640 r[r.length] = c2[i];
6646 function quickId(ns, mode, root, id){
6648 var d = root.ownerDocument || root;
6649 return d.getElementById(id);
6651 ns = getNodes(ns, mode, "*");
6652 return byId(ns, null, id);
6656 getStyle : function(el, name){
6657 return Roo.fly(el).getStyle(name);
6660 * Compiles a selector/xpath query into a reusable function. The returned function
6661 * takes one parameter "root" (optional), which is the context node from where the query should start.
6662 * @param {String} selector The selector/xpath query
6663 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6664 * @return {Function}
6666 compile : function(path, type){
6667 type = type || "select";
6669 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6670 var q = path, mode, lq;
6671 var tk = Roo.DomQuery.matchers;
6672 var tklen = tk.length;
6675 // accept leading mode switch
6676 var lmode = q.match(modeRe);
6677 if(lmode && lmode[1]){
6678 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6679 q = q.replace(lmode[1], "");
6681 // strip leading slashes
6682 while(path.substr(0, 1)=="/"){
6683 path = path.substr(1);
6686 while(q && lq != q){
6688 var tm = q.match(tagTokenRe);
6689 if(type == "select"){
6692 fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6694 fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6696 q = q.replace(tm[0], "");
6697 }else if(q.substr(0, 1) != '@'){
6698 fn[fn.length] = 'n = getNodes(n, mode, "*");';
6703 fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6705 fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6707 q = q.replace(tm[0], "");
6710 while(!(mm = q.match(modeRe))){
6711 var matched = false;
6712 for(var j = 0; j < tklen; j++){
6714 var m = q.match(t.re);
6716 fn[fn.length] = t.select.replace(tplRe, function(x, i){
6719 q = q.replace(m[0], "");
6724 // prevent infinite loop on bad selector
6726 throw 'Error parsing selector, parsing failed at "' + q + '"';
6730 fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6731 q = q.replace(mm[1], "");
6734 fn[fn.length] = "return nodup(n);\n}";
6737 * list of variables that need from compression as they are used by eval.
6747 * eval:var:byClassName
6749 * eval:var:byAttribute
6750 * eval:var:attrValue
6758 * Selects a group of elements.
6759 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6760 * @param {Node} root (optional) The start of the query (defaults to document).
6763 select : function(path, root, type){
6764 if(!root || root == document){
6767 if(typeof root == "string"){
6768 root = document.getElementById(root);
6770 var paths = path.split(",");
6772 for(var i = 0, len = paths.length; i < len; i++){
6773 var p = paths[i].replace(trimRe, "");
6775 cache[p] = Roo.DomQuery.compile(p);
6777 throw p + " is not a valid selector";
6780 var result = cache[p](root);
6781 if(result && result != document){
6782 results = results.concat(result);
6785 if(paths.length > 1){
6786 return nodup(results);
6792 * Selects a single element.
6793 * @param {String} selector The selector/xpath query
6794 * @param {Node} root (optional) The start of the query (defaults to document).
6797 selectNode : function(path, root){
6798 return Roo.DomQuery.select(path, root)[0];
6802 * Selects the value of a node, optionally replacing null with the defaultValue.
6803 * @param {String} selector The selector/xpath query
6804 * @param {Node} root (optional) The start of the query (defaults to document).
6805 * @param {String} defaultValue
6807 selectValue : function(path, root, defaultValue){
6808 path = path.replace(trimRe, "");
6809 if(!valueCache[path]){
6810 valueCache[path] = Roo.DomQuery.compile(path, "select");
6812 var n = valueCache[path](root);
6813 n = n[0] ? n[0] : n;
6814 var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6815 return ((v === null||v === undefined||v==='') ? defaultValue : v);
6819 * Selects the value of a node, parsing integers and floats.
6820 * @param {String} selector The selector/xpath query
6821 * @param {Node} root (optional) The start of the query (defaults to document).
6822 * @param {Number} defaultValue
6825 selectNumber : function(path, root, defaultValue){
6826 var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6827 return parseFloat(v);
6831 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6832 * @param {String/HTMLElement/Array} el An element id, element or array of elements
6833 * @param {String} selector The simple selector to test
6836 is : function(el, ss){
6837 if(typeof el == "string"){
6838 el = document.getElementById(el);
6840 var isArray = (el instanceof Array);
6841 var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6842 return isArray ? (result.length == el.length) : (result.length > 0);
6846 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6847 * @param {Array} el An array of elements to filter
6848 * @param {String} selector The simple selector to test
6849 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6850 * the selector instead of the ones that match
6853 filter : function(els, ss, nonMatches){
6854 ss = ss.replace(trimRe, "");
6855 if(!simpleCache[ss]){
6856 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6858 var result = simpleCache[ss](els);
6859 return nonMatches ? quickDiff(result, els) : result;
6863 * Collection of matching regular expressions and code snippets.
6867 select: 'n = byClassName(n, null, " {1} ");'
6869 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6870 select: 'n = byPseudo(n, "{1}", "{2}");'
6872 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6873 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6876 select: 'n = byId(n, null, "{1}");'
6879 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6884 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6885 * 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, > <.
6888 "=" : function(a, v){
6891 "!=" : function(a, v){
6894 "^=" : function(a, v){
6895 return a && a.substr(0, v.length) == v;
6897 "$=" : function(a, v){
6898 return a && a.substr(a.length-v.length) == v;
6900 "*=" : function(a, v){
6901 return a && a.indexOf(v) !== -1;
6903 "%=" : function(a, v){
6904 return (a % v) == 0;
6906 "|=" : function(a, v){
6907 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6909 "~=" : function(a, v){
6910 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6915 * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6916 * and the argument (if any) supplied in the selector.
6919 "first-child" : function(c){
6920 var r = [], ri = -1, n;
6921 for(var i = 0, ci; ci = n = c[i]; i++){
6922 while((n = n.previousSibling) && n.nodeType != 1);
6930 "last-child" : function(c){
6931 var r = [], ri = -1, n;
6932 for(var i = 0, ci; ci = n = c[i]; i++){
6933 while((n = n.nextSibling) && n.nodeType != 1);
6941 "nth-child" : function(c, a) {
6942 var r = [], ri = -1;
6943 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6944 var f = (m[1] || 1) - 0, l = m[2] - 0;
6945 for(var i = 0, n; n = c[i]; i++){
6946 var pn = n.parentNode;
6947 if (batch != pn._batch) {
6949 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6950 if(cn.nodeType == 1){
6957 if (l == 0 || n.nodeIndex == l){
6960 } else if ((n.nodeIndex + l) % f == 0){
6968 "only-child" : function(c){
6969 var r = [], ri = -1;;
6970 for(var i = 0, ci; ci = c[i]; i++){
6971 if(!prev(ci) && !next(ci)){
6978 "empty" : function(c){
6979 var r = [], ri = -1;
6980 for(var i = 0, ci; ci = c[i]; i++){
6981 var cns = ci.childNodes, j = 0, cn, empty = true;
6984 if(cn.nodeType == 1 || cn.nodeType == 3){
6996 "contains" : function(c, v){
6997 var r = [], ri = -1;
6998 for(var i = 0, ci; ci = c[i]; i++){
6999 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7006 "nodeValue" : function(c, v){
7007 var r = [], ri = -1;
7008 for(var i = 0, ci; ci = c[i]; i++){
7009 if(ci.firstChild && ci.firstChild.nodeValue == v){
7016 "checked" : function(c){
7017 var r = [], ri = -1;
7018 for(var i = 0, ci; ci = c[i]; i++){
7019 if(ci.checked == true){
7026 "not" : function(c, ss){
7027 return Roo.DomQuery.filter(c, ss, true);
7030 "odd" : function(c){
7031 return this["nth-child"](c, "odd");
7034 "even" : function(c){
7035 return this["nth-child"](c, "even");
7038 "nth" : function(c, a){
7039 return c[a-1] || [];
7042 "first" : function(c){
7046 "last" : function(c){
7047 return c[c.length-1] || [];
7050 "has" : function(c, ss){
7051 var s = Roo.DomQuery.select;
7052 var r = [], ri = -1;
7053 for(var i = 0, ci; ci = c[i]; i++){
7054 if(s(ss, ci).length > 0){
7061 "next" : function(c, ss){
7062 var is = Roo.DomQuery.is;
7063 var r = [], ri = -1;
7064 for(var i = 0, ci; ci = c[i]; i++){
7073 "prev" : function(c, ss){
7074 var is = Roo.DomQuery.is;
7075 var r = [], ri = -1;
7076 for(var i = 0, ci; ci = c[i]; i++){
7089 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7090 * @param {String} path The selector/xpath query
7091 * @param {Node} root (optional) The start of the query (defaults to document).
7096 Roo.query = Roo.DomQuery.select;
7099 * Ext JS Library 1.1.1
7100 * Copyright(c) 2006-2007, Ext JS, LLC.
7102 * Originally Released Under LGPL - original licence link has changed is not relivant.
7105 * <script type="text/javascript">
7109 * @class Roo.util.Observable
7110 * Base class that provides a common interface for publishing events. Subclasses are expected to
7111 * to have a property "events" with all the events defined.<br>
7114 Employee = function(name){
7121 Roo.extend(Employee, Roo.util.Observable);
7123 * @param {Object} config properties to use (incuding events / listeners)
7126 Roo.util.Observable = function(cfg){
7127 console.log("UTIL OBSERVABLE CONSTRUCTOR");
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 console.log("COMPONENT CONSTRUCTOR");
16870 config = config || {};
16871 if(config.tagName || config.dom || typeof config == "string"){ // element object
16872 config = {el: config, id: config.id || config};
16874 this.initialConfig = config;
16876 Roo.apply(this, config);
16880 * Fires after the component is disabled.
16881 * @param {Roo.Component} this
16886 * Fires after the component is enabled.
16887 * @param {Roo.Component} this
16891 * @event beforeshow
16892 * Fires before the component is shown. Return false to stop the show.
16893 * @param {Roo.Component} this
16898 * Fires after the component is shown.
16899 * @param {Roo.Component} this
16903 * @event beforehide
16904 * Fires before the component is hidden. Return false to stop the hide.
16905 * @param {Roo.Component} this
16910 * Fires after the component is hidden.
16911 * @param {Roo.Component} this
16915 * @event beforerender
16916 * Fires before the component is rendered. Return false to stop the render.
16917 * @param {Roo.Component} this
16919 beforerender : true,
16922 * Fires after the component is rendered.
16923 * @param {Roo.Component} this
16927 * @event beforedestroy
16928 * Fires before the component is destroyed. Return false to stop the destroy.
16929 * @param {Roo.Component} this
16931 beforedestroy : true,
16934 * Fires after the component is destroyed.
16935 * @param {Roo.Component} this
16940 this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16942 Roo.ComponentMgr.register(this);
16943 Roo.Component.superclass.constructor.call(this);
16944 this.initComponent();
16945 if(this.renderTo){ // not supported by all components yet. use at your own risk!
16946 this.render(this.renderTo);
16947 delete this.renderTo;
16952 Roo.Component.AUTO_ID = 1000;
16954 Roo.extend(Roo.Component, Roo.util.Observable, {
16956 * @scope Roo.Component.prototype
16958 * true if this component is hidden. Read-only.
16963 * true if this component is disabled. Read-only.
16968 * true if this component has been rendered. Read-only.
16972 /** @cfg {String} disableClass
16973 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16975 disabledClass : "x-item-disabled",
16976 /** @cfg {Boolean} allowDomMove
16977 * Whether the component can move the Dom node when rendering (defaults to true).
16979 allowDomMove : true,
16980 /** @cfg {String} hideMode (display|visibility)
16981 * How this component should hidden. Supported values are
16982 * "visibility" (css visibility), "offsets" (negative offset position) and
16983 * "display" (css display) - defaults to "display".
16985 hideMode: 'display',
16988 ctype : "Roo.Component",
16991 * @cfg {String} actionMode
16992 * which property holds the element that used for hide() / show() / disable() / enable()
16993 * default is 'el' for forms you probably want to set this to fieldEl
16998 getActionEl : function(){
16999 return this[this.actionMode];
17002 initComponent : Roo.emptyFn,
17004 * If this is a lazy rendering component, render it to its container element.
17005 * @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.
17007 render : function(container, position){
17013 if(this.fireEvent("beforerender", this) === false){
17017 if(!container && this.el){
17018 this.el = Roo.get(this.el);
17019 container = this.el.dom.parentNode;
17020 this.allowDomMove = false;
17022 this.container = Roo.get(container);
17023 this.rendered = true;
17024 if(position !== undefined){
17025 if(typeof position == 'number'){
17026 position = this.container.dom.childNodes[position];
17028 position = Roo.getDom(position);
17031 this.onRender(this.container, position || null);
17033 this.el.addClass(this.cls);
17037 this.el.applyStyles(this.style);
17040 this.fireEvent("render", this);
17041 this.afterRender(this.container);
17054 // default function is not really useful
17055 onRender : function(ct, position){
17057 this.el = Roo.get(this.el);
17058 if(this.allowDomMove !== false){
17059 ct.dom.insertBefore(this.el.dom, position);
17065 getAutoCreate : function(){
17066 var cfg = typeof this.autoCreate == "object" ?
17067 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17068 if(this.id && !cfg.id){
17075 afterRender : Roo.emptyFn,
17078 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17079 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17081 destroy : function(){
17082 if(this.fireEvent("beforedestroy", this) !== false){
17083 this.purgeListeners();
17084 this.beforeDestroy();
17086 this.el.removeAllListeners();
17088 if(this.actionMode == "container"){
17089 this.container.remove();
17093 Roo.ComponentMgr.unregister(this);
17094 this.fireEvent("destroy", this);
17099 beforeDestroy : function(){
17104 onDestroy : function(){
17109 * Returns the underlying {@link Roo.Element}.
17110 * @return {Roo.Element} The element
17112 getEl : function(){
17117 * Returns the id of this component.
17120 getId : function(){
17125 * Try to focus this component.
17126 * @param {Boolean} selectText True to also select the text in this component (if applicable)
17127 * @return {Roo.Component} this
17129 focus : function(selectText){
17132 if(selectText === true){
17133 this.el.dom.select();
17148 * Disable this component.
17149 * @return {Roo.Component} this
17151 disable : function(){
17155 this.disabled = true;
17156 this.fireEvent("disable", this);
17161 onDisable : function(){
17162 this.getActionEl().addClass(this.disabledClass);
17163 this.el.dom.disabled = true;
17167 * Enable this component.
17168 * @return {Roo.Component} this
17170 enable : function(){
17174 this.disabled = false;
17175 this.fireEvent("enable", this);
17180 onEnable : function(){
17181 this.getActionEl().removeClass(this.disabledClass);
17182 this.el.dom.disabled = false;
17186 * Convenience function for setting disabled/enabled by boolean.
17187 * @param {Boolean} disabled
17189 setDisabled : function(disabled){
17190 this[disabled ? "disable" : "enable"]();
17194 * Show this component.
17195 * @return {Roo.Component} this
17198 if(this.fireEvent("beforeshow", this) !== false){
17199 this.hidden = false;
17203 this.fireEvent("show", this);
17209 onShow : function(){
17210 var ae = this.getActionEl();
17211 if(this.hideMode == 'visibility'){
17212 ae.dom.style.visibility = "visible";
17213 }else if(this.hideMode == 'offsets'){
17214 ae.removeClass('x-hidden');
17216 ae.dom.style.display = "";
17221 * Hide this component.
17222 * @return {Roo.Component} this
17225 if(this.fireEvent("beforehide", this) !== false){
17226 this.hidden = true;
17230 this.fireEvent("hide", this);
17236 onHide : function(){
17237 var ae = this.getActionEl();
17238 if(this.hideMode == 'visibility'){
17239 ae.dom.style.visibility = "hidden";
17240 }else if(this.hideMode == 'offsets'){
17241 ae.addClass('x-hidden');
17243 ae.dom.style.display = "none";
17248 * Convenience function to hide or show this component by boolean.
17249 * @param {Boolean} visible True to show, false to hide
17250 * @return {Roo.Component} this
17252 setVisible: function(visible){
17262 * Returns true if this component is visible.
17264 isVisible : function(){
17265 return this.getActionEl().isVisible();
17268 cloneConfig : function(overrides){
17269 overrides = overrides || {};
17270 var id = overrides.id || Roo.id();
17271 var cfg = Roo.applyIf(overrides, this.initialConfig);
17272 cfg.id = id; // prevent dup id
17273 return new this.constructor(cfg);
17277 * Ext JS Library 1.1.1
17278 * Copyright(c) 2006-2007, Ext JS, LLC.
17280 * Originally Released Under LGPL - original licence link has changed is not relivant.
17283 * <script type="text/javascript">
17287 * @class Roo.BoxComponent
17288 * @extends Roo.Component
17289 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
17290 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
17291 * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17292 * layout containers.
17294 * @param {Roo.Element/String/Object} config The configuration options.
17296 Roo.BoxComponent = function(config){
17297 Roo.Component.call(this, config);
17301 * Fires after the component is resized.
17302 * @param {Roo.Component} this
17303 * @param {Number} adjWidth The box-adjusted width that was set
17304 * @param {Number} adjHeight The box-adjusted height that was set
17305 * @param {Number} rawWidth The width that was originally specified
17306 * @param {Number} rawHeight The height that was originally specified
17311 * Fires after the component is moved.
17312 * @param {Roo.Component} this
17313 * @param {Number} x The new x position
17314 * @param {Number} y The new y position
17320 Roo.extend(Roo.BoxComponent, Roo.Component, {
17321 // private, set in afterRender to signify that the component has been rendered
17323 // private, used to defer height settings to subclasses
17324 deferHeight: false,
17325 /** @cfg {Number} width
17326 * width (optional) size of component
17328 /** @cfg {Number} height
17329 * height (optional) size of component
17333 * Sets the width and height of the component. This method fires the resize event. This method can accept
17334 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17335 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17336 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17337 * @return {Roo.BoxComponent} this
17339 setSize : function(w, h){
17340 // support for standard size objects
17341 if(typeof w == 'object'){
17346 if(!this.boxReady){
17352 // prevent recalcs when not needed
17353 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17356 this.lastSize = {width: w, height: h};
17358 var adj = this.adjustSize(w, h);
17359 var aw = adj.width, ah = adj.height;
17360 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17361 var rz = this.getResizeEl();
17362 if(!this.deferHeight && aw !== undefined && ah !== undefined){
17363 rz.setSize(aw, ah);
17364 }else if(!this.deferHeight && ah !== undefined){
17366 }else if(aw !== undefined){
17369 this.onResize(aw, ah, w, h);
17370 this.fireEvent('resize', this, aw, ah, w, h);
17376 * Gets the current size of the component's underlying element.
17377 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17379 getSize : function(){
17380 return this.el.getSize();
17384 * Gets the current XY position of the component's underlying element.
17385 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17386 * @return {Array} The XY position of the element (e.g., [100, 200])
17388 getPosition : function(local){
17389 if(local === true){
17390 return [this.el.getLeft(true), this.el.getTop(true)];
17392 return this.xy || this.el.getXY();
17396 * Gets the current box measurements of the component's underlying element.
17397 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17398 * @returns {Object} box An object in the format {x, y, width, height}
17400 getBox : function(local){
17401 var s = this.el.getSize();
17403 s.x = this.el.getLeft(true);
17404 s.y = this.el.getTop(true);
17406 var xy = this.xy || this.el.getXY();
17414 * Sets the current box measurements of the component's underlying element.
17415 * @param {Object} box An object in the format {x, y, width, height}
17416 * @returns {Roo.BoxComponent} this
17418 updateBox : function(box){
17419 this.setSize(box.width, box.height);
17420 this.setPagePosition(box.x, box.y);
17425 getResizeEl : function(){
17426 return this.resizeEl || this.el;
17430 getPositionEl : function(){
17431 return this.positionEl || this.el;
17435 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
17436 * This method fires the move event.
17437 * @param {Number} left The new left
17438 * @param {Number} top The new top
17439 * @returns {Roo.BoxComponent} this
17441 setPosition : function(x, y){
17444 if(!this.boxReady){
17447 var adj = this.adjustPosition(x, y);
17448 var ax = adj.x, ay = adj.y;
17450 var el = this.getPositionEl();
17451 if(ax !== undefined || ay !== undefined){
17452 if(ax !== undefined && ay !== undefined){
17453 el.setLeftTop(ax, ay);
17454 }else if(ax !== undefined){
17456 }else if(ay !== undefined){
17459 this.onPosition(ax, ay);
17460 this.fireEvent('move', this, ax, ay);
17466 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
17467 * This method fires the move event.
17468 * @param {Number} x The new x position
17469 * @param {Number} y The new y position
17470 * @returns {Roo.BoxComponent} this
17472 setPagePosition : function(x, y){
17475 if(!this.boxReady){
17478 if(x === undefined || y === undefined){ // cannot translate undefined points
17481 var p = this.el.translatePoints(x, y);
17482 this.setPosition(p.left, p.top);
17487 onRender : function(ct, position){
17488 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17490 this.resizeEl = Roo.get(this.resizeEl);
17492 if(this.positionEl){
17493 this.positionEl = Roo.get(this.positionEl);
17498 afterRender : function(){
17499 Roo.BoxComponent.superclass.afterRender.call(this);
17500 this.boxReady = true;
17501 this.setSize(this.width, this.height);
17502 if(this.x || this.y){
17503 this.setPosition(this.x, this.y);
17505 if(this.pageX || this.pageY){
17506 this.setPagePosition(this.pageX, this.pageY);
17511 * Force the component's size to recalculate based on the underlying element's current height and width.
17512 * @returns {Roo.BoxComponent} this
17514 syncSize : function(){
17515 delete this.lastSize;
17516 this.setSize(this.el.getWidth(), this.el.getHeight());
17521 * Called after the component is resized, this method is empty by default but can be implemented by any
17522 * subclass that needs to perform custom logic after a resize occurs.
17523 * @param {Number} adjWidth The box-adjusted width that was set
17524 * @param {Number} adjHeight The box-adjusted height that was set
17525 * @param {Number} rawWidth The width that was originally specified
17526 * @param {Number} rawHeight The height that was originally specified
17528 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17533 * Called after the component is moved, this method is empty by default but can be implemented by any
17534 * subclass that needs to perform custom logic after a move occurs.
17535 * @param {Number} x The new x position
17536 * @param {Number} y The new y position
17538 onPosition : function(x, y){
17543 adjustSize : function(w, h){
17544 if(this.autoWidth){
17547 if(this.autoHeight){
17550 return {width : w, height: h};
17554 adjustPosition : function(x, y){
17555 return {x : x, y: y};
17559 * Ext JS Library 1.1.1
17560 * Copyright(c) 2006-2007, Ext JS, LLC.
17562 * Originally Released Under LGPL - original licence link has changed is not relivant.
17565 * <script type="text/javascript">
17570 * @extends Roo.Element
17571 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17572 * automatic maintaining of shadow/shim positions.
17573 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17574 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17575 * you can pass a string with a CSS class name. False turns off the shadow.
17576 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17577 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17578 * @cfg {String} cls CSS class to add to the element
17579 * @cfg {Number} zindex Starting z-index (defaults to 11000)
17580 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17582 * @param {Object} config An object with config options.
17583 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17586 Roo.Layer = function(config, existingEl){
17587 config = config || {};
17588 var dh = Roo.DomHelper;
17589 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17591 this.dom = Roo.getDom(existingEl);
17594 var o = config.dh || {tag: "div", cls: "x-layer"};
17595 this.dom = dh.append(pel, o);
17598 this.addClass(config.cls);
17600 this.constrain = config.constrain !== false;
17601 this.visibilityMode = Roo.Element.VISIBILITY;
17603 this.id = this.dom.id = config.id;
17605 this.id = Roo.id(this.dom);
17607 this.zindex = config.zindex || this.getZIndex();
17608 this.position("absolute", this.zindex);
17610 this.shadowOffset = config.shadowOffset || 4;
17611 this.shadow = new Roo.Shadow({
17612 offset : this.shadowOffset,
17613 mode : config.shadow
17616 this.shadowOffset = 0;
17618 this.useShim = config.shim !== false && Roo.useShims;
17619 this.useDisplay = config.useDisplay;
17623 var supr = Roo.Element.prototype;
17625 // shims are shared among layer to keep from having 100 iframes
17628 Roo.extend(Roo.Layer, Roo.Element, {
17630 getZIndex : function(){
17631 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17634 getShim : function(){
17641 var shim = shims.shift();
17643 shim = this.createShim();
17644 shim.enableDisplayMode('block');
17645 shim.dom.style.display = 'none';
17646 shim.dom.style.visibility = 'visible';
17648 var pn = this.dom.parentNode;
17649 if(shim.dom.parentNode != pn){
17650 pn.insertBefore(shim.dom, this.dom);
17652 shim.setStyle('z-index', this.getZIndex()-2);
17657 hideShim : function(){
17659 this.shim.setDisplayed(false);
17660 shims.push(this.shim);
17665 disableShadow : function(){
17667 this.shadowDisabled = true;
17668 this.shadow.hide();
17669 this.lastShadowOffset = this.shadowOffset;
17670 this.shadowOffset = 0;
17674 enableShadow : function(show){
17676 this.shadowDisabled = false;
17677 this.shadowOffset = this.lastShadowOffset;
17678 delete this.lastShadowOffset;
17686 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17687 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17688 sync : function(doShow){
17689 var sw = this.shadow;
17690 if(!this.updating && this.isVisible() && (sw || this.useShim)){
17691 var sh = this.getShim();
17693 var w = this.getWidth(),
17694 h = this.getHeight();
17696 var l = this.getLeft(true),
17697 t = this.getTop(true);
17699 if(sw && !this.shadowDisabled){
17700 if(doShow && !sw.isVisible()){
17703 sw.realign(l, t, w, h);
17709 // fit the shim behind the shadow, so it is shimmed too
17710 var a = sw.adjusts, s = sh.dom.style;
17711 s.left = (Math.min(l, l+a.l))+"px";
17712 s.top = (Math.min(t, t+a.t))+"px";
17713 s.width = (w+a.w)+"px";
17714 s.height = (h+a.h)+"px";
17721 sh.setLeftTop(l, t);
17728 destroy : function(){
17731 this.shadow.hide();
17733 this.removeAllListeners();
17734 var pn = this.dom.parentNode;
17736 pn.removeChild(this.dom);
17738 Roo.Element.uncache(this.id);
17741 remove : function(){
17746 beginUpdate : function(){
17747 this.updating = true;
17751 endUpdate : function(){
17752 this.updating = false;
17757 hideUnders : function(negOffset){
17759 this.shadow.hide();
17765 constrainXY : function(){
17766 if(this.constrain){
17767 var vw = Roo.lib.Dom.getViewWidth(),
17768 vh = Roo.lib.Dom.getViewHeight();
17769 var s = Roo.get(document).getScroll();
17771 var xy = this.getXY();
17772 var x = xy[0], y = xy[1];
17773 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17774 // only move it if it needs it
17776 // first validate right/bottom
17777 if((x + w) > vw+s.left){
17778 x = vw - w - this.shadowOffset;
17781 if((y + h) > vh+s.top){
17782 y = vh - h - this.shadowOffset;
17785 // then make sure top/left isn't negative
17796 var ay = this.avoidY;
17797 if(y <= ay && (y+h) >= ay){
17803 supr.setXY.call(this, xy);
17809 isVisible : function(){
17810 return this.visible;
17814 showAction : function(){
17815 this.visible = true; // track visibility to prevent getStyle calls
17816 if(this.useDisplay === true){
17817 this.setDisplayed("");
17818 }else if(this.lastXY){
17819 supr.setXY.call(this, this.lastXY);
17820 }else if(this.lastLT){
17821 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17826 hideAction : function(){
17827 this.visible = false;
17828 if(this.useDisplay === true){
17829 this.setDisplayed(false);
17831 this.setLeftTop(-10000,-10000);
17835 // overridden Element method
17836 setVisible : function(v, a, d, c, e){
17841 var cb = function(){
17846 }.createDelegate(this);
17847 supr.setVisible.call(this, true, true, d, cb, e);
17850 this.hideUnders(true);
17859 }.createDelegate(this);
17861 supr.setVisible.call(this, v, a, d, cb, e);
17870 storeXY : function(xy){
17871 delete this.lastLT;
17875 storeLeftTop : function(left, top){
17876 delete this.lastXY;
17877 this.lastLT = [left, top];
17881 beforeFx : function(){
17882 this.beforeAction();
17883 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17887 afterFx : function(){
17888 Roo.Layer.superclass.afterFx.apply(this, arguments);
17889 this.sync(this.isVisible());
17893 beforeAction : function(){
17894 if(!this.updating && this.shadow){
17895 this.shadow.hide();
17899 // overridden Element method
17900 setLeft : function(left){
17901 this.storeLeftTop(left, this.getTop(true));
17902 supr.setLeft.apply(this, arguments);
17906 setTop : function(top){
17907 this.storeLeftTop(this.getLeft(true), top);
17908 supr.setTop.apply(this, arguments);
17912 setLeftTop : function(left, top){
17913 this.storeLeftTop(left, top);
17914 supr.setLeftTop.apply(this, arguments);
17918 setXY : function(xy, a, d, c, e){
17920 this.beforeAction();
17922 var cb = this.createCB(c);
17923 supr.setXY.call(this, xy, a, d, cb, e);
17930 createCB : function(c){
17941 // overridden Element method
17942 setX : function(x, a, d, c, e){
17943 this.setXY([x, this.getY()], a, d, c, e);
17946 // overridden Element method
17947 setY : function(y, a, d, c, e){
17948 this.setXY([this.getX(), y], a, d, c, e);
17951 // overridden Element method
17952 setSize : function(w, h, a, d, c, e){
17953 this.beforeAction();
17954 var cb = this.createCB(c);
17955 supr.setSize.call(this, w, h, a, d, cb, e);
17961 // overridden Element method
17962 setWidth : function(w, a, d, c, e){
17963 this.beforeAction();
17964 var cb = this.createCB(c);
17965 supr.setWidth.call(this, w, a, d, cb, e);
17971 // overridden Element method
17972 setHeight : function(h, a, d, c, e){
17973 this.beforeAction();
17974 var cb = this.createCB(c);
17975 supr.setHeight.call(this, h, a, d, cb, e);
17981 // overridden Element method
17982 setBounds : function(x, y, w, h, a, d, c, e){
17983 this.beforeAction();
17984 var cb = this.createCB(c);
17986 this.storeXY([x, y]);
17987 supr.setXY.call(this, [x, y]);
17988 supr.setSize.call(this, w, h, a, d, cb, e);
17991 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17997 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17998 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17999 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18000 * @param {Number} zindex The new z-index to set
18001 * @return {this} The Layer
18003 setZIndex : function(zindex){
18004 this.zindex = zindex;
18005 this.setStyle("z-index", zindex + 2);
18007 this.shadow.setZIndex(zindex + 1);
18010 this.shim.setStyle("z-index", zindex);
18015 * Original code for Roojs - LGPL
18016 * <script type="text/javascript">
18020 * @class Roo.XComponent
18021 * A delayed Element creator...
18022 * Or a way to group chunks of interface together.
18023 * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18024 * used in conjunction with XComponent.build() it will create an instance of each element,
18025 * then call addxtype() to build the User interface.
18027 * Mypart.xyx = new Roo.XComponent({
18029 parent : 'Mypart.xyz', // empty == document.element.!!
18033 disabled : function() {}
18035 tree : function() { // return an tree of xtype declared components
18039 xtype : 'NestedLayoutPanel',
18046 * It can be used to build a big heiracy, with parent etc.
18047 * or you can just use this to render a single compoent to a dom element
18048 * MYPART.render(Roo.Element | String(id) | dom_element )
18055 * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18056 * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18058 * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18060 * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18061 * - if mulitple topModules exist, the last one is defined as the top module.
18065 * When the top level or multiple modules are to embedded into a existing HTML page,
18066 * the parent element can container '#id' of the element where the module will be drawn.
18070 * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18071 * it relies more on a include mechanism, where sub modules are included into an outer page.
18072 * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18074 * Bootstrap Roo Included elements
18076 * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18077 * hence confusing the component builder as it thinks there are multiple top level elements.
18079 * String Over-ride & Translations
18081 * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18082 * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18083 * are needed. @see Roo.XComponent.overlayString
18087 * @extends Roo.util.Observable
18089 * @param cfg {Object} configuration of component
18092 Roo.XComponent = function(cfg) {
18093 Roo.apply(this, cfg);
18097 * Fires when this the componnt is built
18098 * @param {Roo.XComponent} c the component
18103 this.region = this.region || 'center'; // default..
18104 Roo.XComponent.register(this);
18105 this.modules = false;
18106 this.el = false; // where the layout goes..
18110 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18113 * The created element (with Roo.factory())
18114 * @type {Roo.Layout}
18120 * for BC - use el in new code
18121 * @type {Roo.Layout}
18127 * for BC - use el in new code
18128 * @type {Roo.Layout}
18133 * @cfg {Function|boolean} disabled
18134 * If this module is disabled by some rule, return true from the funtion
18139 * @cfg {String} parent
18140 * Name of parent element which it get xtype added to..
18145 * @cfg {String} order
18146 * Used to set the order in which elements are created (usefull for multiple tabs)
18151 * @cfg {String} name
18152 * String to display while loading.
18156 * @cfg {String} region
18157 * Region to render component to (defaults to center)
18162 * @cfg {Array} items
18163 * A single item array - the first element is the root of the tree..
18164 * It's done this way to stay compatible with the Xtype system...
18170 * The method that retuns the tree of parts that make up this compoennt
18177 * render element to dom or tree
18178 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18181 render : function(el)
18185 var hp = this.parent ? 1 : 0;
18186 Roo.debug && Roo.log(this);
18188 var tree = this._tree ? this._tree() : this.tree();
18191 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18192 // if parent is a '#.....' string, then let's use that..
18193 var ename = this.parent.substr(1);
18194 this.parent = false;
18195 Roo.debug && Roo.log(ename);
18197 case 'bootstrap-body':
18198 if (typeof(tree.el) != 'undefined' && tree.el == document.body) {
18199 // this is the BorderLayout standard?
18200 this.parent = { el : true };
18203 if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype) > -1) {
18204 // need to insert stuff...
18206 el : new Roo.bootstrap.layout.Border({
18207 el : document.body,
18213 tabPosition: 'top',
18214 //resizeTabs: true,
18215 alwaysShowTabs: true,
18225 if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18226 this.parent = { el : new Roo.bootstrap.Body() };
18227 Roo.debug && Roo.log("setting el to doc body");
18230 throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18234 this.parent = { el : true};
18237 el = Roo.get(ename);
18238 if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18239 this.parent = { el : true};
18246 if (!el && !this.parent) {
18247 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18252 Roo.debug && Roo.log("EL:");
18253 Roo.debug && Roo.log(el);
18254 Roo.debug && Roo.log("this.parent.el:");
18255 Roo.debug && Roo.log(this.parent.el);
18258 // altertive root elements ??? - we need a better way to indicate these.
18259 var is_alt = Roo.XComponent.is_alt ||
18260 (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18261 (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18262 (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18266 if (!this.parent && is_alt) {
18267 //el = Roo.get(document.body);
18268 this.parent = { el : true };
18273 if (!this.parent) {
18275 Roo.debug && Roo.log("no parent - creating one");
18277 el = el ? Roo.get(el) : false;
18279 if (typeof(Roo.BorderLayout) == 'undefined' ) {
18282 el : new Roo.bootstrap.layout.Border({
18283 el: el || document.body,
18289 tabPosition: 'top',
18290 //resizeTabs: true,
18291 alwaysShowTabs: false,
18294 overflow: 'visible'
18300 // it's a top level one..
18302 el : new Roo.BorderLayout(el || document.body, {
18307 tabPosition: 'top',
18308 //resizeTabs: true,
18309 alwaysShowTabs: el && hp? false : true,
18310 hideTabs: el || !hp ? true : false,
18318 if (!this.parent.el) {
18319 // probably an old style ctor, which has been disabled.
18323 // The 'tree' method is '_tree now'
18325 tree.region = tree.region || this.region;
18326 var is_body = false;
18327 if (this.parent.el === true) {
18328 // bootstrap... - body..
18332 this.parent.el = Roo.factory(tree);
18336 this.el = this.parent.el.addxtype(tree, undefined, is_body);
18337 this.fireEvent('built', this);
18339 this.panel = this.el;
18340 this.layout = this.panel.layout;
18341 this.parentLayout = this.parent.layout || false;
18347 Roo.apply(Roo.XComponent, {
18349 * @property hideProgress
18350 * true to disable the building progress bar.. usefull on single page renders.
18353 hideProgress : false,
18355 * @property buildCompleted
18356 * True when the builder has completed building the interface.
18359 buildCompleted : false,
18362 * @property topModule
18363 * the upper most module - uses document.element as it's constructor.
18370 * @property modules
18371 * array of modules to be created by registration system.
18372 * @type {Array} of Roo.XComponent
18377 * @property elmodules
18378 * array of modules to be created by which use #ID
18379 * @type {Array} of Roo.XComponent
18386 * Is an alternative Root - normally used by bootstrap or other systems,
18387 * where the top element in the tree can wrap 'body'
18388 * @type {boolean} (default false)
18393 * @property build_from_html
18394 * Build elements from html - used by bootstrap HTML stuff
18395 * - this is cleared after build is completed
18396 * @type {boolean} (default false)
18399 build_from_html : false,
18401 * Register components to be built later.
18403 * This solves the following issues
18404 * - Building is not done on page load, but after an authentication process has occured.
18405 * - Interface elements are registered on page load
18406 * - Parent Interface elements may not be loaded before child, so this handles that..
18413 module : 'Pman.Tab.projectMgr',
18415 parent : 'Pman.layout',
18416 disabled : false, // or use a function..
18419 * * @param {Object} details about module
18421 register : function(obj) {
18423 Roo.XComponent.event.fireEvent('register', obj);
18424 switch(typeof(obj.disabled) ) {
18430 if ( obj.disabled() ) {
18436 if (obj.disabled || obj.region == '#disabled') {
18442 this.modules.push(obj);
18446 * convert a string to an object..
18447 * eg. 'AAA.BBB' -> finds AAA.BBB
18451 toObject : function(str)
18453 if (!str || typeof(str) == 'object') {
18456 if (str.substring(0,1) == '#') {
18460 var ar = str.split('.');
18465 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18467 throw "Module not found : " + str;
18471 throw "Module not found : " + str;
18473 Roo.each(ar, function(e) {
18474 if (typeof(o[e]) == 'undefined') {
18475 throw "Module not found : " + str;
18486 * move modules into their correct place in the tree..
18489 preBuild : function ()
18492 Roo.each(this.modules , function (obj)
18494 Roo.XComponent.event.fireEvent('beforebuild', obj);
18496 var opar = obj.parent;
18498 obj.parent = this.toObject(opar);
18500 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18505 Roo.debug && Roo.log("GOT top level module");
18506 Roo.debug && Roo.log(obj);
18507 obj.modules = new Roo.util.MixedCollection(false,
18508 function(o) { return o.order + '' }
18510 this.topModule = obj;
18513 // parent is a string (usually a dom element name..)
18514 if (typeof(obj.parent) == 'string') {
18515 this.elmodules.push(obj);
18518 if (obj.parent.constructor != Roo.XComponent) {
18519 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18521 if (!obj.parent.modules) {
18522 obj.parent.modules = new Roo.util.MixedCollection(false,
18523 function(o) { return o.order + '' }
18526 if (obj.parent.disabled) {
18527 obj.disabled = true;
18529 obj.parent.modules.add(obj);
18534 * make a list of modules to build.
18535 * @return {Array} list of modules.
18538 buildOrder : function()
18541 var cmp = function(a,b) {
18542 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18544 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18545 throw "No top level modules to build";
18548 // make a flat list in order of modules to build.
18549 var mods = this.topModule ? [ this.topModule ] : [];
18552 // elmodules (is a list of DOM based modules )
18553 Roo.each(this.elmodules, function(e) {
18555 if (!this.topModule &&
18556 typeof(e.parent) == 'string' &&
18557 e.parent.substring(0,1) == '#' &&
18558 Roo.get(e.parent.substr(1))
18561 _this.topModule = e;
18567 // add modules to their parents..
18568 var addMod = function(m) {
18569 Roo.debug && Roo.log("build Order: add: " + m.name);
18572 if (m.modules && !m.disabled) {
18573 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18574 m.modules.keySort('ASC', cmp );
18575 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18577 m.modules.each(addMod);
18579 Roo.debug && Roo.log("build Order: no child modules");
18581 // not sure if this is used any more..
18583 m.finalize.name = m.name + " (clean up) ";
18584 mods.push(m.finalize);
18588 if (this.topModule && this.topModule.modules) {
18589 this.topModule.modules.keySort('ASC', cmp );
18590 this.topModule.modules.each(addMod);
18596 * Build the registered modules.
18597 * @param {Object} parent element.
18598 * @param {Function} optional method to call after module has been added.
18602 build : function(opts)
18605 if (typeof(opts) != 'undefined') {
18606 Roo.apply(this,opts);
18610 var mods = this.buildOrder();
18612 //this.allmods = mods;
18613 //Roo.debug && Roo.log(mods);
18615 if (!mods.length) { // should not happen
18616 throw "NO modules!!!";
18620 var msg = "Building Interface...";
18621 // flash it up as modal - so we store the mask!?
18622 if (!this.hideProgress && Roo.MessageBox) {
18623 Roo.MessageBox.show({ title: 'loading' });
18624 Roo.MessageBox.show({
18625 title: "Please wait...",
18635 var total = mods.length;
18638 var progressRun = function() {
18639 if (!mods.length) {
18640 Roo.debug && Roo.log('hide?');
18641 if (!this.hideProgress && Roo.MessageBox) {
18642 Roo.MessageBox.hide();
18644 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18646 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18652 var m = mods.shift();
18655 Roo.debug && Roo.log(m);
18656 // not sure if this is supported any more.. - modules that are are just function
18657 if (typeof(m) == 'function') {
18659 return progressRun.defer(10, _this);
18663 msg = "Building Interface " + (total - mods.length) +
18665 (m.name ? (' - ' + m.name) : '');
18666 Roo.debug && Roo.log(msg);
18667 if (!_this.hideProgress && Roo.MessageBox) {
18668 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
18672 // is the module disabled?
18673 var disabled = (typeof(m.disabled) == 'function') ?
18674 m.disabled.call(m.module.disabled) : m.disabled;
18678 return progressRun(); // we do not update the display!
18686 // it's 10 on top level, and 1 on others??? why...
18687 return progressRun.defer(10, _this);
18690 progressRun.defer(1, _this);
18696 * Overlay a set of modified strings onto a component
18697 * This is dependant on our builder exporting the strings and 'named strings' elements.
18699 * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18700 * @param {Object} associative array of 'named' string and it's new value.
18703 overlayStrings : function( component, strings )
18705 if (typeof(component['_named_strings']) == 'undefined') {
18706 throw "ERROR: component does not have _named_strings";
18708 for ( var k in strings ) {
18709 var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18710 if (md !== false) {
18711 component['_strings'][md] = strings[k];
18713 Roo.log('could not find named string: ' + k + ' in');
18714 Roo.log(component);
18729 * wrapper for event.on - aliased later..
18730 * Typically use to register a event handler for register:
18732 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18741 Roo.XComponent.event = new Roo.util.Observable({
18745 * Fires when an Component is registered,
18746 * set the disable property on the Component to stop registration.
18747 * @param {Roo.XComponent} c the component being registerd.
18752 * @event beforebuild
18753 * Fires before each Component is built
18754 * can be used to apply permissions.
18755 * @param {Roo.XComponent} c the component being registerd.
18758 'beforebuild' : true,
18760 * @event buildcomplete
18761 * Fires on the top level element when all elements have been built
18762 * @param {Roo.XComponent} the top level component.
18764 'buildcomplete' : true
18769 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
18772 * marked - a markdown parser
18773 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18774 * https://github.com/chjj/marked
18780 * Roo.Markdown - is a very crude wrapper around marked..
18784 * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18786 * Note: move the sample code to the bottom of this
18787 * file before uncommenting it.
18792 Roo.Markdown.toHtml = function(text) {
18794 var c = new Roo.Markdown.marked.setOptions({
18795 renderer: new Roo.Markdown.marked.Renderer(),
18806 text = text.replace(/\\\n/g,' ');
18807 return Roo.Markdown.marked(text);
18812 // Wraps all "globals" so that the only thing
18813 // exposed is makeHtml().
18819 * eval:var:unescape
18827 var escape = function (html, encode) {
18829 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18830 .replace(/</g, '<')
18831 .replace(/>/g, '>')
18832 .replace(/"/g, '"')
18833 .replace(/'/g, ''');
18836 var unescape = function (html) {
18837 // explicitly match decimal, hex, and named HTML entities
18838 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18839 n = n.toLowerCase();
18840 if (n === 'colon') { return ':'; }
18841 if (n.charAt(0) === '#') {
18842 return n.charAt(1) === 'x'
18843 ? String.fromCharCode(parseInt(n.substring(2), 16))
18844 : String.fromCharCode(+n.substring(1));
18850 var replace = function (regex, opt) {
18851 regex = regex.source;
18853 return function self(name, val) {
18854 if (!name) { return new RegExp(regex, opt); }
18855 val = val.source || val;
18856 val = val.replace(/(^|[^\[])\^/g, '$1');
18857 regex = regex.replace(name, val);
18866 var noop = function () {}
18872 var merge = function (obj) {
18877 for (; i < arguments.length; i++) {
18878 target = arguments[i];
18879 for (key in target) {
18880 if (Object.prototype.hasOwnProperty.call(target, key)) {
18881 obj[key] = target[key];
18891 * Block-Level Grammar
18899 code: /^( {4}[^\n]+\n*)+/,
18901 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18902 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18904 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18905 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18906 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18907 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18908 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18910 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18914 block.bullet = /(?:[*+-]|\d+\.)/;
18915 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18916 block.item = replace(block.item, 'gm')
18917 (/bull/g, block.bullet)
18920 block.list = replace(block.list)
18921 (/bull/g, block.bullet)
18922 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18923 ('def', '\\n+(?=' + block.def.source + ')')
18926 block.blockquote = replace(block.blockquote)
18930 block._tag = '(?!(?:'
18931 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18932 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18933 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18935 block.html = replace(block.html)
18936 ('comment', /<!--[\s\S]*?-->/)
18937 ('closed', /<(tag)[\s\S]+?<\/\1>/)
18938 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18939 (/tag/g, block._tag)
18942 block.paragraph = replace(block.paragraph)
18944 ('heading', block.heading)
18945 ('lheading', block.lheading)
18946 ('blockquote', block.blockquote)
18947 ('tag', '<' + block._tag)
18952 * Normal Block Grammar
18955 block.normal = merge({}, block);
18958 * GFM Block Grammar
18961 block.gfm = merge({}, block.normal, {
18962 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18964 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18967 block.gfm.paragraph = replace(block.paragraph)
18969 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18970 + block.list.source.replace('\\1', '\\3') + '|')
18974 * GFM + Tables Block Grammar
18977 block.tables = merge({}, block.gfm, {
18978 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18979 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18986 var Lexer = function (options) {
18988 this.tokens.links = {};
18989 this.options = options || marked.defaults;
18990 this.rules = block.normal;
18992 if (this.options.gfm) {
18993 if (this.options.tables) {
18994 this.rules = block.tables;
18996 this.rules = block.gfm;
19002 * Expose Block Rules
19005 Lexer.rules = block;
19008 * Static Lex Method
19011 Lexer.lex = function(src, options) {
19012 var lexer = new Lexer(options);
19013 return lexer.lex(src);
19020 Lexer.prototype.lex = function(src) {
19022 .replace(/\r\n|\r/g, '\n')
19023 .replace(/\t/g, ' ')
19024 .replace(/\u00a0/g, ' ')
19025 .replace(/\u2424/g, '\n');
19027 return this.token(src, true);
19034 Lexer.prototype.token = function(src, top, bq) {
19035 var src = src.replace(/^ +$/gm, '')
19048 if (cap = this.rules.newline.exec(src)) {
19049 src = src.substring(cap[0].length);
19050 if (cap[0].length > 1) {
19058 if (cap = this.rules.code.exec(src)) {
19059 src = src.substring(cap[0].length);
19060 cap = cap[0].replace(/^ {4}/gm, '');
19063 text: !this.options.pedantic
19064 ? cap.replace(/\n+$/, '')
19071 if (cap = this.rules.fences.exec(src)) {
19072 src = src.substring(cap[0].length);
19082 if (cap = this.rules.heading.exec(src)) {
19083 src = src.substring(cap[0].length);
19086 depth: cap[1].length,
19092 // table no leading pipe (gfm)
19093 if (top && (cap = this.rules.nptable.exec(src))) {
19094 src = src.substring(cap[0].length);
19098 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19099 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19100 cells: cap[3].replace(/\n$/, '').split('\n')
19103 for (i = 0; i < item.align.length; i++) {
19104 if (/^ *-+: *$/.test(item.align[i])) {
19105 item.align[i] = 'right';
19106 } else if (/^ *:-+: *$/.test(item.align[i])) {
19107 item.align[i] = 'center';
19108 } else if (/^ *:-+ *$/.test(item.align[i])) {
19109 item.align[i] = 'left';
19111 item.align[i] = null;
19115 for (i = 0; i < item.cells.length; i++) {
19116 item.cells[i] = item.cells[i].split(/ *\| */);
19119 this.tokens.push(item);
19125 if (cap = this.rules.lheading.exec(src)) {
19126 src = src.substring(cap[0].length);
19129 depth: cap[2] === '=' ? 1 : 2,
19136 if (cap = this.rules.hr.exec(src)) {
19137 src = src.substring(cap[0].length);
19145 if (cap = this.rules.blockquote.exec(src)) {
19146 src = src.substring(cap[0].length);
19149 type: 'blockquote_start'
19152 cap = cap[0].replace(/^ *> ?/gm, '');
19154 // Pass `top` to keep the current
19155 // "toplevel" state. This is exactly
19156 // how markdown.pl works.
19157 this.token(cap, top, true);
19160 type: 'blockquote_end'
19167 if (cap = this.rules.list.exec(src)) {
19168 src = src.substring(cap[0].length);
19172 type: 'list_start',
19173 ordered: bull.length > 1
19176 // Get each top-level item.
19177 cap = cap[0].match(this.rules.item);
19183 for (; i < l; i++) {
19186 // Remove the list item's bullet
19187 // so it is seen as the next token.
19188 space = item.length;
19189 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19191 // Outdent whatever the
19192 // list item contains. Hacky.
19193 if (~item.indexOf('\n ')) {
19194 space -= item.length;
19195 item = !this.options.pedantic
19196 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19197 : item.replace(/^ {1,4}/gm, '');
19200 // Determine whether the next list item belongs here.
19201 // Backpedal if it does not belong in this list.
19202 if (this.options.smartLists && i !== l - 1) {
19203 b = block.bullet.exec(cap[i + 1])[0];
19204 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19205 src = cap.slice(i + 1).join('\n') + src;
19210 // Determine whether item is loose or not.
19211 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19212 // for discount behavior.
19213 loose = next || /\n\n(?!\s*$)/.test(item);
19215 next = item.charAt(item.length - 1) === '\n';
19216 if (!loose) { loose = next; }
19221 ? 'loose_item_start'
19222 : 'list_item_start'
19226 this.token(item, false, bq);
19229 type: 'list_item_end'
19241 if (cap = this.rules.html.exec(src)) {
19242 src = src.substring(cap[0].length);
19244 type: this.options.sanitize
19247 pre: !this.options.sanitizer
19248 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19255 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19256 src = src.substring(cap[0].length);
19257 this.tokens.links[cap[1].toLowerCase()] = {
19265 if (top && (cap = this.rules.table.exec(src))) {
19266 src = src.substring(cap[0].length);
19270 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19271 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19272 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19275 for (i = 0; i < item.align.length; i++) {
19276 if (/^ *-+: *$/.test(item.align[i])) {
19277 item.align[i] = 'right';
19278 } else if (/^ *:-+: *$/.test(item.align[i])) {
19279 item.align[i] = 'center';
19280 } else if (/^ *:-+ *$/.test(item.align[i])) {
19281 item.align[i] = 'left';
19283 item.align[i] = null;
19287 for (i = 0; i < item.cells.length; i++) {
19288 item.cells[i] = item.cells[i]
19289 .replace(/^ *\| *| *\| *$/g, '')
19293 this.tokens.push(item);
19298 // top-level paragraph
19299 if (top && (cap = this.rules.paragraph.exec(src))) {
19300 src = src.substring(cap[0].length);
19303 text: cap[1].charAt(cap[1].length - 1) === '\n'
19304 ? cap[1].slice(0, -1)
19311 if (cap = this.rules.text.exec(src)) {
19312 // Top-level should never reach here.
19313 src = src.substring(cap[0].length);
19323 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19327 return this.tokens;
19331 * Inline-Level Grammar
19335 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19336 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19338 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19339 link: /^!?\[(inside)\]\(href\)/,
19340 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19341 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19342 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19343 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19344 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19345 br: /^ {2,}\n(?!\s*$)/,
19347 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19350 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19351 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19353 inline.link = replace(inline.link)
19354 ('inside', inline._inside)
19355 ('href', inline._href)
19358 inline.reflink = replace(inline.reflink)
19359 ('inside', inline._inside)
19363 * Normal Inline Grammar
19366 inline.normal = merge({}, inline);
19369 * Pedantic Inline Grammar
19372 inline.pedantic = merge({}, inline.normal, {
19373 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19374 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19378 * GFM Inline Grammar
19381 inline.gfm = merge({}, inline.normal, {
19382 escape: replace(inline.escape)('])', '~|])')(),
19383 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19384 del: /^~~(?=\S)([\s\S]*?\S)~~/,
19385 text: replace(inline.text)
19387 ('|', '|https?://|')
19392 * GFM + Line Breaks Inline Grammar
19395 inline.breaks = merge({}, inline.gfm, {
19396 br: replace(inline.br)('{2,}', '*')(),
19397 text: replace(inline.gfm.text)('{2,}', '*')()
19401 * Inline Lexer & Compiler
19404 var InlineLexer = function (links, options) {
19405 this.options = options || marked.defaults;
19406 this.links = links;
19407 this.rules = inline.normal;
19408 this.renderer = this.options.renderer || new Renderer;
19409 this.renderer.options = this.options;
19413 Error('Tokens array requires a `links` property.');
19416 if (this.options.gfm) {
19417 if (this.options.breaks) {
19418 this.rules = inline.breaks;
19420 this.rules = inline.gfm;
19422 } else if (this.options.pedantic) {
19423 this.rules = inline.pedantic;
19428 * Expose Inline Rules
19431 InlineLexer.rules = inline;
19434 * Static Lexing/Compiling Method
19437 InlineLexer.output = function(src, links, options) {
19438 var inline = new InlineLexer(links, options);
19439 return inline.output(src);
19446 InlineLexer.prototype.output = function(src) {
19455 if (cap = this.rules.escape.exec(src)) {
19456 src = src.substring(cap[0].length);
19462 if (cap = this.rules.autolink.exec(src)) {
19463 src = src.substring(cap[0].length);
19464 if (cap[2] === '@') {
19465 text = cap[1].charAt(6) === ':'
19466 ? this.mangle(cap[1].substring(7))
19467 : this.mangle(cap[1]);
19468 href = this.mangle('mailto:') + text;
19470 text = escape(cap[1]);
19473 out += this.renderer.link(href, null, text);
19478 if (!this.inLink && (cap = this.rules.url.exec(src))) {
19479 src = src.substring(cap[0].length);
19480 text = escape(cap[1]);
19482 out += this.renderer.link(href, null, text);
19487 if (cap = this.rules.tag.exec(src)) {
19488 if (!this.inLink && /^<a /i.test(cap[0])) {
19489 this.inLink = true;
19490 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19491 this.inLink = false;
19493 src = src.substring(cap[0].length);
19494 out += this.options.sanitize
19495 ? this.options.sanitizer
19496 ? this.options.sanitizer(cap[0])
19503 if (cap = this.rules.link.exec(src)) {
19504 src = src.substring(cap[0].length);
19505 this.inLink = true;
19506 out += this.outputLink(cap, {
19510 this.inLink = false;
19515 if ((cap = this.rules.reflink.exec(src))
19516 || (cap = this.rules.nolink.exec(src))) {
19517 src = src.substring(cap[0].length);
19518 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19519 link = this.links[link.toLowerCase()];
19520 if (!link || !link.href) {
19521 out += cap[0].charAt(0);
19522 src = cap[0].substring(1) + src;
19525 this.inLink = true;
19526 out += this.outputLink(cap, link);
19527 this.inLink = false;
19532 if (cap = this.rules.strong.exec(src)) {
19533 src = src.substring(cap[0].length);
19534 out += this.renderer.strong(this.output(cap[2] || cap[1]));
19539 if (cap = this.rules.em.exec(src)) {
19540 src = src.substring(cap[0].length);
19541 out += this.renderer.em(this.output(cap[2] || cap[1]));
19546 if (cap = this.rules.code.exec(src)) {
19547 src = src.substring(cap[0].length);
19548 out += this.renderer.codespan(escape(cap[2], true));
19553 if (cap = this.rules.br.exec(src)) {
19554 src = src.substring(cap[0].length);
19555 out += this.renderer.br();
19560 if (cap = this.rules.del.exec(src)) {
19561 src = src.substring(cap[0].length);
19562 out += this.renderer.del(this.output(cap[1]));
19567 if (cap = this.rules.text.exec(src)) {
19568 src = src.substring(cap[0].length);
19569 out += this.renderer.text(escape(this.smartypants(cap[0])));
19575 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19586 InlineLexer.prototype.outputLink = function(cap, link) {
19587 var href = escape(link.href)
19588 , title = link.title ? escape(link.title) : null;
19590 return cap[0].charAt(0) !== '!'
19591 ? this.renderer.link(href, title, this.output(cap[1]))
19592 : this.renderer.image(href, title, escape(cap[1]));
19596 * Smartypants Transformations
19599 InlineLexer.prototype.smartypants = function(text) {
19600 if (!this.options.smartypants) { return text; }
19603 .replace(/---/g, '\u2014')
19605 .replace(/--/g, '\u2013')
19607 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19608 // closing singles & apostrophes
19609 .replace(/'/g, '\u2019')
19611 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19613 .replace(/"/g, '\u201d')
19615 .replace(/\.{3}/g, '\u2026');
19622 InlineLexer.prototype.mangle = function(text) {
19623 if (!this.options.mangle) { return text; }
19629 for (; i < l; i++) {
19630 ch = text.charCodeAt(i);
19631 if (Math.random() > 0.5) {
19632 ch = 'x' + ch.toString(16);
19634 out += '&#' + ch + ';';
19645 * eval:var:Renderer
19648 var Renderer = function (options) {
19649 this.options = options || {};
19652 Renderer.prototype.code = function(code, lang, escaped) {
19653 if (this.options.highlight) {
19654 var out = this.options.highlight(code, lang);
19655 if (out != null && out !== code) {
19660 // hack!!! - it's already escapeD?
19665 return '<pre><code>'
19666 + (escaped ? code : escape(code, true))
19667 + '\n</code></pre>';
19670 return '<pre><code class="'
19671 + this.options.langPrefix
19672 + escape(lang, true)
19674 + (escaped ? code : escape(code, true))
19675 + '\n</code></pre>\n';
19678 Renderer.prototype.blockquote = function(quote) {
19679 return '<blockquote>\n' + quote + '</blockquote>\n';
19682 Renderer.prototype.html = function(html) {
19686 Renderer.prototype.heading = function(text, level, raw) {
19690 + this.options.headerPrefix
19691 + raw.toLowerCase().replace(/[^\w]+/g, '-')
19699 Renderer.prototype.hr = function() {
19700 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19703 Renderer.prototype.list = function(body, ordered) {
19704 var type = ordered ? 'ol' : 'ul';
19705 return '<' + type + '>\n' + body + '</' + type + '>\n';
19708 Renderer.prototype.listitem = function(text) {
19709 return '<li>' + text + '</li>\n';
19712 Renderer.prototype.paragraph = function(text) {
19713 return '<p>' + text + '</p>\n';
19716 Renderer.prototype.table = function(header, body) {
19717 return '<table class="table table-striped">\n'
19727 Renderer.prototype.tablerow = function(content) {
19728 return '<tr>\n' + content + '</tr>\n';
19731 Renderer.prototype.tablecell = function(content, flags) {
19732 var type = flags.header ? 'th' : 'td';
19733 var tag = flags.align
19734 ? '<' + type + ' style="text-align:' + flags.align + '">'
19735 : '<' + type + '>';
19736 return tag + content + '</' + type + '>\n';
19739 // span level renderer
19740 Renderer.prototype.strong = function(text) {
19741 return '<strong>' + text + '</strong>';
19744 Renderer.prototype.em = function(text) {
19745 return '<em>' + text + '</em>';
19748 Renderer.prototype.codespan = function(text) {
19749 return '<code>' + text + '</code>';
19752 Renderer.prototype.br = function() {
19753 return this.options.xhtml ? '<br/>' : '<br>';
19756 Renderer.prototype.del = function(text) {
19757 return '<del>' + text + '</del>';
19760 Renderer.prototype.link = function(href, title, text) {
19761 if (this.options.sanitize) {
19763 var prot = decodeURIComponent(unescape(href))
19764 .replace(/[^\w:]/g, '')
19769 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19773 var out = '<a href="' + href + '"';
19775 out += ' title="' + title + '"';
19777 out += '>' + text + '</a>';
19781 Renderer.prototype.image = function(href, title, text) {
19782 var out = '<img src="' + href + '" alt="' + text + '"';
19784 out += ' title="' + title + '"';
19786 out += this.options.xhtml ? '/>' : '>';
19790 Renderer.prototype.text = function(text) {
19795 * Parsing & Compiling
19801 var Parser= function (options) {
19804 this.options = options || marked.defaults;
19805 this.options.renderer = this.options.renderer || new Renderer;
19806 this.renderer = this.options.renderer;
19807 this.renderer.options = this.options;
19811 * Static Parse Method
19814 Parser.parse = function(src, options, renderer) {
19815 var parser = new Parser(options, renderer);
19816 return parser.parse(src);
19823 Parser.prototype.parse = function(src) {
19824 this.inline = new InlineLexer(src.links, this.options, this.renderer);
19825 this.tokens = src.reverse();
19828 while (this.next()) {
19839 Parser.prototype.next = function() {
19840 return this.token = this.tokens.pop();
19844 * Preview Next Token
19847 Parser.prototype.peek = function() {
19848 return this.tokens[this.tokens.length - 1] || 0;
19852 * Parse Text Tokens
19855 Parser.prototype.parseText = function() {
19856 var body = this.token.text;
19858 while (this.peek().type === 'text') {
19859 body += '\n' + this.next().text;
19862 return this.inline.output(body);
19866 * Parse Current Token
19869 Parser.prototype.tok = function() {
19870 switch (this.token.type) {
19875 return this.renderer.hr();
19878 return this.renderer.heading(
19879 this.inline.output(this.token.text),
19884 return this.renderer.code(this.token.text,
19886 this.token.escaped);
19899 for (i = 0; i < this.token.header.length; i++) {
19900 flags = { header: true, align: this.token.align[i] };
19901 cell += this.renderer.tablecell(
19902 this.inline.output(this.token.header[i]),
19903 { header: true, align: this.token.align[i] }
19906 header += this.renderer.tablerow(cell);
19908 for (i = 0; i < this.token.cells.length; i++) {
19909 row = this.token.cells[i];
19912 for (j = 0; j < row.length; j++) {
19913 cell += this.renderer.tablecell(
19914 this.inline.output(row[j]),
19915 { header: false, align: this.token.align[j] }
19919 body += this.renderer.tablerow(cell);
19921 return this.renderer.table(header, body);
19923 case 'blockquote_start': {
19926 while (this.next().type !== 'blockquote_end') {
19927 body += this.tok();
19930 return this.renderer.blockquote(body);
19932 case 'list_start': {
19934 , ordered = this.token.ordered;
19936 while (this.next().type !== 'list_end') {
19937 body += this.tok();
19940 return this.renderer.list(body, ordered);
19942 case 'list_item_start': {
19945 while (this.next().type !== 'list_item_end') {
19946 body += this.token.type === 'text'
19951 return this.renderer.listitem(body);
19953 case 'loose_item_start': {
19956 while (this.next().type !== 'list_item_end') {
19957 body += this.tok();
19960 return this.renderer.listitem(body);
19963 var html = !this.token.pre && !this.options.pedantic
19964 ? this.inline.output(this.token.text)
19966 return this.renderer.html(html);
19968 case 'paragraph': {
19969 return this.renderer.paragraph(this.inline.output(this.token.text));
19972 return this.renderer.paragraph(this.parseText());
19984 var marked = function (src, opt, callback) {
19985 if (callback || typeof opt === 'function') {
19991 opt = merge({}, marked.defaults, opt || {});
19993 var highlight = opt.highlight
19999 tokens = Lexer.lex(src, opt)
20001 return callback(e);
20004 pending = tokens.length;
20008 var done = function(err) {
20010 opt.highlight = highlight;
20011 return callback(err);
20017 out = Parser.parse(tokens, opt);
20022 opt.highlight = highlight;
20026 : callback(null, out);
20029 if (!highlight || highlight.length < 3) {
20033 delete opt.highlight;
20035 if (!pending) { return done(); }
20037 for (; i < tokens.length; i++) {
20039 if (token.type !== 'code') {
20040 return --pending || done();
20042 return highlight(token.text, token.lang, function(err, code) {
20043 if (err) { return done(err); }
20044 if (code == null || code === token.text) {
20045 return --pending || done();
20048 token.escaped = true;
20049 --pending || done();
20057 if (opt) { opt = merge({}, marked.defaults, opt); }
20058 return Parser.parse(Lexer.lex(src, opt), opt);
20060 e.message += '\nPlease report this to https://github.com/chjj/marked.';
20061 if ((opt || marked.defaults).silent) {
20062 return '<p>An error occured:</p><pre>'
20063 + escape(e.message + '', true)
20075 marked.setOptions = function(opt) {
20076 merge(marked.defaults, opt);
20080 marked.defaults = {
20091 langPrefix: 'lang-',
20092 smartypants: false,
20094 renderer: new Renderer,
20102 marked.Parser = Parser;
20103 marked.parser = Parser.parse;
20105 marked.Renderer = Renderer;
20107 marked.Lexer = Lexer;
20108 marked.lexer = Lexer.lex;
20110 marked.InlineLexer = InlineLexer;
20111 marked.inlineLexer = InlineLexer.output;
20113 marked.parse = marked;
20115 Roo.Markdown.marked = marked;
20119 * Ext JS Library 1.1.1
20120 * Copyright(c) 2006-2007, Ext JS, LLC.
20122 * Originally Released Under LGPL - original licence link has changed is not relivant.
20125 * <script type="text/javascript">
20131 * These classes are derivatives of the similarly named classes in the YUI Library.
20132 * The original license:
20133 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20134 * Code licensed under the BSD License:
20135 * http://developer.yahoo.net/yui/license.txt
20140 var Event=Roo.EventManager;
20141 var Dom=Roo.lib.Dom;
20144 * @class Roo.dd.DragDrop
20145 * @extends Roo.util.Observable
20146 * Defines the interface and base operation of items that that can be
20147 * dragged or can be drop targets. It was designed to be extended, overriding
20148 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20149 * Up to three html elements can be associated with a DragDrop instance:
20151 * <li>linked element: the element that is passed into the constructor.
20152 * This is the element which defines the boundaries for interaction with
20153 * other DragDrop objects.</li>
20154 * <li>handle element(s): The drag operation only occurs if the element that
20155 * was clicked matches a handle element. By default this is the linked
20156 * element, but there are times that you will want only a portion of the
20157 * linked element to initiate the drag operation, and the setHandleElId()
20158 * method provides a way to define this.</li>
20159 * <li>drag element: this represents the element that would be moved along
20160 * with the cursor during a drag operation. By default, this is the linked
20161 * element itself as in {@link Roo.dd.DD}. setDragElId() lets you define
20162 * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20165 * This class should not be instantiated until the onload event to ensure that
20166 * the associated elements are available.
20167 * The following would define a DragDrop obj that would interact with any
20168 * other DragDrop obj in the "group1" group:
20170 * dd = new Roo.dd.DragDrop("div1", "group1");
20172 * Since none of the event handlers have been implemented, nothing would
20173 * actually happen if you were to run the code above. Normally you would
20174 * override this class or one of the default implementations, but you can
20175 * also override the methods you want on an instance of the class...
20177 * dd.onDragDrop = function(e, id) {
20178 * alert("dd was dropped on " + id);
20182 * @param {String} id of the element that is linked to this instance
20183 * @param {String} sGroup the group of related DragDrop objects
20184 * @param {object} config an object containing configurable attributes
20185 * Valid properties for DragDrop:
20186 * padding, isTarget, maintainOffset, primaryButtonOnly
20188 Roo.dd.DragDrop = function(id, sGroup, config) {
20190 this.init(id, sGroup, config);
20195 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20198 * The id of the element associated with this object. This is what we
20199 * refer to as the "linked element" because the size and position of
20200 * this element is used to determine when the drag and drop objects have
20208 * Configuration attributes passed into the constructor
20215 * The id of the element that will be dragged. By default this is same
20216 * as the linked element , but could be changed to another element. Ex:
20218 * @property dragElId
20225 * the id of the element that initiates the drag operation. By default
20226 * this is the linked element, but could be changed to be a child of this
20227 * element. This lets us do things like only starting the drag when the
20228 * header element within the linked html element is clicked.
20229 * @property handleElId
20236 * An associative array of HTML tags that will be ignored if clicked.
20237 * @property invalidHandleTypes
20238 * @type {string: string}
20240 invalidHandleTypes: null,
20243 * An associative array of ids for elements that will be ignored if clicked
20244 * @property invalidHandleIds
20245 * @type {string: string}
20247 invalidHandleIds: null,
20250 * An indexted array of css class names for elements that will be ignored
20252 * @property invalidHandleClasses
20255 invalidHandleClasses: null,
20258 * The linked element's absolute X position at the time the drag was
20260 * @property startPageX
20267 * The linked element's absolute X position at the time the drag was
20269 * @property startPageY
20276 * The group defines a logical collection of DragDrop objects that are
20277 * related. Instances only get events when interacting with other
20278 * DragDrop object in the same group. This lets us define multiple
20279 * groups using a single DragDrop subclass if we want.
20281 * @type {string: string}
20286 * Individual drag/drop instances can be locked. This will prevent
20287 * onmousedown start drag.
20295 * Lock this instance
20298 lock: function() { this.locked = true; },
20301 * Unlock this instace
20304 unlock: function() { this.locked = false; },
20307 * By default, all insances can be a drop target. This can be disabled by
20308 * setting isTarget to false.
20315 * The padding configured for this drag and drop object for calculating
20316 * the drop zone intersection with this object.
20323 * Cached reference to the linked element
20324 * @property _domRef
20330 * Internal typeof flag
20331 * @property __ygDragDrop
20334 __ygDragDrop: true,
20337 * Set to true when horizontal contraints are applied
20338 * @property constrainX
20345 * Set to true when vertical contraints are applied
20346 * @property constrainY
20353 * The left constraint
20361 * The right constraint
20369 * The up constraint
20378 * The down constraint
20386 * Maintain offsets when we resetconstraints. Set to true when you want
20387 * the position of the element relative to its parent to stay the same
20388 * when the page changes
20390 * @property maintainOffset
20393 maintainOffset: false,
20396 * Array of pixel locations the element will snap to if we specified a
20397 * horizontal graduation/interval. This array is generated automatically
20398 * when you define a tick interval.
20405 * Array of pixel locations the element will snap to if we specified a
20406 * vertical graduation/interval. This array is generated automatically
20407 * when you define a tick interval.
20414 * By default the drag and drop instance will only respond to the primary
20415 * button click (left button for a right-handed mouse). Set to true to
20416 * allow drag and drop to start with any mouse click that is propogated
20418 * @property primaryButtonOnly
20421 primaryButtonOnly: true,
20424 * The availabe property is false until the linked dom element is accessible.
20425 * @property available
20431 * By default, drags can only be initiated if the mousedown occurs in the
20432 * region the linked element is. This is done in part to work around a
20433 * bug in some browsers that mis-report the mousedown if the previous
20434 * mouseup happened outside of the window. This property is set to true
20435 * if outer handles are defined.
20437 * @property hasOuterHandles
20441 hasOuterHandles: false,
20444 * Code that executes immediately before the startDrag event
20445 * @method b4StartDrag
20448 b4StartDrag: function(x, y) { },
20451 * Abstract method called after a drag/drop object is clicked
20452 * and the drag or mousedown time thresholds have beeen met.
20453 * @method startDrag
20454 * @param {int} X click location
20455 * @param {int} Y click location
20457 startDrag: function(x, y) { /* override this */ },
20460 * Code that executes immediately before the onDrag event
20464 b4Drag: function(e) { },
20467 * Abstract method called during the onMouseMove event while dragging an
20470 * @param {Event} e the mousemove event
20472 onDrag: function(e) { /* override this */ },
20475 * Abstract method called when this element fist begins hovering over
20476 * another DragDrop obj
20477 * @method onDragEnter
20478 * @param {Event} e the mousemove event
20479 * @param {String|DragDrop[]} id In POINT mode, the element
20480 * id this is hovering over. In INTERSECT mode, an array of one or more
20481 * dragdrop items being hovered over.
20483 onDragEnter: function(e, id) { /* override this */ },
20486 * Code that executes immediately before the onDragOver event
20487 * @method b4DragOver
20490 b4DragOver: function(e) { },
20493 * Abstract method called when this element is hovering over another
20495 * @method onDragOver
20496 * @param {Event} e the mousemove event
20497 * @param {String|DragDrop[]} id In POINT mode, the element
20498 * id this is hovering over. In INTERSECT mode, an array of dd items
20499 * being hovered over.
20501 onDragOver: function(e, id) { /* override this */ },
20504 * Code that executes immediately before the onDragOut event
20505 * @method b4DragOut
20508 b4DragOut: function(e) { },
20511 * Abstract method called when we are no longer hovering over an element
20512 * @method onDragOut
20513 * @param {Event} e the mousemove event
20514 * @param {String|DragDrop[]} id In POINT mode, the element
20515 * id this was hovering over. In INTERSECT mode, an array of dd items
20516 * that the mouse is no longer over.
20518 onDragOut: function(e, id) { /* override this */ },
20521 * Code that executes immediately before the onDragDrop event
20522 * @method b4DragDrop
20525 b4DragDrop: function(e) { },
20528 * Abstract method called when this item is dropped on another DragDrop
20530 * @method onDragDrop
20531 * @param {Event} e the mouseup event
20532 * @param {String|DragDrop[]} id In POINT mode, the element
20533 * id this was dropped on. In INTERSECT mode, an array of dd items this
20536 onDragDrop: function(e, id) { /* override this */ },
20539 * Abstract method called when this item is dropped on an area with no
20541 * @method onInvalidDrop
20542 * @param {Event} e the mouseup event
20544 onInvalidDrop: function(e) { /* override this */ },
20547 * Code that executes immediately before the endDrag event
20548 * @method b4EndDrag
20551 b4EndDrag: function(e) { },
20554 * Fired when we are done dragging the object
20556 * @param {Event} e the mouseup event
20558 endDrag: function(e) { /* override this */ },
20561 * Code executed immediately before the onMouseDown event
20562 * @method b4MouseDown
20563 * @param {Event} e the mousedown event
20566 b4MouseDown: function(e) { },
20569 * Event handler that fires when a drag/drop obj gets a mousedown
20570 * @method onMouseDown
20571 * @param {Event} e the mousedown event
20573 onMouseDown: function(e) { /* override this */ },
20576 * Event handler that fires when a drag/drop obj gets a mouseup
20577 * @method onMouseUp
20578 * @param {Event} e the mouseup event
20580 onMouseUp: function(e) { /* override this */ },
20583 * Override the onAvailable method to do what is needed after the initial
20584 * position was determined.
20585 * @method onAvailable
20587 onAvailable: function () {
20591 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20594 defaultPadding : {left:0, right:0, top:0, bottom:0},
20597 * Initializes the drag drop object's constraints to restrict movement to a certain element.
20601 var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20602 { dragElId: "existingProxyDiv" });
20603 dd.startDrag = function(){
20604 this.constrainTo("parent-id");
20607 * Or you can initalize it using the {@link Roo.Element} object:
20609 Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20610 startDrag : function(){
20611 this.constrainTo("parent-id");
20615 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20616 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20617 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20618 * an object containing the sides to pad. For example: {right:10, bottom:10}
20619 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20621 constrainTo : function(constrainTo, pad, inContent){
20622 if(typeof pad == "number"){
20623 pad = {left: pad, right:pad, top:pad, bottom:pad};
20625 pad = pad || this.defaultPadding;
20626 var b = Roo.get(this.getEl()).getBox();
20627 var ce = Roo.get(constrainTo);
20628 var s = ce.getScroll();
20629 var c, cd = ce.dom;
20630 if(cd == document.body){
20631 c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20634 c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20638 var topSpace = b.y - c.y;
20639 var leftSpace = b.x - c.x;
20641 this.resetConstraints();
20642 this.setXConstraint(leftSpace - (pad.left||0), // left
20643 c.width - leftSpace - b.width - (pad.right||0) //right
20645 this.setYConstraint(topSpace - (pad.top||0), //top
20646 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20651 * Returns a reference to the linked element
20653 * @return {HTMLElement} the html element
20655 getEl: function() {
20656 if (!this._domRef) {
20657 this._domRef = Roo.getDom(this.id);
20660 return this._domRef;
20664 * Returns a reference to the actual element to drag. By default this is
20665 * the same as the html element, but it can be assigned to another
20666 * element. An example of this can be found in Roo.dd.DDProxy
20667 * @method getDragEl
20668 * @return {HTMLElement} the html element
20670 getDragEl: function() {
20671 return Roo.getDom(this.dragElId);
20675 * Sets up the DragDrop object. Must be called in the constructor of any
20676 * Roo.dd.DragDrop subclass
20678 * @param id the id of the linked element
20679 * @param {String} sGroup the group of related items
20680 * @param {object} config configuration attributes
20682 init: function(id, sGroup, config) {
20683 this.initTarget(id, sGroup, config);
20684 if (!Roo.isTouch) {
20685 Event.on(this.id, "mousedown", this.handleMouseDown, this);
20687 Event.on(this.id, "touchstart", this.handleMouseDown, this);
20688 // Event.on(this.id, "selectstart", Event.preventDefault);
20692 * Initializes Targeting functionality only... the object does not
20693 * get a mousedown handler.
20694 * @method initTarget
20695 * @param id the id of the linked element
20696 * @param {String} sGroup the group of related items
20697 * @param {object} config configuration attributes
20699 initTarget: function(id, sGroup, config) {
20701 // configuration attributes
20702 this.config = config || {};
20704 // create a local reference to the drag and drop manager
20705 this.DDM = Roo.dd.DDM;
20706 // initialize the groups array
20709 // assume that we have an element reference instead of an id if the
20710 // parameter is not a string
20711 if (typeof id !== "string") {
20718 // add to an interaction group
20719 this.addToGroup((sGroup) ? sGroup : "default");
20721 // We don't want to register this as the handle with the manager
20722 // so we just set the id rather than calling the setter.
20723 this.handleElId = id;
20725 // the linked element is the element that gets dragged by default
20726 this.setDragElId(id);
20728 // by default, clicked anchors will not start drag operations.
20729 this.invalidHandleTypes = { A: "A" };
20730 this.invalidHandleIds = {};
20731 this.invalidHandleClasses = [];
20733 this.applyConfig();
20735 this.handleOnAvailable();
20739 * Applies the configuration parameters that were passed into the constructor.
20740 * This is supposed to happen at each level through the inheritance chain. So
20741 * a DDProxy implentation will execute apply config on DDProxy, DD, and
20742 * DragDrop in order to get all of the parameters that are available in
20744 * @method applyConfig
20746 applyConfig: function() {
20748 // configurable properties:
20749 // padding, isTarget, maintainOffset, primaryButtonOnly
20750 this.padding = this.config.padding || [0, 0, 0, 0];
20751 this.isTarget = (this.config.isTarget !== false);
20752 this.maintainOffset = (this.config.maintainOffset);
20753 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20758 * Executed when the linked element is available
20759 * @method handleOnAvailable
20762 handleOnAvailable: function() {
20763 this.available = true;
20764 this.resetConstraints();
20765 this.onAvailable();
20769 * Configures the padding for the target zone in px. Effectively expands
20770 * (or reduces) the virtual object size for targeting calculations.
20771 * Supports css-style shorthand; if only one parameter is passed, all sides
20772 * will have that padding, and if only two are passed, the top and bottom
20773 * will have the first param, the left and right the second.
20774 * @method setPadding
20775 * @param {int} iTop Top pad
20776 * @param {int} iRight Right pad
20777 * @param {int} iBot Bot pad
20778 * @param {int} iLeft Left pad
20780 setPadding: function(iTop, iRight, iBot, iLeft) {
20781 // this.padding = [iLeft, iRight, iTop, iBot];
20782 if (!iRight && 0 !== iRight) {
20783 this.padding = [iTop, iTop, iTop, iTop];
20784 } else if (!iBot && 0 !== iBot) {
20785 this.padding = [iTop, iRight, iTop, iRight];
20787 this.padding = [iTop, iRight, iBot, iLeft];
20792 * Stores the initial placement of the linked element.
20793 * @method setInitialPosition
20794 * @param {int} diffX the X offset, default 0
20795 * @param {int} diffY the Y offset, default 0
20797 setInitPosition: function(diffX, diffY) {
20798 var el = this.getEl();
20800 if (!this.DDM.verifyEl(el)) {
20804 var dx = diffX || 0;
20805 var dy = diffY || 0;
20807 var p = Dom.getXY( el );
20809 this.initPageX = p[0] - dx;
20810 this.initPageY = p[1] - dy;
20812 this.lastPageX = p[0];
20813 this.lastPageY = p[1];
20816 this.setStartPosition(p);
20820 * Sets the start position of the element. This is set when the obj
20821 * is initialized, the reset when a drag is started.
20822 * @method setStartPosition
20823 * @param pos current position (from previous lookup)
20826 setStartPosition: function(pos) {
20827 var p = pos || Dom.getXY( this.getEl() );
20828 this.deltaSetXY = null;
20830 this.startPageX = p[0];
20831 this.startPageY = p[1];
20835 * Add this instance to a group of related drag/drop objects. All
20836 * instances belong to at least one group, and can belong to as many
20837 * groups as needed.
20838 * @method addToGroup
20839 * @param sGroup {string} the name of the group
20841 addToGroup: function(sGroup) {
20842 this.groups[sGroup] = true;
20843 this.DDM.regDragDrop(this, sGroup);
20847 * Remove's this instance from the supplied interaction group
20848 * @method removeFromGroup
20849 * @param {string} sGroup The group to drop
20851 removeFromGroup: function(sGroup) {
20852 if (this.groups[sGroup]) {
20853 delete this.groups[sGroup];
20856 this.DDM.removeDDFromGroup(this, sGroup);
20860 * Allows you to specify that an element other than the linked element
20861 * will be moved with the cursor during a drag
20862 * @method setDragElId
20863 * @param id {string} the id of the element that will be used to initiate the drag
20865 setDragElId: function(id) {
20866 this.dragElId = id;
20870 * Allows you to specify a child of the linked element that should be
20871 * used to initiate the drag operation. An example of this would be if
20872 * you have a content div with text and links. Clicking anywhere in the
20873 * content area would normally start the drag operation. Use this method
20874 * to specify that an element inside of the content div is the element
20875 * that starts the drag operation.
20876 * @method setHandleElId
20877 * @param id {string} the id of the element that will be used to
20878 * initiate the drag.
20880 setHandleElId: function(id) {
20881 if (typeof id !== "string") {
20884 this.handleElId = id;
20885 this.DDM.regHandle(this.id, id);
20889 * Allows you to set an element outside of the linked element as a drag
20891 * @method setOuterHandleElId
20892 * @param id the id of the element that will be used to initiate the drag
20894 setOuterHandleElId: function(id) {
20895 if (typeof id !== "string") {
20898 Event.on(id, "mousedown",
20899 this.handleMouseDown, this);
20900 this.setHandleElId(id);
20902 this.hasOuterHandles = true;
20906 * Remove all drag and drop hooks for this element
20909 unreg: function() {
20910 Event.un(this.id, "mousedown",
20911 this.handleMouseDown);
20912 Event.un(this.id, "touchstart",
20913 this.handleMouseDown);
20914 this._domRef = null;
20915 this.DDM._remove(this);
20918 destroy : function(){
20923 * Returns true if this instance is locked, or the drag drop mgr is locked
20924 * (meaning that all drag/drop is disabled on the page.)
20926 * @return {boolean} true if this obj or all drag/drop is locked, else
20929 isLocked: function() {
20930 return (this.DDM.isLocked() || this.locked);
20934 * Fired when this object is clicked
20935 * @method handleMouseDown
20937 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20940 handleMouseDown: function(e, oDD){
20942 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20943 //Roo.log('not touch/ button !=0');
20946 if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20947 return; // double touch..
20951 if (this.isLocked()) {
20952 //Roo.log('locked');
20956 this.DDM.refreshCache(this.groups);
20957 // Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20958 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20959 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
20960 //Roo.log('no outer handes or not over target');
20963 // Roo.log('check validator');
20964 if (this.clickValidator(e)) {
20965 // Roo.log('validate success');
20966 // set the initial element position
20967 this.setStartPosition();
20970 this.b4MouseDown(e);
20971 this.onMouseDown(e);
20973 this.DDM.handleMouseDown(e, this);
20975 this.DDM.stopEvent(e);
20983 clickValidator: function(e) {
20984 var target = e.getTarget();
20985 return ( this.isValidHandleChild(target) &&
20986 (this.id == this.handleElId ||
20987 this.DDM.handleWasClicked(target, this.id)) );
20991 * Allows you to specify a tag name that should not start a drag operation
20992 * when clicked. This is designed to facilitate embedding links within a
20993 * drag handle that do something other than start the drag.
20994 * @method addInvalidHandleType
20995 * @param {string} tagName the type of element to exclude
20997 addInvalidHandleType: function(tagName) {
20998 var type = tagName.toUpperCase();
20999 this.invalidHandleTypes[type] = type;
21003 * Lets you to specify an element id for a child of a drag handle
21004 * that should not initiate a drag
21005 * @method addInvalidHandleId
21006 * @param {string} id the element id of the element you wish to ignore
21008 addInvalidHandleId: function(id) {
21009 if (typeof id !== "string") {
21012 this.invalidHandleIds[id] = id;
21016 * Lets you specify a css class of elements that will not initiate a drag
21017 * @method addInvalidHandleClass
21018 * @param {string} cssClass the class of the elements you wish to ignore
21020 addInvalidHandleClass: function(cssClass) {
21021 this.invalidHandleClasses.push(cssClass);
21025 * Unsets an excluded tag name set by addInvalidHandleType
21026 * @method removeInvalidHandleType
21027 * @param {string} tagName the type of element to unexclude
21029 removeInvalidHandleType: function(tagName) {
21030 var type = tagName.toUpperCase();
21031 // this.invalidHandleTypes[type] = null;
21032 delete this.invalidHandleTypes[type];
21036 * Unsets an invalid handle id
21037 * @method removeInvalidHandleId
21038 * @param {string} id the id of the element to re-enable
21040 removeInvalidHandleId: function(id) {
21041 if (typeof id !== "string") {
21044 delete this.invalidHandleIds[id];
21048 * Unsets an invalid css class
21049 * @method removeInvalidHandleClass
21050 * @param {string} cssClass the class of the element(s) you wish to
21053 removeInvalidHandleClass: function(cssClass) {
21054 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21055 if (this.invalidHandleClasses[i] == cssClass) {
21056 delete this.invalidHandleClasses[i];
21062 * Checks the tag exclusion list to see if this click should be ignored
21063 * @method isValidHandleChild
21064 * @param {HTMLElement} node the HTMLElement to evaluate
21065 * @return {boolean} true if this is a valid tag type, false if not
21067 isValidHandleChild: function(node) {
21070 // var n = (node.nodeName == "#text") ? node.parentNode : node;
21073 nodeName = node.nodeName.toUpperCase();
21075 nodeName = node.nodeName;
21077 valid = valid && !this.invalidHandleTypes[nodeName];
21078 valid = valid && !this.invalidHandleIds[node.id];
21080 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21081 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21090 * Create the array of horizontal tick marks if an interval was specified
21091 * in setXConstraint().
21092 * @method setXTicks
21095 setXTicks: function(iStartX, iTickSize) {
21097 this.xTickSize = iTickSize;
21101 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21103 this.xTicks[this.xTicks.length] = i;
21108 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21110 this.xTicks[this.xTicks.length] = i;
21115 this.xTicks.sort(this.DDM.numericSort) ;
21119 * Create the array of vertical tick marks if an interval was specified in
21120 * setYConstraint().
21121 * @method setYTicks
21124 setYTicks: function(iStartY, iTickSize) {
21126 this.yTickSize = iTickSize;
21130 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21132 this.yTicks[this.yTicks.length] = i;
21137 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21139 this.yTicks[this.yTicks.length] = i;
21144 this.yTicks.sort(this.DDM.numericSort) ;
21148 * By default, the element can be dragged any place on the screen. Use
21149 * this method to limit the horizontal travel of the element. Pass in
21150 * 0,0 for the parameters if you want to lock the drag to the y axis.
21151 * @method setXConstraint
21152 * @param {int} iLeft the number of pixels the element can move to the left
21153 * @param {int} iRight the number of pixels the element can move to the
21155 * @param {int} iTickSize optional parameter for specifying that the
21157 * should move iTickSize pixels at a time.
21159 setXConstraint: function(iLeft, iRight, iTickSize) {
21160 this.leftConstraint = iLeft;
21161 this.rightConstraint = iRight;
21163 this.minX = this.initPageX - iLeft;
21164 this.maxX = this.initPageX + iRight;
21165 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21167 this.constrainX = true;
21171 * Clears any constraints applied to this instance. Also clears ticks
21172 * since they can't exist independent of a constraint at this time.
21173 * @method clearConstraints
21175 clearConstraints: function() {
21176 this.constrainX = false;
21177 this.constrainY = false;
21182 * Clears any tick interval defined for this instance
21183 * @method clearTicks
21185 clearTicks: function() {
21186 this.xTicks = null;
21187 this.yTicks = null;
21188 this.xTickSize = 0;
21189 this.yTickSize = 0;
21193 * By default, the element can be dragged any place on the screen. Set
21194 * this to limit the vertical travel of the element. Pass in 0,0 for the
21195 * parameters if you want to lock the drag to the x axis.
21196 * @method setYConstraint
21197 * @param {int} iUp the number of pixels the element can move up
21198 * @param {int} iDown the number of pixels the element can move down
21199 * @param {int} iTickSize optional parameter for specifying that the
21200 * element should move iTickSize pixels at a time.
21202 setYConstraint: function(iUp, iDown, iTickSize) {
21203 this.topConstraint = iUp;
21204 this.bottomConstraint = iDown;
21206 this.minY = this.initPageY - iUp;
21207 this.maxY = this.initPageY + iDown;
21208 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21210 this.constrainY = true;
21215 * resetConstraints must be called if you manually reposition a dd element.
21216 * @method resetConstraints
21217 * @param {boolean} maintainOffset
21219 resetConstraints: function() {
21222 // Maintain offsets if necessary
21223 if (this.initPageX || this.initPageX === 0) {
21224 // figure out how much this thing has moved
21225 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21226 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21228 this.setInitPosition(dx, dy);
21230 // This is the first time we have detected the element's position
21232 this.setInitPosition();
21235 if (this.constrainX) {
21236 this.setXConstraint( this.leftConstraint,
21237 this.rightConstraint,
21241 if (this.constrainY) {
21242 this.setYConstraint( this.topConstraint,
21243 this.bottomConstraint,
21249 * Normally the drag element is moved pixel by pixel, but we can specify
21250 * that it move a number of pixels at a time. This method resolves the
21251 * location when we have it set up like this.
21253 * @param {int} val where we want to place the object
21254 * @param {int[]} tickArray sorted array of valid points
21255 * @return {int} the closest tick
21258 getTick: function(val, tickArray) {
21261 // If tick interval is not defined, it is effectively 1 pixel,
21262 // so we return the value passed to us.
21264 } else if (tickArray[0] >= val) {
21265 // The value is lower than the first tick, so we return the first
21267 return tickArray[0];
21269 for (var i=0, len=tickArray.length; i<len; ++i) {
21271 if (tickArray[next] && tickArray[next] >= val) {
21272 var diff1 = val - tickArray[i];
21273 var diff2 = tickArray[next] - val;
21274 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21278 // The value is larger than the last tick, so we return the last
21280 return tickArray[tickArray.length - 1];
21287 * @return {string} string representation of the dd obj
21289 toString: function() {
21290 return ("DragDrop " + this.id);
21298 * Ext JS Library 1.1.1
21299 * Copyright(c) 2006-2007, Ext JS, LLC.
21301 * Originally Released Under LGPL - original licence link has changed is not relivant.
21304 * <script type="text/javascript">
21309 * The drag and drop utility provides a framework for building drag and drop
21310 * applications. In addition to enabling drag and drop for specific elements,
21311 * the drag and drop elements are tracked by the manager class, and the
21312 * interactions between the various elements are tracked during the drag and
21313 * the implementing code is notified about these important moments.
21316 // Only load the library once. Rewriting the manager class would orphan
21317 // existing drag and drop instances.
21318 if (!Roo.dd.DragDropMgr) {
21321 * @class Roo.dd.DragDropMgr
21322 * DragDropMgr is a singleton that tracks the element interaction for
21323 * all DragDrop items in the window. Generally, you will not call
21324 * this class directly, but it does have helper methods that could
21325 * be useful in your DragDrop implementations.
21328 Roo.dd.DragDropMgr = function() {
21330 var Event = Roo.EventManager;
21335 * Two dimensional Array of registered DragDrop objects. The first
21336 * dimension is the DragDrop item group, the second the DragDrop
21339 * @type {string: string}
21346 * Array of element ids defined as drag handles. Used to determine
21347 * if the element that generated the mousedown event is actually the
21348 * handle and not the html element itself.
21349 * @property handleIds
21350 * @type {string: string}
21357 * the DragDrop object that is currently being dragged
21358 * @property dragCurrent
21366 * the DragDrop object(s) that are being hovered over
21367 * @property dragOvers
21375 * the X distance between the cursor and the object being dragged
21384 * the Y distance between the cursor and the object being dragged
21393 * Flag to determine if we should prevent the default behavior of the
21394 * events we define. By default this is true, but this can be set to
21395 * false if you need the default behavior (not recommended)
21396 * @property preventDefault
21400 preventDefault: true,
21403 * Flag to determine if we should stop the propagation of the events
21404 * we generate. This is true by default but you may want to set it to
21405 * false if the html element contains other features that require the
21407 * @property stopPropagation
21411 stopPropagation: true,
21414 * Internal flag that is set to true when drag and drop has been
21416 * @property initialized
21423 * All drag and drop can be disabled.
21431 * Called the first time an element is registered.
21437 this.initialized = true;
21441 * In point mode, drag and drop interaction is defined by the
21442 * location of the cursor during the drag/drop
21450 * In intersect mode, drag and drop interactio nis defined by the
21451 * overlap of two or more drag and drop objects.
21452 * @property INTERSECT
21459 * The current drag and drop mode. Default: POINT
21467 * Runs method on all drag and drop objects
21468 * @method _execOnAll
21472 _execOnAll: function(sMethod, args) {
21473 for (var i in this.ids) {
21474 for (var j in this.ids[i]) {
21475 var oDD = this.ids[i][j];
21476 if (! this.isTypeOfDD(oDD)) {
21479 oDD[sMethod].apply(oDD, args);
21485 * Drag and drop initialization. Sets up the global event handlers
21490 _onLoad: function() {
21494 if (!Roo.isTouch) {
21495 Event.on(document, "mouseup", this.handleMouseUp, this, true);
21496 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21498 Event.on(document, "touchend", this.handleMouseUp, this, true);
21499 Event.on(document, "touchmove", this.handleMouseMove, this, true);
21501 Event.on(window, "unload", this._onUnload, this, true);
21502 Event.on(window, "resize", this._onResize, this, true);
21503 // Event.on(window, "mouseout", this._test);
21508 * Reset constraints on all drag and drop objs
21509 * @method _onResize
21513 _onResize: function(e) {
21514 this._execOnAll("resetConstraints", []);
21518 * Lock all drag and drop functionality
21522 lock: function() { this.locked = true; },
21525 * Unlock all drag and drop functionality
21529 unlock: function() { this.locked = false; },
21532 * Is drag and drop locked?
21534 * @return {boolean} True if drag and drop is locked, false otherwise.
21537 isLocked: function() { return this.locked; },
21540 * Location cache that is set for all drag drop objects when a drag is
21541 * initiated, cleared when the drag is finished.
21542 * @property locationCache
21549 * Set useCache to false if you want to force object the lookup of each
21550 * drag and drop linked element constantly during a drag.
21551 * @property useCache
21558 * The number of pixels that the mouse needs to move after the
21559 * mousedown before the drag is initiated. Default=3;
21560 * @property clickPixelThresh
21564 clickPixelThresh: 3,
21567 * The number of milliseconds after the mousedown event to initiate the
21568 * drag if we don't get a mouseup event. Default=1000
21569 * @property clickTimeThresh
21573 clickTimeThresh: 350,
21576 * Flag that indicates that either the drag pixel threshold or the
21577 * mousdown time threshold has been met
21578 * @property dragThreshMet
21583 dragThreshMet: false,
21586 * Timeout used for the click time threshold
21587 * @property clickTimeout
21592 clickTimeout: null,
21595 * The X position of the mousedown event stored for later use when a
21596 * drag threshold is met.
21605 * The Y position of the mousedown event stored for later use when a
21606 * drag threshold is met.
21615 * Each DragDrop instance must be registered with the DragDropMgr.
21616 * This is executed in DragDrop.init()
21617 * @method regDragDrop
21618 * @param {DragDrop} oDD the DragDrop object to register
21619 * @param {String} sGroup the name of the group this element belongs to
21622 regDragDrop: function(oDD, sGroup) {
21623 if (!this.initialized) { this.init(); }
21625 if (!this.ids[sGroup]) {
21626 this.ids[sGroup] = {};
21628 this.ids[sGroup][oDD.id] = oDD;
21632 * Removes the supplied dd instance from the supplied group. Executed
21633 * by DragDrop.removeFromGroup, so don't call this function directly.
21634 * @method removeDDFromGroup
21638 removeDDFromGroup: function(oDD, sGroup) {
21639 if (!this.ids[sGroup]) {
21640 this.ids[sGroup] = {};
21643 var obj = this.ids[sGroup];
21644 if (obj && obj[oDD.id]) {
21645 delete obj[oDD.id];
21650 * Unregisters a drag and drop item. This is executed in
21651 * DragDrop.unreg, use that method instead of calling this directly.
21656 _remove: function(oDD) {
21657 for (var g in oDD.groups) {
21658 if (g && this.ids[g][oDD.id]) {
21659 delete this.ids[g][oDD.id];
21662 delete this.handleIds[oDD.id];
21666 * Each DragDrop handle element must be registered. This is done
21667 * automatically when executing DragDrop.setHandleElId()
21668 * @method regHandle
21669 * @param {String} sDDId the DragDrop id this element is a handle for
21670 * @param {String} sHandleId the id of the element that is the drag
21674 regHandle: function(sDDId, sHandleId) {
21675 if (!this.handleIds[sDDId]) {
21676 this.handleIds[sDDId] = {};
21678 this.handleIds[sDDId][sHandleId] = sHandleId;
21682 * Utility function to determine if a given element has been
21683 * registered as a drag drop item.
21684 * @method isDragDrop
21685 * @param {String} id the element id to check
21686 * @return {boolean} true if this element is a DragDrop item,
21690 isDragDrop: function(id) {
21691 return ( this.getDDById(id) ) ? true : false;
21695 * Returns the drag and drop instances that are in all groups the
21696 * passed in instance belongs to.
21697 * @method getRelated
21698 * @param {DragDrop} p_oDD the obj to get related data for
21699 * @param {boolean} bTargetsOnly if true, only return targetable objs
21700 * @return {DragDrop[]} the related instances
21703 getRelated: function(p_oDD, bTargetsOnly) {
21705 for (var i in p_oDD.groups) {
21706 for (j in this.ids[i]) {
21707 var dd = this.ids[i][j];
21708 if (! this.isTypeOfDD(dd)) {
21711 if (!bTargetsOnly || dd.isTarget) {
21712 oDDs[oDDs.length] = dd;
21721 * Returns true if the specified dd target is a legal target for
21722 * the specifice drag obj
21723 * @method isLegalTarget
21724 * @param {DragDrop} the drag obj
21725 * @param {DragDrop} the target
21726 * @return {boolean} true if the target is a legal target for the
21730 isLegalTarget: function (oDD, oTargetDD) {
21731 var targets = this.getRelated(oDD, true);
21732 for (var i=0, len=targets.length;i<len;++i) {
21733 if (targets[i].id == oTargetDD.id) {
21742 * My goal is to be able to transparently determine if an object is
21743 * typeof DragDrop, and the exact subclass of DragDrop. typeof
21744 * returns "object", oDD.constructor.toString() always returns
21745 * "DragDrop" and not the name of the subclass. So for now it just
21746 * evaluates a well-known variable in DragDrop.
21747 * @method isTypeOfDD
21748 * @param {Object} the object to evaluate
21749 * @return {boolean} true if typeof oDD = DragDrop
21752 isTypeOfDD: function (oDD) {
21753 return (oDD && oDD.__ygDragDrop);
21757 * Utility function to determine if a given element has been
21758 * registered as a drag drop handle for the given Drag Drop object.
21760 * @param {String} id the element id to check
21761 * @return {boolean} true if this element is a DragDrop handle, false
21765 isHandle: function(sDDId, sHandleId) {
21766 return ( this.handleIds[sDDId] &&
21767 this.handleIds[sDDId][sHandleId] );
21771 * Returns the DragDrop instance for a given id
21772 * @method getDDById
21773 * @param {String} id the id of the DragDrop object
21774 * @return {DragDrop} the drag drop object, null if it is not found
21777 getDDById: function(id) {
21778 for (var i in this.ids) {
21779 if (this.ids[i][id]) {
21780 return this.ids[i][id];
21787 * Fired after a registered DragDrop object gets the mousedown event.
21788 * Sets up the events required to track the object being dragged
21789 * @method handleMouseDown
21790 * @param {Event} e the event
21791 * @param oDD the DragDrop object being dragged
21795 handleMouseDown: function(e, oDD) {
21797 Roo.QuickTips.disable();
21799 this.currentTarget = e.getTarget();
21801 this.dragCurrent = oDD;
21803 var el = oDD.getEl();
21805 // track start position
21806 this.startX = e.getPageX();
21807 this.startY = e.getPageY();
21809 this.deltaX = this.startX - el.offsetLeft;
21810 this.deltaY = this.startY - el.offsetTop;
21812 this.dragThreshMet = false;
21814 this.clickTimeout = setTimeout(
21816 var DDM = Roo.dd.DDM;
21817 DDM.startDrag(DDM.startX, DDM.startY);
21819 this.clickTimeThresh );
21823 * Fired when either the drag pixel threshol or the mousedown hold
21824 * time threshold has been met.
21825 * @method startDrag
21826 * @param x {int} the X position of the original mousedown
21827 * @param y {int} the Y position of the original mousedown
21830 startDrag: function(x, y) {
21831 clearTimeout(this.clickTimeout);
21832 if (this.dragCurrent) {
21833 this.dragCurrent.b4StartDrag(x, y);
21834 this.dragCurrent.startDrag(x, y);
21836 this.dragThreshMet = true;
21840 * Internal function to handle the mouseup event. Will be invoked
21841 * from the context of the document.
21842 * @method handleMouseUp
21843 * @param {Event} e the event
21847 handleMouseUp: function(e) {
21850 Roo.QuickTips.enable();
21852 if (! this.dragCurrent) {
21856 clearTimeout(this.clickTimeout);
21858 if (this.dragThreshMet) {
21859 this.fireEvents(e, true);
21869 * Utility to stop event propagation and event default, if these
21870 * features are turned on.
21871 * @method stopEvent
21872 * @param {Event} e the event as returned by this.getEvent()
21875 stopEvent: function(e){
21876 if(this.stopPropagation) {
21877 e.stopPropagation();
21880 if (this.preventDefault) {
21881 e.preventDefault();
21886 * Internal function to clean up event handlers after the drag
21887 * operation is complete
21889 * @param {Event} e the event
21893 stopDrag: function(e) {
21894 // Fire the drag end event for the item that was dragged
21895 if (this.dragCurrent) {
21896 if (this.dragThreshMet) {
21897 this.dragCurrent.b4EndDrag(e);
21898 this.dragCurrent.endDrag(e);
21901 this.dragCurrent.onMouseUp(e);
21904 this.dragCurrent = null;
21905 this.dragOvers = {};
21909 * Internal function to handle the mousemove event. Will be invoked
21910 * from the context of the html element.
21912 * @TODO figure out what we can do about mouse events lost when the
21913 * user drags objects beyond the window boundary. Currently we can
21914 * detect this in internet explorer by verifying that the mouse is
21915 * down during the mousemove event. Firefox doesn't give us the
21916 * button state on the mousemove event.
21917 * @method handleMouseMove
21918 * @param {Event} e the event
21922 handleMouseMove: function(e) {
21923 if (! this.dragCurrent) {
21927 // var button = e.which || e.button;
21929 // check for IE mouseup outside of page boundary
21930 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21932 return this.handleMouseUp(e);
21935 if (!this.dragThreshMet) {
21936 var diffX = Math.abs(this.startX - e.getPageX());
21937 var diffY = Math.abs(this.startY - e.getPageY());
21938 if (diffX > this.clickPixelThresh ||
21939 diffY > this.clickPixelThresh) {
21940 this.startDrag(this.startX, this.startY);
21944 if (this.dragThreshMet) {
21945 this.dragCurrent.b4Drag(e);
21946 this.dragCurrent.onDrag(e);
21947 if(!this.dragCurrent.moveOnly){
21948 this.fireEvents(e, false);
21958 * Iterates over all of the DragDrop elements to find ones we are
21959 * hovering over or dropping on
21960 * @method fireEvents
21961 * @param {Event} e the event
21962 * @param {boolean} isDrop is this a drop op or a mouseover op?
21966 fireEvents: function(e, isDrop) {
21967 var dc = this.dragCurrent;
21969 // If the user did the mouse up outside of the window, we could
21970 // get here even though we have ended the drag.
21971 if (!dc || dc.isLocked()) {
21975 var pt = e.getPoint();
21977 // cache the previous dragOver array
21983 var enterEvts = [];
21985 // Check to see if the object(s) we were hovering over is no longer
21986 // being hovered over so we can fire the onDragOut event
21987 for (var i in this.dragOvers) {
21989 var ddo = this.dragOvers[i];
21991 if (! this.isTypeOfDD(ddo)) {
21995 if (! this.isOverTarget(pt, ddo, this.mode)) {
21996 outEvts.push( ddo );
21999 oldOvers[i] = true;
22000 delete this.dragOvers[i];
22003 for (var sGroup in dc.groups) {
22005 if ("string" != typeof sGroup) {
22009 for (i in this.ids[sGroup]) {
22010 var oDD = this.ids[sGroup][i];
22011 if (! this.isTypeOfDD(oDD)) {
22015 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22016 if (this.isOverTarget(pt, oDD, this.mode)) {
22017 // look for drop interactions
22019 dropEvts.push( oDD );
22020 // look for drag enter and drag over interactions
22023 // initial drag over: dragEnter fires
22024 if (!oldOvers[oDD.id]) {
22025 enterEvts.push( oDD );
22026 // subsequent drag overs: dragOver fires
22028 overEvts.push( oDD );
22031 this.dragOvers[oDD.id] = oDD;
22039 if (outEvts.length) {
22040 dc.b4DragOut(e, outEvts);
22041 dc.onDragOut(e, outEvts);
22044 if (enterEvts.length) {
22045 dc.onDragEnter(e, enterEvts);
22048 if (overEvts.length) {
22049 dc.b4DragOver(e, overEvts);
22050 dc.onDragOver(e, overEvts);
22053 if (dropEvts.length) {
22054 dc.b4DragDrop(e, dropEvts);
22055 dc.onDragDrop(e, dropEvts);
22059 // fire dragout events
22061 for (i=0, len=outEvts.length; i<len; ++i) {
22062 dc.b4DragOut(e, outEvts[i].id);
22063 dc.onDragOut(e, outEvts[i].id);
22066 // fire enter events
22067 for (i=0,len=enterEvts.length; i<len; ++i) {
22068 // dc.b4DragEnter(e, oDD.id);
22069 dc.onDragEnter(e, enterEvts[i].id);
22072 // fire over events
22073 for (i=0,len=overEvts.length; i<len; ++i) {
22074 dc.b4DragOver(e, overEvts[i].id);
22075 dc.onDragOver(e, overEvts[i].id);
22078 // fire drop events
22079 for (i=0, len=dropEvts.length; i<len; ++i) {
22080 dc.b4DragDrop(e, dropEvts[i].id);
22081 dc.onDragDrop(e, dropEvts[i].id);
22086 // notify about a drop that did not find a target
22087 if (isDrop && !dropEvts.length) {
22088 dc.onInvalidDrop(e);
22094 * Helper function for getting the best match from the list of drag
22095 * and drop objects returned by the drag and drop events when we are
22096 * in INTERSECT mode. It returns either the first object that the
22097 * cursor is over, or the object that has the greatest overlap with
22098 * the dragged element.
22099 * @method getBestMatch
22100 * @param {DragDrop[]} dds The array of drag and drop objects
22102 * @return {DragDrop} The best single match
22105 getBestMatch: function(dds) {
22107 // Return null if the input is not what we expect
22108 //if (!dds || !dds.length || dds.length == 0) {
22110 // If there is only one item, it wins
22111 //} else if (dds.length == 1) {
22113 var len = dds.length;
22118 // Loop through the targeted items
22119 for (var i=0; i<len; ++i) {
22121 // If the cursor is over the object, it wins. If the
22122 // cursor is over multiple matches, the first one we come
22124 if (dd.cursorIsOver) {
22127 // Otherwise the object with the most overlap wins
22130 winner.overlap.getArea() < dd.overlap.getArea()) {
22141 * Refreshes the cache of the top-left and bottom-right points of the
22142 * drag and drop objects in the specified group(s). This is in the
22143 * format that is stored in the drag and drop instance, so typical
22146 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22150 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22152 * @TODO this really should be an indexed array. Alternatively this
22153 * method could accept both.
22154 * @method refreshCache
22155 * @param {Object} groups an associative array of groups to refresh
22158 refreshCache: function(groups) {
22159 for (var sGroup in groups) {
22160 if ("string" != typeof sGroup) {
22163 for (var i in this.ids[sGroup]) {
22164 var oDD = this.ids[sGroup][i];
22166 if (this.isTypeOfDD(oDD)) {
22167 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22168 var loc = this.getLocation(oDD);
22170 this.locationCache[oDD.id] = loc;
22172 delete this.locationCache[oDD.id];
22173 // this will unregister the drag and drop object if
22174 // the element is not in a usable state
22183 * This checks to make sure an element exists and is in the DOM. The
22184 * main purpose is to handle cases where innerHTML is used to remove
22185 * drag and drop objects from the DOM. IE provides an 'unspecified
22186 * error' when trying to access the offsetParent of such an element
22188 * @param {HTMLElement} el the element to check
22189 * @return {boolean} true if the element looks usable
22192 verifyEl: function(el) {
22197 parent = el.offsetParent;
22200 parent = el.offsetParent;
22211 * Returns a Region object containing the drag and drop element's position
22212 * and size, including the padding configured for it
22213 * @method getLocation
22214 * @param {DragDrop} oDD the drag and drop object to get the
22216 * @return {Roo.lib.Region} a Region object representing the total area
22217 * the element occupies, including any padding
22218 * the instance is configured for.
22221 getLocation: function(oDD) {
22222 if (! this.isTypeOfDD(oDD)) {
22226 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22229 pos= Roo.lib.Dom.getXY(el);
22237 x2 = x1 + el.offsetWidth;
22239 y2 = y1 + el.offsetHeight;
22241 t = y1 - oDD.padding[0];
22242 r = x2 + oDD.padding[1];
22243 b = y2 + oDD.padding[2];
22244 l = x1 - oDD.padding[3];
22246 return new Roo.lib.Region( t, r, b, l );
22250 * Checks the cursor location to see if it over the target
22251 * @method isOverTarget
22252 * @param {Roo.lib.Point} pt The point to evaluate
22253 * @param {DragDrop} oTarget the DragDrop object we are inspecting
22254 * @return {boolean} true if the mouse is over the target
22258 isOverTarget: function(pt, oTarget, intersect) {
22259 // use cache if available
22260 var loc = this.locationCache[oTarget.id];
22261 if (!loc || !this.useCache) {
22262 loc = this.getLocation(oTarget);
22263 this.locationCache[oTarget.id] = loc;
22271 oTarget.cursorIsOver = loc.contains( pt );
22273 // DragDrop is using this as a sanity check for the initial mousedown
22274 // in this case we are done. In POINT mode, if the drag obj has no
22275 // contraints, we are also done. Otherwise we need to evaluate the
22276 // location of the target as related to the actual location of the
22277 // dragged element.
22278 var dc = this.dragCurrent;
22279 if (!dc || !dc.getTargetCoord ||
22280 (!intersect && !dc.constrainX && !dc.constrainY)) {
22281 return oTarget.cursorIsOver;
22284 oTarget.overlap = null;
22286 // Get the current location of the drag element, this is the
22287 // location of the mouse event less the delta that represents
22288 // where the original mousedown happened on the element. We
22289 // need to consider constraints and ticks as well.
22290 var pos = dc.getTargetCoord(pt.x, pt.y);
22292 var el = dc.getDragEl();
22293 var curRegion = new Roo.lib.Region( pos.y,
22294 pos.x + el.offsetWidth,
22295 pos.y + el.offsetHeight,
22298 var overlap = curRegion.intersect(loc);
22301 oTarget.overlap = overlap;
22302 return (intersect) ? true : oTarget.cursorIsOver;
22309 * unload event handler
22310 * @method _onUnload
22314 _onUnload: function(e, me) {
22315 Roo.dd.DragDropMgr.unregAll();
22319 * Cleans up the drag and drop events and objects.
22324 unregAll: function() {
22326 if (this.dragCurrent) {
22328 this.dragCurrent = null;
22331 this._execOnAll("unreg", []);
22333 for (i in this.elementCache) {
22334 delete this.elementCache[i];
22337 this.elementCache = {};
22342 * A cache of DOM elements
22343 * @property elementCache
22350 * Get the wrapper for the DOM element specified
22351 * @method getElWrapper
22352 * @param {String} id the id of the element to get
22353 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22355 * @deprecated This wrapper isn't that useful
22358 getElWrapper: function(id) {
22359 var oWrapper = this.elementCache[id];
22360 if (!oWrapper || !oWrapper.el) {
22361 oWrapper = this.elementCache[id] =
22362 new this.ElementWrapper(Roo.getDom(id));
22368 * Returns the actual DOM element
22369 * @method getElement
22370 * @param {String} id the id of the elment to get
22371 * @return {Object} The element
22372 * @deprecated use Roo.getDom instead
22375 getElement: function(id) {
22376 return Roo.getDom(id);
22380 * Returns the style property for the DOM element (i.e.,
22381 * document.getElById(id).style)
22383 * @param {String} id the id of the elment to get
22384 * @return {Object} The style property of the element
22385 * @deprecated use Roo.getDom instead
22388 getCss: function(id) {
22389 var el = Roo.getDom(id);
22390 return (el) ? el.style : null;
22394 * Inner class for cached elements
22395 * @class DragDropMgr.ElementWrapper
22400 ElementWrapper: function(el) {
22405 this.el = el || null;
22410 this.id = this.el && el.id;
22412 * A reference to the style property
22415 this.css = this.el && el.style;
22419 * Returns the X position of an html element
22421 * @param el the element for which to get the position
22422 * @return {int} the X coordinate
22424 * @deprecated use Roo.lib.Dom.getX instead
22427 getPosX: function(el) {
22428 return Roo.lib.Dom.getX(el);
22432 * Returns the Y position of an html element
22434 * @param el the element for which to get the position
22435 * @return {int} the Y coordinate
22436 * @deprecated use Roo.lib.Dom.getY instead
22439 getPosY: function(el) {
22440 return Roo.lib.Dom.getY(el);
22444 * Swap two nodes. In IE, we use the native method, for others we
22445 * emulate the IE behavior
22447 * @param n1 the first node to swap
22448 * @param n2 the other node to swap
22451 swapNode: function(n1, n2) {
22455 var p = n2.parentNode;
22456 var s = n2.nextSibling;
22459 p.insertBefore(n1, n2);
22460 } else if (n2 == n1.nextSibling) {
22461 p.insertBefore(n2, n1);
22463 n1.parentNode.replaceChild(n2, n1);
22464 p.insertBefore(n1, s);
22470 * Returns the current scroll position
22471 * @method getScroll
22475 getScroll: function () {
22476 var t, l, dde=document.documentElement, db=document.body;
22477 if (dde && (dde.scrollTop || dde.scrollLeft)) {
22479 l = dde.scrollLeft;
22486 return { top: t, left: l };
22490 * Returns the specified element style property
22492 * @param {HTMLElement} el the element
22493 * @param {string} styleProp the style property
22494 * @return {string} The value of the style property
22495 * @deprecated use Roo.lib.Dom.getStyle
22498 getStyle: function(el, styleProp) {
22499 return Roo.fly(el).getStyle(styleProp);
22503 * Gets the scrollTop
22504 * @method getScrollTop
22505 * @return {int} the document's scrollTop
22508 getScrollTop: function () { return this.getScroll().top; },
22511 * Gets the scrollLeft
22512 * @method getScrollLeft
22513 * @return {int} the document's scrollTop
22516 getScrollLeft: function () { return this.getScroll().left; },
22519 * Sets the x/y position of an element to the location of the
22522 * @param {HTMLElement} moveEl The element to move
22523 * @param {HTMLElement} targetEl The position reference element
22526 moveToEl: function (moveEl, targetEl) {
22527 var aCoord = Roo.lib.Dom.getXY(targetEl);
22528 Roo.lib.Dom.setXY(moveEl, aCoord);
22532 * Numeric array sort function
22533 * @method numericSort
22536 numericSort: function(a, b) { return (a - b); },
22540 * @property _timeoutCount
22547 * Trying to make the load order less important. Without this we get
22548 * an error if this file is loaded before the Event Utility.
22549 * @method _addListeners
22553 _addListeners: function() {
22554 var DDM = Roo.dd.DDM;
22555 if ( Roo.lib.Event && document ) {
22558 if (DDM._timeoutCount > 2000) {
22560 setTimeout(DDM._addListeners, 10);
22561 if (document && document.body) {
22562 DDM._timeoutCount += 1;
22569 * Recursively searches the immediate parent and all child nodes for
22570 * the handle element in order to determine wheter or not it was
22572 * @method handleWasClicked
22573 * @param node the html element to inspect
22576 handleWasClicked: function(node, id) {
22577 if (this.isHandle(id, node.id)) {
22580 // check to see if this is a text node child of the one we want
22581 var p = node.parentNode;
22584 if (this.isHandle(id, p.id)) {
22599 // shorter alias, save a few bytes
22600 Roo.dd.DDM = Roo.dd.DragDropMgr;
22601 Roo.dd.DDM._addListeners();
22605 * Ext JS Library 1.1.1
22606 * Copyright(c) 2006-2007, Ext JS, LLC.
22608 * Originally Released Under LGPL - original licence link has changed is not relivant.
22611 * <script type="text/javascript">
22616 * A DragDrop implementation where the linked element follows the
22617 * mouse cursor during a drag.
22618 * @extends Roo.dd.DragDrop
22620 * @param {String} id the id of the linked element
22621 * @param {String} sGroup the group of related DragDrop items
22622 * @param {object} config an object containing configurable attributes
22623 * Valid properties for DD:
22626 Roo.dd.DD = function(id, sGroup, config) {
22628 this.init(id, sGroup, config);
22632 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22635 * When set to true, the utility automatically tries to scroll the browser
22636 * window wehn a drag and drop element is dragged near the viewport boundary.
22637 * Defaults to true.
22644 * Sets the pointer offset to the distance between the linked element's top
22645 * left corner and the location the element was clicked
22646 * @method autoOffset
22647 * @param {int} iPageX the X coordinate of the click
22648 * @param {int} iPageY the Y coordinate of the click
22650 autoOffset: function(iPageX, iPageY) {
22651 var x = iPageX - this.startPageX;
22652 var y = iPageY - this.startPageY;
22653 this.setDelta(x, y);
22657 * Sets the pointer offset. You can call this directly to force the
22658 * offset to be in a particular location (e.g., pass in 0,0 to set it
22659 * to the center of the object)
22661 * @param {int} iDeltaX the distance from the left
22662 * @param {int} iDeltaY the distance from the top
22664 setDelta: function(iDeltaX, iDeltaY) {
22665 this.deltaX = iDeltaX;
22666 this.deltaY = iDeltaY;
22670 * Sets the drag element to the location of the mousedown or click event,
22671 * maintaining the cursor location relative to the location on the element
22672 * that was clicked. Override this if you want to place the element in a
22673 * location other than where the cursor is.
22674 * @method setDragElPos
22675 * @param {int} iPageX the X coordinate of the mousedown or drag event
22676 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22678 setDragElPos: function(iPageX, iPageY) {
22679 // the first time we do this, we are going to check to make sure
22680 // the element has css positioning
22682 var el = this.getDragEl();
22683 this.alignElWithMouse(el, iPageX, iPageY);
22687 * Sets the element to the location of the mousedown or click event,
22688 * maintaining the cursor location relative to the location on the element
22689 * that was clicked. Override this if you want to place the element in a
22690 * location other than where the cursor is.
22691 * @method alignElWithMouse
22692 * @param {HTMLElement} el the element to move
22693 * @param {int} iPageX the X coordinate of the mousedown or drag event
22694 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22696 alignElWithMouse: function(el, iPageX, iPageY) {
22697 var oCoord = this.getTargetCoord(iPageX, iPageY);
22698 var fly = el.dom ? el : Roo.fly(el);
22699 if (!this.deltaSetXY) {
22700 var aCoord = [oCoord.x, oCoord.y];
22702 var newLeft = fly.getLeft(true);
22703 var newTop = fly.getTop(true);
22704 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22706 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22709 this.cachePosition(oCoord.x, oCoord.y);
22710 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22715 * Saves the most recent position so that we can reset the constraints and
22716 * tick marks on-demand. We need to know this so that we can calculate the
22717 * number of pixels the element is offset from its original position.
22718 * @method cachePosition
22719 * @param iPageX the current x position (optional, this just makes it so we
22720 * don't have to look it up again)
22721 * @param iPageY the current y position (optional, this just makes it so we
22722 * don't have to look it up again)
22724 cachePosition: function(iPageX, iPageY) {
22726 this.lastPageX = iPageX;
22727 this.lastPageY = iPageY;
22729 var aCoord = Roo.lib.Dom.getXY(this.getEl());
22730 this.lastPageX = aCoord[0];
22731 this.lastPageY = aCoord[1];
22736 * Auto-scroll the window if the dragged object has been moved beyond the
22737 * visible window boundary.
22738 * @method autoScroll
22739 * @param {int} x the drag element's x position
22740 * @param {int} y the drag element's y position
22741 * @param {int} h the height of the drag element
22742 * @param {int} w the width of the drag element
22745 autoScroll: function(x, y, h, w) {
22748 // The client height
22749 var clientH = Roo.lib.Dom.getViewWidth();
22751 // The client width
22752 var clientW = Roo.lib.Dom.getViewHeight();
22754 // The amt scrolled down
22755 var st = this.DDM.getScrollTop();
22757 // The amt scrolled right
22758 var sl = this.DDM.getScrollLeft();
22760 // Location of the bottom of the element
22763 // Location of the right of the element
22766 // The distance from the cursor to the bottom of the visible area,
22767 // adjusted so that we don't scroll if the cursor is beyond the
22768 // element drag constraints
22769 var toBot = (clientH + st - y - this.deltaY);
22771 // The distance from the cursor to the right of the visible area
22772 var toRight = (clientW + sl - x - this.deltaX);
22775 // How close to the edge the cursor must be before we scroll
22776 // var thresh = (document.all) ? 100 : 40;
22779 // How many pixels to scroll per autoscroll op. This helps to reduce
22780 // clunky scrolling. IE is more sensitive about this ... it needs this
22781 // value to be higher.
22782 var scrAmt = (document.all) ? 80 : 30;
22784 // Scroll down if we are near the bottom of the visible page and the
22785 // obj extends below the crease
22786 if ( bot > clientH && toBot < thresh ) {
22787 window.scrollTo(sl, st + scrAmt);
22790 // Scroll up if the window is scrolled down and the top of the object
22791 // goes above the top border
22792 if ( y < st && st > 0 && y - st < thresh ) {
22793 window.scrollTo(sl, st - scrAmt);
22796 // Scroll right if the obj is beyond the right border and the cursor is
22797 // near the border.
22798 if ( right > clientW && toRight < thresh ) {
22799 window.scrollTo(sl + scrAmt, st);
22802 // Scroll left if the window has been scrolled to the right and the obj
22803 // extends past the left border
22804 if ( x < sl && sl > 0 && x - sl < thresh ) {
22805 window.scrollTo(sl - scrAmt, st);
22811 * Finds the location the element should be placed if we want to move
22812 * it to where the mouse location less the click offset would place us.
22813 * @method getTargetCoord
22814 * @param {int} iPageX the X coordinate of the click
22815 * @param {int} iPageY the Y coordinate of the click
22816 * @return an object that contains the coordinates (Object.x and Object.y)
22819 getTargetCoord: function(iPageX, iPageY) {
22822 var x = iPageX - this.deltaX;
22823 var y = iPageY - this.deltaY;
22825 if (this.constrainX) {
22826 if (x < this.minX) { x = this.minX; }
22827 if (x > this.maxX) { x = this.maxX; }
22830 if (this.constrainY) {
22831 if (y < this.minY) { y = this.minY; }
22832 if (y > this.maxY) { y = this.maxY; }
22835 x = this.getTick(x, this.xTicks);
22836 y = this.getTick(y, this.yTicks);
22843 * Sets up config options specific to this class. Overrides
22844 * Roo.dd.DragDrop, but all versions of this method through the
22845 * inheritance chain are called
22847 applyConfig: function() {
22848 Roo.dd.DD.superclass.applyConfig.call(this);
22849 this.scroll = (this.config.scroll !== false);
22853 * Event that fires prior to the onMouseDown event. Overrides
22856 b4MouseDown: function(e) {
22857 // this.resetConstraints();
22858 this.autoOffset(e.getPageX(),
22863 * Event that fires prior to the onDrag event. Overrides
22866 b4Drag: function(e) {
22867 this.setDragElPos(e.getPageX(),
22871 toString: function() {
22872 return ("DD " + this.id);
22875 //////////////////////////////////////////////////////////////////////////
22876 // Debugging ygDragDrop events that can be overridden
22877 //////////////////////////////////////////////////////////////////////////
22879 startDrag: function(x, y) {
22882 onDrag: function(e) {
22885 onDragEnter: function(e, id) {
22888 onDragOver: function(e, id) {
22891 onDragOut: function(e, id) {
22894 onDragDrop: function(e, id) {
22897 endDrag: function(e) {
22904 * Ext JS Library 1.1.1
22905 * Copyright(c) 2006-2007, Ext JS, LLC.
22907 * Originally Released Under LGPL - original licence link has changed is not relivant.
22910 * <script type="text/javascript">
22914 * @class Roo.dd.DDProxy
22915 * A DragDrop implementation that inserts an empty, bordered div into
22916 * the document that follows the cursor during drag operations. At the time of
22917 * the click, the frame div is resized to the dimensions of the linked html
22918 * element, and moved to the exact location of the linked element.
22920 * References to the "frame" element refer to the single proxy element that
22921 * was created to be dragged in place of all DDProxy elements on the
22924 * @extends Roo.dd.DD
22926 * @param {String} id the id of the linked html element
22927 * @param {String} sGroup the group of related DragDrop objects
22928 * @param {object} config an object containing configurable attributes
22929 * Valid properties for DDProxy in addition to those in DragDrop:
22930 * resizeFrame, centerFrame, dragElId
22932 Roo.dd.DDProxy = function(id, sGroup, config) {
22934 this.init(id, sGroup, config);
22940 * The default drag frame div id
22941 * @property Roo.dd.DDProxy.dragElId
22945 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22947 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22950 * By default we resize the drag frame to be the same size as the element
22951 * we want to drag (this is to get the frame effect). We can turn it off
22952 * if we want a different behavior.
22953 * @property resizeFrame
22959 * By default the frame is positioned exactly where the drag element is, so
22960 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
22961 * you do not have constraints on the obj is to have the drag frame centered
22962 * around the cursor. Set centerFrame to true for this effect.
22963 * @property centerFrame
22966 centerFrame: false,
22969 * Creates the proxy element if it does not yet exist
22970 * @method createFrame
22972 createFrame: function() {
22974 var body = document.body;
22976 if (!body || !body.firstChild) {
22977 setTimeout( function() { self.createFrame(); }, 50 );
22981 var div = this.getDragEl();
22984 div = document.createElement("div");
22985 div.id = this.dragElId;
22988 s.position = "absolute";
22989 s.visibility = "hidden";
22991 s.border = "2px solid #aaa";
22994 // appendChild can blow up IE if invoked prior to the window load event
22995 // while rendering a table. It is possible there are other scenarios
22996 // that would cause this to happen as well.
22997 body.insertBefore(div, body.firstChild);
23002 * Initialization for the drag frame element. Must be called in the
23003 * constructor of all subclasses
23004 * @method initFrame
23006 initFrame: function() {
23007 this.createFrame();
23010 applyConfig: function() {
23011 Roo.dd.DDProxy.superclass.applyConfig.call(this);
23013 this.resizeFrame = (this.config.resizeFrame !== false);
23014 this.centerFrame = (this.config.centerFrame);
23015 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23019 * Resizes the drag frame to the dimensions of the clicked object, positions
23020 * it over the object, and finally displays it
23021 * @method showFrame
23022 * @param {int} iPageX X click position
23023 * @param {int} iPageY Y click position
23026 showFrame: function(iPageX, iPageY) {
23027 var el = this.getEl();
23028 var dragEl = this.getDragEl();
23029 var s = dragEl.style;
23031 this._resizeProxy();
23033 if (this.centerFrame) {
23034 this.setDelta( Math.round(parseInt(s.width, 10)/2),
23035 Math.round(parseInt(s.height, 10)/2) );
23038 this.setDragElPos(iPageX, iPageY);
23040 Roo.fly(dragEl).show();
23044 * The proxy is automatically resized to the dimensions of the linked
23045 * element when a drag is initiated, unless resizeFrame is set to false
23046 * @method _resizeProxy
23049 _resizeProxy: function() {
23050 if (this.resizeFrame) {
23051 var el = this.getEl();
23052 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23056 // overrides Roo.dd.DragDrop
23057 b4MouseDown: function(e) {
23058 var x = e.getPageX();
23059 var y = e.getPageY();
23060 this.autoOffset(x, y);
23061 this.setDragElPos(x, y);
23064 // overrides Roo.dd.DragDrop
23065 b4StartDrag: function(x, y) {
23066 // show the drag frame
23067 this.showFrame(x, y);
23070 // overrides Roo.dd.DragDrop
23071 b4EndDrag: function(e) {
23072 Roo.fly(this.getDragEl()).hide();
23075 // overrides Roo.dd.DragDrop
23076 // By default we try to move the element to the last location of the frame.
23077 // This is so that the default behavior mirrors that of Roo.dd.DD.
23078 endDrag: function(e) {
23080 var lel = this.getEl();
23081 var del = this.getDragEl();
23083 // Show the drag frame briefly so we can get its position
23084 del.style.visibility = "";
23087 // Hide the linked element before the move to get around a Safari
23089 lel.style.visibility = "hidden";
23090 Roo.dd.DDM.moveToEl(lel, del);
23091 del.style.visibility = "hidden";
23092 lel.style.visibility = "";
23097 beforeMove : function(){
23101 afterDrag : function(){
23105 toString: function() {
23106 return ("DDProxy " + this.id);
23112 * Ext JS Library 1.1.1
23113 * Copyright(c) 2006-2007, Ext JS, LLC.
23115 * Originally Released Under LGPL - original licence link has changed is not relivant.
23118 * <script type="text/javascript">
23122 * @class Roo.dd.DDTarget
23123 * A DragDrop implementation that does not move, but can be a drop
23124 * target. You would get the same result by simply omitting implementation
23125 * for the event callbacks, but this way we reduce the processing cost of the
23126 * event listener and the callbacks.
23127 * @extends Roo.dd.DragDrop
23129 * @param {String} id the id of the element that is a drop target
23130 * @param {String} sGroup the group of related DragDrop objects
23131 * @param {object} config an object containing configurable attributes
23132 * Valid properties for DDTarget in addition to those in
23136 Roo.dd.DDTarget = function(id, sGroup, config) {
23138 this.initTarget(id, sGroup, config);
23140 if (config && (config.listeners || config.events)) {
23141 Roo.dd.DragDrop.superclass.constructor.call(this, {
23142 listeners : config.listeners || {},
23143 events : config.events || {}
23148 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23149 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23150 toString: function() {
23151 return ("DDTarget " + this.id);
23156 * Ext JS Library 1.1.1
23157 * Copyright(c) 2006-2007, Ext JS, LLC.
23159 * Originally Released Under LGPL - original licence link has changed is not relivant.
23162 * <script type="text/javascript">
23167 * @class Roo.dd.ScrollManager
23168 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23169 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23172 Roo.dd.ScrollManager = function(){
23173 var ddm = Roo.dd.DragDropMgr;
23180 var onStop = function(e){
23185 var triggerRefresh = function(){
23186 if(ddm.dragCurrent){
23187 ddm.refreshCache(ddm.dragCurrent.groups);
23191 var doScroll = function(){
23192 if(ddm.dragCurrent){
23193 var dds = Roo.dd.ScrollManager;
23195 if(proc.el.scroll(proc.dir, dds.increment)){
23199 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23204 var clearProc = function(){
23206 clearInterval(proc.id);
23213 var startProc = function(el, dir){
23214 Roo.log('scroll startproc');
23218 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23221 var onFire = function(e, isDrop){
23223 if(isDrop || !ddm.dragCurrent){ return; }
23224 var dds = Roo.dd.ScrollManager;
23225 if(!dragEl || dragEl != ddm.dragCurrent){
23226 dragEl = ddm.dragCurrent;
23227 // refresh regions on drag start
23228 dds.refreshCache();
23231 var xy = Roo.lib.Event.getXY(e);
23232 var pt = new Roo.lib.Point(xy[0], xy[1]);
23233 for(var id in els){
23234 var el = els[id], r = el._region;
23235 if(r && r.contains(pt) && el.isScrollable()){
23236 if(r.bottom - pt.y <= dds.thresh){
23238 startProc(el, "down");
23241 }else if(r.right - pt.x <= dds.thresh){
23243 startProc(el, "left");
23246 }else if(pt.y - r.top <= dds.thresh){
23248 startProc(el, "up");
23251 }else if(pt.x - r.left <= dds.thresh){
23253 startProc(el, "right");
23262 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23263 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23267 * Registers new overflow element(s) to auto scroll
23268 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23270 register : function(el){
23271 if(el instanceof Array){
23272 for(var i = 0, len = el.length; i < len; i++) {
23273 this.register(el[i]);
23279 Roo.dd.ScrollManager.els = els;
23283 * Unregisters overflow element(s) so they are no longer scrolled
23284 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23286 unregister : function(el){
23287 if(el instanceof Array){
23288 for(var i = 0, len = el.length; i < len; i++) {
23289 this.unregister(el[i]);
23298 * The number of pixels from the edge of a container the pointer needs to be to
23299 * trigger scrolling (defaults to 25)
23305 * The number of pixels to scroll in each scroll increment (defaults to 50)
23311 * The frequency of scrolls in milliseconds (defaults to 500)
23317 * True to animate the scroll (defaults to true)
23323 * The animation duration in seconds -
23324 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23330 * Manually trigger a cache refresh.
23332 refreshCache : function(){
23333 for(var id in els){
23334 if(typeof els[id] == 'object'){ // for people extending the object prototype
23335 els[id]._region = els[id].getRegion();
23342 * Ext JS Library 1.1.1
23343 * Copyright(c) 2006-2007, Ext JS, LLC.
23345 * Originally Released Under LGPL - original licence link has changed is not relivant.
23348 * <script type="text/javascript">
23353 * @class Roo.dd.Registry
23354 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
23355 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23358 Roo.dd.Registry = function(){
23361 var autoIdSeed = 0;
23363 var getId = function(el, autogen){
23364 if(typeof el == "string"){
23368 if(!id && autogen !== false){
23369 id = "roodd-" + (++autoIdSeed);
23377 * Register a drag drop element
23378 * @param {String|HTMLElement} element The id or DOM node to register
23379 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23380 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
23381 * knows how to interpret, plus there are some specific properties known to the Registry that should be
23382 * populated in the data object (if applicable):
23384 Value Description<br />
23385 --------- ------------------------------------------<br />
23386 handles Array of DOM nodes that trigger dragging<br />
23387 for the element being registered<br />
23388 isHandle True if the element passed in triggers<br />
23389 dragging itself, else false
23392 register : function(el, data){
23394 if(typeof el == "string"){
23395 el = document.getElementById(el);
23398 elements[getId(el)] = data;
23399 if(data.isHandle !== false){
23400 handles[data.ddel.id] = data;
23403 var hs = data.handles;
23404 for(var i = 0, len = hs.length; i < len; i++){
23405 handles[getId(hs[i])] = data;
23411 * Unregister a drag drop element
23412 * @param {String|HTMLElement} element The id or DOM node to unregister
23414 unregister : function(el){
23415 var id = getId(el, false);
23416 var data = elements[id];
23418 delete elements[id];
23420 var hs = data.handles;
23421 for(var i = 0, len = hs.length; i < len; i++){
23422 delete handles[getId(hs[i], false)];
23429 * Returns the handle registered for a DOM Node by id
23430 * @param {String|HTMLElement} id The DOM node or id to look up
23431 * @return {Object} handle The custom handle data
23433 getHandle : function(id){
23434 if(typeof id != "string"){ // must be element?
23437 return handles[id];
23441 * Returns the handle that is registered for the DOM node that is the target of the event
23442 * @param {Event} e The event
23443 * @return {Object} handle The custom handle data
23445 getHandleFromEvent : function(e){
23446 var t = Roo.lib.Event.getTarget(e);
23447 return t ? handles[t.id] : null;
23451 * Returns a custom data object that is registered for a DOM node by id
23452 * @param {String|HTMLElement} id The DOM node or id to look up
23453 * @return {Object} data The custom data
23455 getTarget : function(id){
23456 if(typeof id != "string"){ // must be element?
23459 return elements[id];
23463 * Returns a custom data object that is registered for the DOM node that is the target of the event
23464 * @param {Event} e The event
23465 * @return {Object} data The custom data
23467 getTargetFromEvent : function(e){
23468 var t = Roo.lib.Event.getTarget(e);
23469 return t ? elements[t.id] || handles[t.id] : null;
23474 * Ext JS Library 1.1.1
23475 * Copyright(c) 2006-2007, Ext JS, LLC.
23477 * Originally Released Under LGPL - original licence link has changed is not relivant.
23480 * <script type="text/javascript">
23485 * @class Roo.dd.StatusProxy
23486 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
23487 * default drag proxy used by all Roo.dd components.
23489 * @param {Object} config
23491 Roo.dd.StatusProxy = function(config){
23492 Roo.apply(this, config);
23493 this.id = this.id || Roo.id();
23494 this.el = new Roo.Layer({
23496 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23497 {tag: "div", cls: "x-dd-drop-icon"},
23498 {tag: "div", cls: "x-dd-drag-ghost"}
23501 shadow: !config || config.shadow !== false
23503 this.ghost = Roo.get(this.el.dom.childNodes[1]);
23504 this.dropStatus = this.dropNotAllowed;
23507 Roo.dd.StatusProxy.prototype = {
23509 * @cfg {String} dropAllowed
23510 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23512 dropAllowed : "x-dd-drop-ok",
23514 * @cfg {String} dropNotAllowed
23515 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23517 dropNotAllowed : "x-dd-drop-nodrop",
23520 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23521 * over the current target element.
23522 * @param {String} cssClass The css class for the new drop status indicator image
23524 setStatus : function(cssClass){
23525 cssClass = cssClass || this.dropNotAllowed;
23526 if(this.dropStatus != cssClass){
23527 this.el.replaceClass(this.dropStatus, cssClass);
23528 this.dropStatus = cssClass;
23533 * Resets the status indicator to the default dropNotAllowed value
23534 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23536 reset : function(clearGhost){
23537 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23538 this.dropStatus = this.dropNotAllowed;
23540 this.ghost.update("");
23545 * Updates the contents of the ghost element
23546 * @param {String} html The html that will replace the current innerHTML of the ghost element
23548 update : function(html){
23549 if(typeof html == "string"){
23550 this.ghost.update(html);
23552 this.ghost.update("");
23553 html.style.margin = "0";
23554 this.ghost.dom.appendChild(html);
23556 // ensure float = none set?? cant remember why though.
23557 var el = this.ghost.dom.firstChild;
23559 Roo.fly(el).setStyle('float', 'none');
23564 * Returns the underlying proxy {@link Roo.Layer}
23565 * @return {Roo.Layer} el
23567 getEl : function(){
23572 * Returns the ghost element
23573 * @return {Roo.Element} el
23575 getGhost : function(){
23581 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23583 hide : function(clear){
23591 * Stops the repair animation if it's currently running
23594 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23600 * Displays this proxy
23607 * Force the Layer to sync its shadow and shim positions to the element
23614 * Causes the proxy to return to its position of origin via an animation. Should be called after an
23615 * invalid drop operation by the item being dragged.
23616 * @param {Array} xy The XY position of the element ([x, y])
23617 * @param {Function} callback The function to call after the repair is complete
23618 * @param {Object} scope The scope in which to execute the callback
23620 repair : function(xy, callback, scope){
23621 this.callback = callback;
23622 this.scope = scope;
23623 if(xy && this.animRepair !== false){
23624 this.el.addClass("x-dd-drag-repair");
23625 this.el.hideUnders(true);
23626 this.anim = this.el.shift({
23627 duration: this.repairDuration || .5,
23631 callback: this.afterRepair,
23635 this.afterRepair();
23640 afterRepair : function(){
23642 if(typeof this.callback == "function"){
23643 this.callback.call(this.scope || this);
23645 this.callback = null;
23650 * Ext JS Library 1.1.1
23651 * Copyright(c) 2006-2007, Ext JS, LLC.
23653 * Originally Released Under LGPL - original licence link has changed is not relivant.
23656 * <script type="text/javascript">
23660 * @class Roo.dd.DragSource
23661 * @extends Roo.dd.DDProxy
23662 * A simple class that provides the basic implementation needed to make any element draggable.
23664 * @param {String/HTMLElement/Element} el The container element
23665 * @param {Object} config
23667 Roo.dd.DragSource = function(el, config){
23668 this.el = Roo.get(el);
23669 this.dragData = {};
23671 Roo.apply(this, config);
23674 this.proxy = new Roo.dd.StatusProxy();
23677 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23678 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23680 this.dragging = false;
23683 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23685 * @cfg {String} dropAllowed
23686 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23688 dropAllowed : "x-dd-drop-ok",
23690 * @cfg {String} dropNotAllowed
23691 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23693 dropNotAllowed : "x-dd-drop-nodrop",
23696 * Returns the data object associated with this drag source
23697 * @return {Object} data An object containing arbitrary data
23699 getDragData : function(e){
23700 return this.dragData;
23704 onDragEnter : function(e, id){
23705 var target = Roo.dd.DragDropMgr.getDDById(id);
23706 this.cachedTarget = target;
23707 if(this.beforeDragEnter(target, e, id) !== false){
23708 if(target.isNotifyTarget){
23709 var status = target.notifyEnter(this, e, this.dragData);
23710 this.proxy.setStatus(status);
23712 this.proxy.setStatus(this.dropAllowed);
23715 if(this.afterDragEnter){
23717 * An empty function by default, but provided so that you can perform a custom action
23718 * when the dragged item enters the drop target by providing an implementation.
23719 * @param {Roo.dd.DragDrop} target The drop target
23720 * @param {Event} e The event object
23721 * @param {String} id The id of the dragged element
23722 * @method afterDragEnter
23724 this.afterDragEnter(target, e, id);
23730 * An empty function by default, but provided so that you can perform a custom action
23731 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23732 * @param {Roo.dd.DragDrop} target The drop target
23733 * @param {Event} e The event object
23734 * @param {String} id The id of the dragged element
23735 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23737 beforeDragEnter : function(target, e, id){
23742 alignElWithMouse: function() {
23743 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23748 onDragOver : function(e, id){
23749 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23750 if(this.beforeDragOver(target, e, id) !== false){
23751 if(target.isNotifyTarget){
23752 var status = target.notifyOver(this, e, this.dragData);
23753 this.proxy.setStatus(status);
23756 if(this.afterDragOver){
23758 * An empty function by default, but provided so that you can perform a custom action
23759 * while the dragged item is over the drop target by providing an implementation.
23760 * @param {Roo.dd.DragDrop} target The drop target
23761 * @param {Event} e The event object
23762 * @param {String} id The id of the dragged element
23763 * @method afterDragOver
23765 this.afterDragOver(target, e, id);
23771 * An empty function by default, but provided so that you can perform a custom action
23772 * while the dragged item is over the drop target and optionally cancel the onDragOver.
23773 * @param {Roo.dd.DragDrop} target The drop target
23774 * @param {Event} e The event object
23775 * @param {String} id The id of the dragged element
23776 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23778 beforeDragOver : function(target, e, id){
23783 onDragOut : function(e, id){
23784 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23785 if(this.beforeDragOut(target, e, id) !== false){
23786 if(target.isNotifyTarget){
23787 target.notifyOut(this, e, this.dragData);
23789 this.proxy.reset();
23790 if(this.afterDragOut){
23792 * An empty function by default, but provided so that you can perform a custom action
23793 * after the dragged item is dragged out of the target without dropping.
23794 * @param {Roo.dd.DragDrop} target The drop target
23795 * @param {Event} e The event object
23796 * @param {String} id The id of the dragged element
23797 * @method afterDragOut
23799 this.afterDragOut(target, e, id);
23802 this.cachedTarget = null;
23806 * An empty function by default, but provided so that you can perform a custom action before the dragged
23807 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23808 * @param {Roo.dd.DragDrop} target The drop target
23809 * @param {Event} e The event object
23810 * @param {String} id The id of the dragged element
23811 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23813 beforeDragOut : function(target, e, id){
23818 onDragDrop : function(e, id){
23819 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23820 if(this.beforeDragDrop(target, e, id) !== false){
23821 if(target.isNotifyTarget){
23822 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23823 this.onValidDrop(target, e, id);
23825 this.onInvalidDrop(target, e, id);
23828 this.onValidDrop(target, e, id);
23831 if(this.afterDragDrop){
23833 * An empty function by default, but provided so that you can perform a custom action
23834 * after a valid drag drop has occurred by providing an implementation.
23835 * @param {Roo.dd.DragDrop} target The drop target
23836 * @param {Event} e The event object
23837 * @param {String} id The id of the dropped element
23838 * @method afterDragDrop
23840 this.afterDragDrop(target, e, id);
23843 delete this.cachedTarget;
23847 * An empty function by default, but provided so that you can perform a custom action before the dragged
23848 * item is dropped onto the target and optionally cancel the onDragDrop.
23849 * @param {Roo.dd.DragDrop} target The drop target
23850 * @param {Event} e The event object
23851 * @param {String} id The id of the dragged element
23852 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23854 beforeDragDrop : function(target, e, id){
23859 onValidDrop : function(target, e, id){
23861 if(this.afterValidDrop){
23863 * An empty function by default, but provided so that you can perform a custom action
23864 * after a valid drop has occurred by providing an implementation.
23865 * @param {Object} target The target DD
23866 * @param {Event} e The event object
23867 * @param {String} id The id of the dropped element
23868 * @method afterInvalidDrop
23870 this.afterValidDrop(target, e, id);
23875 getRepairXY : function(e, data){
23876 return this.el.getXY();
23880 onInvalidDrop : function(target, e, id){
23881 this.beforeInvalidDrop(target, e, id);
23882 if(this.cachedTarget){
23883 if(this.cachedTarget.isNotifyTarget){
23884 this.cachedTarget.notifyOut(this, e, this.dragData);
23886 this.cacheTarget = null;
23888 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23890 if(this.afterInvalidDrop){
23892 * An empty function by default, but provided so that you can perform a custom action
23893 * after an invalid drop has occurred by providing an implementation.
23894 * @param {Event} e The event object
23895 * @param {String} id The id of the dropped element
23896 * @method afterInvalidDrop
23898 this.afterInvalidDrop(e, id);
23903 afterRepair : function(){
23905 this.el.highlight(this.hlColor || "c3daf9");
23907 this.dragging = false;
23911 * An empty function by default, but provided so that you can perform a custom action after an invalid
23912 * drop has occurred.
23913 * @param {Roo.dd.DragDrop} target The drop target
23914 * @param {Event} e The event object
23915 * @param {String} id The id of the dragged element
23916 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23918 beforeInvalidDrop : function(target, e, id){
23923 handleMouseDown : function(e){
23924 if(this.dragging) {
23927 var data = this.getDragData(e);
23928 if(data && this.onBeforeDrag(data, e) !== false){
23929 this.dragData = data;
23931 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23936 * An empty function by default, but provided so that you can perform a custom action before the initial
23937 * drag event begins and optionally cancel it.
23938 * @param {Object} data An object containing arbitrary data to be shared with drop targets
23939 * @param {Event} e The event object
23940 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23942 onBeforeDrag : function(data, e){
23947 * An empty function by default, but provided so that you can perform a custom action once the initial
23948 * drag event has begun. The drag cannot be canceled from this function.
23949 * @param {Number} x The x position of the click on the dragged object
23950 * @param {Number} y The y position of the click on the dragged object
23952 onStartDrag : Roo.emptyFn,
23954 // private - YUI override
23955 startDrag : function(x, y){
23956 this.proxy.reset();
23957 this.dragging = true;
23958 this.proxy.update("");
23959 this.onInitDrag(x, y);
23964 onInitDrag : function(x, y){
23965 var clone = this.el.dom.cloneNode(true);
23966 clone.id = Roo.id(); // prevent duplicate ids
23967 this.proxy.update(clone);
23968 this.onStartDrag(x, y);
23973 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23974 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23976 getProxy : function(){
23981 * Hides the drag source's {@link Roo.dd.StatusProxy}
23983 hideProxy : function(){
23985 this.proxy.reset(true);
23986 this.dragging = false;
23990 triggerCacheRefresh : function(){
23991 Roo.dd.DDM.refreshCache(this.groups);
23994 // private - override to prevent hiding
23995 b4EndDrag: function(e) {
23998 // private - override to prevent moving
23999 endDrag : function(e){
24000 this.onEndDrag(this.dragData, e);
24004 onEndDrag : function(data, e){
24007 // private - pin to cursor
24008 autoOffset : function(x, y) {
24009 this.setDelta(-12, -20);
24013 * Ext JS Library 1.1.1
24014 * Copyright(c) 2006-2007, Ext JS, LLC.
24016 * Originally Released Under LGPL - original licence link has changed is not relivant.
24019 * <script type="text/javascript">
24024 * @class Roo.dd.DropTarget
24025 * @extends Roo.dd.DDTarget
24026 * A simple class that provides the basic implementation needed to make any element a drop target that can have
24027 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
24029 * @param {String/HTMLElement/Element} el The container element
24030 * @param {Object} config
24032 Roo.dd.DropTarget = function(el, config){
24033 this.el = Roo.get(el);
24035 var listeners = false; ;
24036 if (config && config.listeners) {
24037 listeners= config.listeners;
24038 delete config.listeners;
24040 Roo.apply(this, config);
24042 if(this.containerScroll){
24043 Roo.dd.ScrollManager.register(this.el);
24047 * @scope Roo.dd.DropTarget
24052 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24053 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
24054 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
24056 * IMPORTANT : it should set this.valid to true|false
24058 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24059 * @param {Event} e The event
24060 * @param {Object} data An object containing arbitrary data supplied by the drag source
24066 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24067 * This method will be called on every mouse movement while the drag source is over the drop target.
24068 * This default implementation simply returns the dropAllowed config value.
24070 * IMPORTANT : it should set this.valid to true|false
24072 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24073 * @param {Event} e The event
24074 * @param {Object} data An object containing arbitrary data supplied by the drag source
24080 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24081 * out of the target without dropping. This default implementation simply removes the CSS class specified by
24082 * overClass (if any) from the drop element.
24085 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24086 * @param {Event} e The event
24087 * @param {Object} data An object containing arbitrary data supplied by the drag source
24093 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24094 * been dropped on it. This method has no default implementation and returns false, so you must provide an
24095 * implementation that does something to process the drop event and returns true so that the drag source's
24096 * repair action does not run.
24098 * IMPORTANT : it should set this.success
24100 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24101 * @param {Event} e The event
24102 * @param {Object} data An object containing arbitrary data supplied by the drag source
24108 Roo.dd.DropTarget.superclass.constructor.call( this,
24110 this.ddGroup || this.group,
24113 listeners : listeners || {}
24121 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24123 * @cfg {String} overClass
24124 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24127 * @cfg {String} ddGroup
24128 * The drag drop group to handle drop events for
24132 * @cfg {String} dropAllowed
24133 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24135 dropAllowed : "x-dd-drop-ok",
24137 * @cfg {String} dropNotAllowed
24138 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24140 dropNotAllowed : "x-dd-drop-nodrop",
24142 * @cfg {boolean} success
24143 * set this after drop listener..
24147 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24148 * if the drop point is valid for over/enter..
24155 isNotifyTarget : true,
24160 notifyEnter : function(dd, e, data)
24163 this.fireEvent('enter', dd, e, data);
24164 if(this.overClass){
24165 this.el.addClass(this.overClass);
24167 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24168 this.valid ? this.dropAllowed : this.dropNotAllowed
24175 notifyOver : function(dd, e, data)
24178 this.fireEvent('over', dd, e, data);
24179 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24180 this.valid ? this.dropAllowed : this.dropNotAllowed
24187 notifyOut : function(dd, e, data)
24189 this.fireEvent('out', dd, e, data);
24190 if(this.overClass){
24191 this.el.removeClass(this.overClass);
24198 notifyDrop : function(dd, e, data)
24200 this.success = false;
24201 this.fireEvent('drop', dd, e, data);
24202 return this.success;
24206 * Ext JS Library 1.1.1
24207 * Copyright(c) 2006-2007, Ext JS, LLC.
24209 * Originally Released Under LGPL - original licence link has changed is not relivant.
24212 * <script type="text/javascript">
24217 * @class Roo.dd.DragZone
24218 * @extends Roo.dd.DragSource
24219 * This class provides a container DD instance that proxies for multiple child node sources.<br />
24220 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24222 * @param {String/HTMLElement/Element} el The container element
24223 * @param {Object} config
24225 Roo.dd.DragZone = function(el, config){
24226 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24227 if(this.containerScroll){
24228 Roo.dd.ScrollManager.register(this.el);
24232 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24234 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24235 * for auto scrolling during drag operations.
24238 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24239 * method after a failed drop (defaults to "c3daf9" - light blue)
24243 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24244 * for a valid target to drag based on the mouse down. Override this method
24245 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24246 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24247 * @param {EventObject} e The mouse down event
24248 * @return {Object} The dragData
24250 getDragData : function(e){
24251 return Roo.dd.Registry.getHandleFromEvent(e);
24255 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24256 * this.dragData.ddel
24257 * @param {Number} x The x position of the click on the dragged object
24258 * @param {Number} y The y position of the click on the dragged object
24259 * @return {Boolean} true to continue the drag, false to cancel
24261 onInitDrag : function(x, y){
24262 this.proxy.update(this.dragData.ddel.cloneNode(true));
24263 this.onStartDrag(x, y);
24268 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
24270 afterRepair : function(){
24272 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24274 this.dragging = false;
24278 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24279 * the XY of this.dragData.ddel
24280 * @param {EventObject} e The mouse up event
24281 * @return {Array} The xy location (e.g. [100, 200])
24283 getRepairXY : function(e){
24284 return Roo.Element.fly(this.dragData.ddel).getXY();
24288 * Ext JS Library 1.1.1
24289 * Copyright(c) 2006-2007, Ext JS, LLC.
24291 * Originally Released Under LGPL - original licence link has changed is not relivant.
24294 * <script type="text/javascript">
24297 * @class Roo.dd.DropZone
24298 * @extends Roo.dd.DropTarget
24299 * This class provides a container DD instance that proxies for multiple child node targets.<br />
24300 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24302 * @param {String/HTMLElement/Element} el The container element
24303 * @param {Object} config
24305 Roo.dd.DropZone = function(el, config){
24306 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24309 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24311 * Returns a custom data object associated with the DOM node that is the target of the event. By default
24312 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24313 * provide your own custom lookup.
24314 * @param {Event} e The event
24315 * @return {Object} data The custom data
24317 getTargetFromEvent : function(e){
24318 return Roo.dd.Registry.getTargetFromEvent(e);
24322 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24323 * that it has registered. This method has no default implementation and should be overridden to provide
24324 * node-specific processing if necessary.
24325 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24326 * {@link #getTargetFromEvent} for this node)
24327 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24328 * @param {Event} e The event
24329 * @param {Object} data An object containing arbitrary data supplied by the drag source
24331 onNodeEnter : function(n, dd, e, data){
24336 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24337 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
24338 * overridden to provide the proper feedback.
24339 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24340 * {@link #getTargetFromEvent} for this node)
24341 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24342 * @param {Event} e The event
24343 * @param {Object} data An object containing arbitrary data supplied by the drag source
24344 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24345 * underlying {@link Roo.dd.StatusProxy} can be updated
24347 onNodeOver : function(n, dd, e, data){
24348 return this.dropAllowed;
24352 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24353 * the drop node without dropping. This method has no default implementation and should be overridden to provide
24354 * node-specific processing if necessary.
24355 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24356 * {@link #getTargetFromEvent} for this node)
24357 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24358 * @param {Event} e The event
24359 * @param {Object} data An object containing arbitrary data supplied by the drag source
24361 onNodeOut : function(n, dd, e, data){
24366 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24367 * the drop node. The default implementation returns false, so it should be overridden to provide the
24368 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24369 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24370 * {@link #getTargetFromEvent} for this node)
24371 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24372 * @param {Event} e The event
24373 * @param {Object} data An object containing arbitrary data supplied by the drag source
24374 * @return {Boolean} True if the drop was valid, else false
24376 onNodeDrop : function(n, dd, e, data){
24381 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24382 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
24383 * it should be overridden to provide the proper feedback if necessary.
24384 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24385 * @param {Event} e The event
24386 * @param {Object} data An object containing arbitrary data supplied by the drag source
24387 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24388 * underlying {@link Roo.dd.StatusProxy} can be updated
24390 onContainerOver : function(dd, e, data){
24391 return this.dropNotAllowed;
24395 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24396 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
24397 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24398 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
24399 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24400 * @param {Event} e The event
24401 * @param {Object} data An object containing arbitrary data supplied by the drag source
24402 * @return {Boolean} True if the drop was valid, else false
24404 onContainerDrop : function(dd, e, data){
24409 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24410 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
24411 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24412 * you should override this method and provide a custom implementation.
24413 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24414 * @param {Event} e The event
24415 * @param {Object} data An object containing arbitrary data supplied by the drag source
24416 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24417 * underlying {@link Roo.dd.StatusProxy} can be updated
24419 notifyEnter : function(dd, e, data){
24420 return this.dropNotAllowed;
24424 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24425 * This method will be called on every mouse movement while the drag source is over the drop zone.
24426 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24427 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24428 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24429 * registered node, it will call {@link #onContainerOver}.
24430 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24431 * @param {Event} e The event
24432 * @param {Object} data An object containing arbitrary data supplied by the drag source
24433 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24434 * underlying {@link Roo.dd.StatusProxy} can be updated
24436 notifyOver : function(dd, e, data){
24437 var n = this.getTargetFromEvent(e);
24438 if(!n){ // not over valid drop target
24439 if(this.lastOverNode){
24440 this.onNodeOut(this.lastOverNode, dd, e, data);
24441 this.lastOverNode = null;
24443 return this.onContainerOver(dd, e, data);
24445 if(this.lastOverNode != n){
24446 if(this.lastOverNode){
24447 this.onNodeOut(this.lastOverNode, dd, e, data);
24449 this.onNodeEnter(n, dd, e, data);
24450 this.lastOverNode = n;
24452 return this.onNodeOver(n, dd, e, data);
24456 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24457 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
24458 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24459 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24460 * @param {Event} e The event
24461 * @param {Object} data An object containing arbitrary data supplied by the drag zone
24463 notifyOut : function(dd, e, data){
24464 if(this.lastOverNode){
24465 this.onNodeOut(this.lastOverNode, dd, e, data);
24466 this.lastOverNode = null;
24471 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24472 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
24473 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24474 * otherwise it will call {@link #onContainerDrop}.
24475 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24476 * @param {Event} e The event
24477 * @param {Object} data An object containing arbitrary data supplied by the drag source
24478 * @return {Boolean} True if the drop was valid, else false
24480 notifyDrop : function(dd, e, data){
24481 if(this.lastOverNode){
24482 this.onNodeOut(this.lastOverNode, dd, e, data);
24483 this.lastOverNode = null;
24485 var n = this.getTargetFromEvent(e);
24487 this.onNodeDrop(n, dd, e, data) :
24488 this.onContainerDrop(dd, e, data);
24492 triggerCacheRefresh : function(){
24493 Roo.dd.DDM.refreshCache(this.groups);
24497 * Ext JS Library 1.1.1
24498 * Copyright(c) 2006-2007, Ext JS, LLC.
24500 * Originally Released Under LGPL - original licence link has changed is not relivant.
24503 * <script type="text/javascript">
24508 * @class Roo.data.SortTypes
24510 * Defines the default sorting (casting?) comparison functions used when sorting data.
24512 Roo.data.SortTypes = {
24514 * Default sort that does nothing
24515 * @param {Mixed} s The value being converted
24516 * @return {Mixed} The comparison value
24518 none : function(s){
24523 * The regular expression used to strip tags
24527 stripTagsRE : /<\/?[^>]+>/gi,
24530 * Strips all HTML tags to sort on text only
24531 * @param {Mixed} s The value being converted
24532 * @return {String} The comparison value
24534 asText : function(s){
24535 return String(s).replace(this.stripTagsRE, "");
24539 * Strips all HTML tags to sort on text only - Case insensitive
24540 * @param {Mixed} s The value being converted
24541 * @return {String} The comparison value
24543 asUCText : function(s){
24544 return String(s).toUpperCase().replace(this.stripTagsRE, "");
24548 * Case insensitive string
24549 * @param {Mixed} s The value being converted
24550 * @return {String} The comparison value
24552 asUCString : function(s) {
24553 return String(s).toUpperCase();
24558 * @param {Mixed} s The value being converted
24559 * @return {Number} The comparison value
24561 asDate : function(s) {
24565 if(s instanceof Date){
24566 return s.getTime();
24568 return Date.parse(String(s));
24573 * @param {Mixed} s The value being converted
24574 * @return {Float} The comparison value
24576 asFloat : function(s) {
24577 var val = parseFloat(String(s).replace(/,/g, ""));
24586 * @param {Mixed} s The value being converted
24587 * @return {Number} The comparison value
24589 asInt : function(s) {
24590 var val = parseInt(String(s).replace(/,/g, ""));
24598 * Ext JS Library 1.1.1
24599 * Copyright(c) 2006-2007, Ext JS, LLC.
24601 * Originally Released Under LGPL - original licence link has changed is not relivant.
24604 * <script type="text/javascript">
24608 * @class Roo.data.Record
24609 * Instances of this class encapsulate both record <em>definition</em> information, and record
24610 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24611 * to access Records cached in an {@link Roo.data.Store} object.<br>
24613 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24614 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24617 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24619 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24620 * {@link #create}. The parameters are the same.
24621 * @param {Array} data An associative Array of data values keyed by the field name.
24622 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24623 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24624 * not specified an integer id is generated.
24626 Roo.data.Record = function(data, id){
24627 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24632 * Generate a constructor for a specific record layout.
24633 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24634 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24635 * Each field definition object may contain the following properties: <ul>
24636 * <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,
24637 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24638 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24639 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24640 * is being used, then this is a string containing the javascript expression to reference the data relative to
24641 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24642 * to the data item relative to the record element. If the mapping expression is the same as the field name,
24643 * this may be omitted.</p></li>
24644 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24645 * <ul><li>auto (Default, implies no conversion)</li>
24650 * <li>date</li></ul></p></li>
24651 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24652 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24653 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24654 * by the Reader into an object that will be stored in the Record. It is passed the
24655 * following parameters:<ul>
24656 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24658 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24660 * <br>usage:<br><pre><code>
24661 var TopicRecord = Roo.data.Record.create(
24662 {name: 'title', mapping: 'topic_title'},
24663 {name: 'author', mapping: 'username'},
24664 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24665 {name: 'lastPost', mapping: 'post_time', type: 'date'},
24666 {name: 'lastPoster', mapping: 'user2'},
24667 {name: 'excerpt', mapping: 'post_text'}
24670 var myNewRecord = new TopicRecord({
24671 title: 'Do my job please',
24674 lastPost: new Date(),
24675 lastPoster: 'Animal',
24676 excerpt: 'No way dude!'
24678 myStore.add(myNewRecord);
24683 Roo.data.Record.create = function(o){
24684 var f = function(){
24685 f.superclass.constructor.apply(this, arguments);
24687 Roo.extend(f, Roo.data.Record);
24688 var p = f.prototype;
24689 p.fields = new Roo.util.MixedCollection(false, function(field){
24692 for(var i = 0, len = o.length; i < len; i++){
24693 p.fields.add(new Roo.data.Field(o[i]));
24695 f.getField = function(name){
24696 return p.fields.get(name);
24701 Roo.data.Record.AUTO_ID = 1000;
24702 Roo.data.Record.EDIT = 'edit';
24703 Roo.data.Record.REJECT = 'reject';
24704 Roo.data.Record.COMMIT = 'commit';
24706 Roo.data.Record.prototype = {
24708 * Readonly flag - true if this record has been modified.
24717 join : function(store){
24718 this.store = store;
24722 * Set the named field to the specified value.
24723 * @param {String} name The name of the field to set.
24724 * @param {Object} value The value to set the field to.
24726 set : function(name, value){
24727 if(this.data[name] == value){
24731 if(!this.modified){
24732 this.modified = {};
24734 if(typeof this.modified[name] == 'undefined'){
24735 this.modified[name] = this.data[name];
24737 this.data[name] = value;
24738 if(!this.editing && this.store){
24739 this.store.afterEdit(this);
24744 * Get the value of the named field.
24745 * @param {String} name The name of the field to get the value of.
24746 * @return {Object} The value of the field.
24748 get : function(name){
24749 return this.data[name];
24753 beginEdit : function(){
24754 this.editing = true;
24755 this.modified = {};
24759 cancelEdit : function(){
24760 this.editing = false;
24761 delete this.modified;
24765 endEdit : function(){
24766 this.editing = false;
24767 if(this.dirty && this.store){
24768 this.store.afterEdit(this);
24773 * Usually called by the {@link Roo.data.Store} which owns the Record.
24774 * Rejects all changes made to the Record since either creation, or the last commit operation.
24775 * Modified fields are reverted to their original values.
24777 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24778 * of reject operations.
24780 reject : function(){
24781 var m = this.modified;
24783 if(typeof m[n] != "function"){
24784 this.data[n] = m[n];
24787 this.dirty = false;
24788 delete this.modified;
24789 this.editing = false;
24791 this.store.afterReject(this);
24796 * Usually called by the {@link Roo.data.Store} which owns the Record.
24797 * Commits all changes made to the Record since either creation, or the last commit operation.
24799 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24800 * of commit operations.
24802 commit : function(){
24803 this.dirty = false;
24804 delete this.modified;
24805 this.editing = false;
24807 this.store.afterCommit(this);
24812 hasError : function(){
24813 return this.error != null;
24817 clearError : function(){
24822 * Creates a copy of this record.
24823 * @param {String} id (optional) A new record id if you don't want to use this record's id
24826 copy : function(newId) {
24827 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24831 * Ext JS Library 1.1.1
24832 * Copyright(c) 2006-2007, Ext JS, LLC.
24834 * Originally Released Under LGPL - original licence link has changed is not relivant.
24837 * <script type="text/javascript">
24843 * @class Roo.data.Store
24844 * @extends Roo.util.Observable
24845 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24846 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24848 * 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
24849 * has no knowledge of the format of the data returned by the Proxy.<br>
24851 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24852 * instances from the data object. These records are cached and made available through accessor functions.
24854 * Creates a new Store.
24855 * @param {Object} config A config object containing the objects needed for the Store to access data,
24856 * and read the data into Records.
24858 Roo.data.Store = function(config){
24859 this.data = new Roo.util.MixedCollection(false);
24860 this.data.getKey = function(o){
24863 this.baseParams = {};
24865 this.paramNames = {
24870 "multisort" : "_multisort"
24873 if(config && config.data){
24874 this.inlineData = config.data;
24875 delete config.data;
24878 Roo.apply(this, config);
24880 if(this.reader){ // reader passed
24881 this.reader = Roo.factory(this.reader, Roo.data);
24882 this.reader.xmodule = this.xmodule || false;
24883 if(!this.recordType){
24884 this.recordType = this.reader.recordType;
24886 if(this.reader.onMetaChange){
24887 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24891 if(this.recordType){
24892 this.fields = this.recordType.prototype.fields;
24894 this.modified = [];
24898 * @event datachanged
24899 * Fires when the data cache has changed, and a widget which is using this Store
24900 * as a Record cache should refresh its view.
24901 * @param {Store} this
24903 datachanged : true,
24905 * @event metachange
24906 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24907 * @param {Store} this
24908 * @param {Object} meta The JSON metadata
24913 * Fires when Records have been added to the Store
24914 * @param {Store} this
24915 * @param {Roo.data.Record[]} records The array of Records added
24916 * @param {Number} index The index at which the record(s) were added
24921 * Fires when a Record has been removed from the Store
24922 * @param {Store} this
24923 * @param {Roo.data.Record} record The Record that was removed
24924 * @param {Number} index The index at which the record was removed
24929 * Fires when a Record has been updated
24930 * @param {Store} this
24931 * @param {Roo.data.Record} record The Record that was updated
24932 * @param {String} operation The update operation being performed. Value may be one of:
24934 Roo.data.Record.EDIT
24935 Roo.data.Record.REJECT
24936 Roo.data.Record.COMMIT
24942 * Fires when the data cache has been cleared.
24943 * @param {Store} this
24947 * @event beforeload
24948 * Fires before a request is made for a new data object. If the beforeload handler returns false
24949 * the load action will be canceled.
24950 * @param {Store} this
24951 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24955 * @event beforeloadadd
24956 * Fires after a new set of Records has been loaded.
24957 * @param {Store} this
24958 * @param {Roo.data.Record[]} records The Records that were loaded
24959 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24961 beforeloadadd : true,
24964 * Fires after a new set of Records has been loaded, before they are added to the store.
24965 * @param {Store} this
24966 * @param {Roo.data.Record[]} records The Records that were loaded
24967 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24968 * @params {Object} return from reader
24972 * @event loadexception
24973 * Fires if an exception occurs in the Proxy during loading.
24974 * Called with the signature of the Proxy's "loadexception" event.
24975 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24978 * @param {Object} return from JsonData.reader() - success, totalRecords, records
24979 * @param {Object} load options
24980 * @param {Object} jsonData from your request (normally this contains the Exception)
24982 loadexception : true
24986 this.proxy = Roo.factory(this.proxy, Roo.data);
24987 this.proxy.xmodule = this.xmodule || false;
24988 this.relayEvents(this.proxy, ["loadexception"]);
24990 this.sortToggle = {};
24991 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24993 Roo.data.Store.superclass.constructor.call(this);
24995 if(this.inlineData){
24996 this.loadData(this.inlineData);
24997 delete this.inlineData;
25001 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25003 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
25004 * without a remote query - used by combo/forms at present.
25008 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25011 * @cfg {Array} data Inline data to be loaded when the store is initialized.
25014 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
25015 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25018 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25019 * on any HTTP request
25022 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25025 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25029 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25030 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25032 remoteSort : false,
25035 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25036 * loaded or when a record is removed. (defaults to false).
25038 pruneModifiedRecords : false,
25041 lastOptions : null,
25044 * Add Records to the Store and fires the add event.
25045 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25047 add : function(records){
25048 records = [].concat(records);
25049 for(var i = 0, len = records.length; i < len; i++){
25050 records[i].join(this);
25052 var index = this.data.length;
25053 this.data.addAll(records);
25054 this.fireEvent("add", this, records, index);
25058 * Remove a Record from the Store and fires the remove event.
25059 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25061 remove : function(record){
25062 var index = this.data.indexOf(record);
25063 this.data.removeAt(index);
25065 if(this.pruneModifiedRecords){
25066 this.modified.remove(record);
25068 this.fireEvent("remove", this, record, index);
25072 * Remove all Records from the Store and fires the clear event.
25074 removeAll : function(){
25076 if(this.pruneModifiedRecords){
25077 this.modified = [];
25079 this.fireEvent("clear", this);
25083 * Inserts Records to the Store at the given index and fires the add event.
25084 * @param {Number} index The start index at which to insert the passed Records.
25085 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25087 insert : function(index, records){
25088 records = [].concat(records);
25089 for(var i = 0, len = records.length; i < len; i++){
25090 this.data.insert(index, records[i]);
25091 records[i].join(this);
25093 this.fireEvent("add", this, records, index);
25097 * Get the index within the cache of the passed Record.
25098 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25099 * @return {Number} The index of the passed Record. Returns -1 if not found.
25101 indexOf : function(record){
25102 return this.data.indexOf(record);
25106 * Get the index within the cache of the Record with the passed id.
25107 * @param {String} id The id of the Record to find.
25108 * @return {Number} The index of the Record. Returns -1 if not found.
25110 indexOfId : function(id){
25111 return this.data.indexOfKey(id);
25115 * Get the Record with the specified id.
25116 * @param {String} id The id of the Record to find.
25117 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25119 getById : function(id){
25120 return this.data.key(id);
25124 * Get the Record at the specified index.
25125 * @param {Number} index The index of the Record to find.
25126 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25128 getAt : function(index){
25129 return this.data.itemAt(index);
25133 * Returns a range of Records between specified indices.
25134 * @param {Number} startIndex (optional) The starting index (defaults to 0)
25135 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25136 * @return {Roo.data.Record[]} An array of Records
25138 getRange : function(start, end){
25139 return this.data.getRange(start, end);
25143 storeOptions : function(o){
25144 o = Roo.apply({}, o);
25147 this.lastOptions = o;
25151 * Loads the Record cache from the configured Proxy using the configured Reader.
25153 * If using remote paging, then the first load call must specify the <em>start</em>
25154 * and <em>limit</em> properties in the options.params property to establish the initial
25155 * position within the dataset, and the number of Records to cache on each read from the Proxy.
25157 * <strong>It is important to note that for remote data sources, loading is asynchronous,
25158 * and this call will return before the new data has been loaded. Perform any post-processing
25159 * in a callback function, or in a "load" event handler.</strong>
25161 * @param {Object} options An object containing properties which control loading options:<ul>
25162 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25163 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25166 data : data, // array of key=>value data like JsonReader
25167 total : data.length,
25173 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25174 * passed the following arguments:<ul>
25175 * <li>r : Roo.data.Record[]</li>
25176 * <li>options: Options object from the load call</li>
25177 * <li>success: Boolean success indicator</li></ul></li>
25178 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25179 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25182 load : function(options){
25183 options = options || {};
25184 if(this.fireEvent("beforeload", this, options) !== false){
25185 this.storeOptions(options);
25186 var p = Roo.apply(options.params || {}, this.baseParams);
25187 // if meta was not loaded from remote source.. try requesting it.
25188 if (!this.reader.metaFromRemote) {
25189 p._requestMeta = 1;
25191 if(this.sortInfo && this.remoteSort){
25192 var pn = this.paramNames;
25193 p[pn["sort"]] = this.sortInfo.field;
25194 p[pn["dir"]] = this.sortInfo.direction;
25196 if (this.multiSort) {
25197 var pn = this.paramNames;
25198 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25201 this.proxy.load(p, this.reader, this.loadRecords, this, options);
25206 * Reloads the Record cache from the configured Proxy using the configured Reader and
25207 * the options from the last load operation performed.
25208 * @param {Object} options (optional) An object containing properties which may override the options
25209 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25210 * the most recently used options are reused).
25212 reload : function(options){
25213 this.load(Roo.applyIf(options||{}, this.lastOptions));
25217 // Called as a callback by the Reader during a load operation.
25218 loadRecords : function(o, options, success){
25221 if(success !== false){
25222 this.fireEvent("load", this, [], options, o);
25224 if(options.callback){
25225 options.callback.call(options.scope || this, [], options, false);
25229 // if data returned failure - throw an exception.
25230 if (o.success === false) {
25231 // show a message if no listener is registered.
25232 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25233 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25235 // loadmask wil be hooked into this..
25236 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25239 var r = o.records, t = o.totalRecords || r.length;
25241 this.fireEvent("beforeloadadd", this, r, options, o);
25243 if(!options || options.add !== true){
25244 if(this.pruneModifiedRecords){
25245 this.modified = [];
25247 for(var i = 0, len = r.length; i < len; i++){
25251 this.data = this.snapshot;
25252 delete this.snapshot;
25255 this.data.addAll(r);
25256 this.totalLength = t;
25258 this.fireEvent("datachanged", this);
25260 this.totalLength = Math.max(t, this.data.length+r.length);
25264 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25266 var e = new Roo.data.Record({});
25268 e.set(this.parent.displayField, this.parent.emptyTitle);
25269 e.set(this.parent.valueField, '');
25274 this.fireEvent("load", this, r, options, o);
25275 if(options.callback){
25276 options.callback.call(options.scope || this, r, options, true);
25282 * Loads data from a passed data block. A Reader which understands the format of the data
25283 * must have been configured in the constructor.
25284 * @param {Object} data The data block from which to read the Records. The format of the data expected
25285 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25286 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25288 loadData : function(o, append){
25289 var r = this.reader.readRecords(o);
25290 this.loadRecords(r, {add: append}, true);
25294 * using 'cn' the nested child reader read the child array into it's child stores.
25295 * @param {Object} rec The record with a 'children array
25297 loadDataFromChildren : function(rec)
25299 this.loadData(this.reader.toLoadData(rec));
25304 * Gets the number of cached records.
25306 * <em>If using paging, this may not be the total size of the dataset. If the data object
25307 * used by the Reader contains the dataset size, then the getTotalCount() function returns
25308 * the data set size</em>
25310 getCount : function(){
25311 return this.data.length || 0;
25315 * Gets the total number of records in the dataset as returned by the server.
25317 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25318 * the dataset size</em>
25320 getTotalCount : function(){
25321 return this.totalLength || 0;
25325 * Returns the sort state of the Store as an object with two properties:
25327 field {String} The name of the field by which the Records are sorted
25328 direction {String} The sort order, "ASC" or "DESC"
25331 getSortState : function(){
25332 return this.sortInfo;
25336 applySort : function(){
25337 if(this.sortInfo && !this.remoteSort){
25338 var s = this.sortInfo, f = s.field;
25339 var st = this.fields.get(f).sortType;
25340 var fn = function(r1, r2){
25341 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25342 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25344 this.data.sort(s.direction, fn);
25345 if(this.snapshot && this.snapshot != this.data){
25346 this.snapshot.sort(s.direction, fn);
25352 * Sets the default sort column and order to be used by the next load operation.
25353 * @param {String} fieldName The name of the field to sort by.
25354 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25356 setDefaultSort : function(field, dir){
25357 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25361 * Sort the Records.
25362 * If remote sorting is used, the sort is performed on the server, and the cache is
25363 * reloaded. If local sorting is used, the cache is sorted internally.
25364 * @param {String} fieldName The name of the field to sort by.
25365 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25367 sort : function(fieldName, dir){
25368 var f = this.fields.get(fieldName);
25370 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25372 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25373 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25378 this.sortToggle[f.name] = dir;
25379 this.sortInfo = {field: f.name, direction: dir};
25380 if(!this.remoteSort){
25382 this.fireEvent("datachanged", this);
25384 this.load(this.lastOptions);
25389 * Calls the specified function for each of the Records in the cache.
25390 * @param {Function} fn The function to call. The Record is passed as the first parameter.
25391 * Returning <em>false</em> aborts and exits the iteration.
25392 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25394 each : function(fn, scope){
25395 this.data.each(fn, scope);
25399 * Gets all records modified since the last commit. Modified records are persisted across load operations
25400 * (e.g., during paging).
25401 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25403 getModifiedRecords : function(){
25404 return this.modified;
25408 createFilterFn : function(property, value, anyMatch){
25409 if(!value.exec){ // not a regex
25410 value = String(value);
25411 if(value.length == 0){
25414 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25416 return function(r){
25417 return value.test(r.data[property]);
25422 * Sums the value of <i>property</i> for each record between start and end and returns the result.
25423 * @param {String} property A field on your records
25424 * @param {Number} start The record index to start at (defaults to 0)
25425 * @param {Number} end The last record index to include (defaults to length - 1)
25426 * @return {Number} The sum
25428 sum : function(property, start, end){
25429 var rs = this.data.items, v = 0;
25430 start = start || 0;
25431 end = (end || end === 0) ? end : rs.length-1;
25433 for(var i = start; i <= end; i++){
25434 v += (rs[i].data[property] || 0);
25440 * Filter the records by a specified property.
25441 * @param {String} field A field on your records
25442 * @param {String/RegExp} value Either a string that the field
25443 * should start with or a RegExp to test against the field
25444 * @param {Boolean} anyMatch True to match any part not just the beginning
25446 filter : function(property, value, anyMatch){
25447 var fn = this.createFilterFn(property, value, anyMatch);
25448 return fn ? this.filterBy(fn) : this.clearFilter();
25452 * Filter by a function. The specified function will be called with each
25453 * record in this data source. If the function returns true the record is included,
25454 * otherwise it is filtered.
25455 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25456 * @param {Object} scope (optional) The scope of the function (defaults to this)
25458 filterBy : function(fn, scope){
25459 this.snapshot = this.snapshot || this.data;
25460 this.data = this.queryBy(fn, scope||this);
25461 this.fireEvent("datachanged", this);
25465 * Query the records by a specified property.
25466 * @param {String} field A field on your records
25467 * @param {String/RegExp} value Either a string that the field
25468 * should start with or a RegExp to test against the field
25469 * @param {Boolean} anyMatch True to match any part not just the beginning
25470 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25472 query : function(property, value, anyMatch){
25473 var fn = this.createFilterFn(property, value, anyMatch);
25474 return fn ? this.queryBy(fn) : this.data.clone();
25478 * Query by a function. The specified function will be called with each
25479 * record in this data source. If the function returns true the record is included
25481 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25482 * @param {Object} scope (optional) The scope of the function (defaults to this)
25483 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25485 queryBy : function(fn, scope){
25486 var data = this.snapshot || this.data;
25487 return data.filterBy(fn, scope||this);
25491 * Collects unique values for a particular dataIndex from this store.
25492 * @param {String} dataIndex The property to collect
25493 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25494 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25495 * @return {Array} An array of the unique values
25497 collect : function(dataIndex, allowNull, bypassFilter){
25498 var d = (bypassFilter === true && this.snapshot) ?
25499 this.snapshot.items : this.data.items;
25500 var v, sv, r = [], l = {};
25501 for(var i = 0, len = d.length; i < len; i++){
25502 v = d[i].data[dataIndex];
25504 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25513 * Revert to a view of the Record cache with no filtering applied.
25514 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25516 clearFilter : function(suppressEvent){
25517 if(this.snapshot && this.snapshot != this.data){
25518 this.data = this.snapshot;
25519 delete this.snapshot;
25520 if(suppressEvent !== true){
25521 this.fireEvent("datachanged", this);
25527 afterEdit : function(record){
25528 if(this.modified.indexOf(record) == -1){
25529 this.modified.push(record);
25531 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25535 afterReject : function(record){
25536 this.modified.remove(record);
25537 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25541 afterCommit : function(record){
25542 this.modified.remove(record);
25543 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25547 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25548 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25550 commitChanges : function(){
25551 var m = this.modified.slice(0);
25552 this.modified = [];
25553 for(var i = 0, len = m.length; i < len; i++){
25559 * Cancel outstanding changes on all changed records.
25561 rejectChanges : function(){
25562 var m = this.modified.slice(0);
25563 this.modified = [];
25564 for(var i = 0, len = m.length; i < len; i++){
25569 onMetaChange : function(meta, rtype, o){
25570 this.recordType = rtype;
25571 this.fields = rtype.prototype.fields;
25572 delete this.snapshot;
25573 this.sortInfo = meta.sortInfo || this.sortInfo;
25574 this.modified = [];
25575 this.fireEvent('metachange', this, this.reader.meta);
25578 moveIndex : function(data, type)
25580 var index = this.indexOf(data);
25582 var newIndex = index + type;
25586 this.insert(newIndex, data);
25591 * Ext JS Library 1.1.1
25592 * Copyright(c) 2006-2007, Ext JS, LLC.
25594 * Originally Released Under LGPL - original licence link has changed is not relivant.
25597 * <script type="text/javascript">
25601 * @class Roo.data.SimpleStore
25602 * @extends Roo.data.Store
25603 * Small helper class to make creating Stores from Array data easier.
25604 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25605 * @cfg {Array} fields An array of field definition objects, or field name strings.
25606 * @cfg {Object} an existing reader (eg. copied from another store)
25607 * @cfg {Array} data The multi-dimensional array of data
25608 * @cfg {Roo.data.DataProxy} proxy [not-required]
25609 * @cfg {Roo.data.Reader} reader [not-required]
25611 * @param {Object} config
25613 Roo.data.SimpleStore = function(config)
25615 Roo.data.SimpleStore.superclass.constructor.call(this, {
25617 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25620 Roo.data.Record.create(config.fields)
25622 proxy : new Roo.data.MemoryProxy(config.data)
25626 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25628 * Ext JS Library 1.1.1
25629 * Copyright(c) 2006-2007, Ext JS, LLC.
25631 * Originally Released Under LGPL - original licence link has changed is not relivant.
25634 * <script type="text/javascript">
25639 * @extends Roo.data.Store
25640 * @class Roo.data.JsonStore
25641 * Small helper class to make creating Stores for JSON data easier. <br/>
25643 var store = new Roo.data.JsonStore({
25644 url: 'get-images.php',
25646 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25649 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25650 * JsonReader and HttpProxy (unless inline data is provided).</b>
25651 * @cfg {Array} fields An array of field definition objects, or field name strings.
25653 * @param {Object} config
25655 Roo.data.JsonStore = function(c){
25656 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25657 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25658 reader: new Roo.data.JsonReader(c, c.fields)
25661 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25663 * Ext JS Library 1.1.1
25664 * Copyright(c) 2006-2007, Ext JS, LLC.
25666 * Originally Released Under LGPL - original licence link has changed is not relivant.
25669 * <script type="text/javascript">
25673 Roo.data.Field = function(config){
25674 if(typeof config == "string"){
25675 config = {name: config};
25677 Roo.apply(this, config);
25680 this.type = "auto";
25683 var st = Roo.data.SortTypes;
25684 // named sortTypes are supported, here we look them up
25685 if(typeof this.sortType == "string"){
25686 this.sortType = st[this.sortType];
25689 // set default sortType for strings and dates
25690 if(!this.sortType){
25693 this.sortType = st.asUCString;
25696 this.sortType = st.asDate;
25699 this.sortType = st.none;
25704 var stripRe = /[\$,%]/g;
25706 // prebuilt conversion function for this field, instead of
25707 // switching every time we're reading a value
25709 var cv, dateFormat = this.dateFormat;
25714 cv = function(v){ return v; };
25717 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25721 return v !== undefined && v !== null && v !== '' ?
25722 parseInt(String(v).replace(stripRe, ""), 10) : '';
25727 return v !== undefined && v !== null && v !== '' ?
25728 parseFloat(String(v).replace(stripRe, ""), 10) : '';
25733 cv = function(v){ return v === true || v === "true" || v == 1; };
25740 if(v instanceof Date){
25744 if(dateFormat == "timestamp"){
25745 return new Date(v*1000);
25747 return Date.parseDate(v, dateFormat);
25749 var parsed = Date.parse(v);
25750 return parsed ? new Date(parsed) : null;
25759 Roo.data.Field.prototype = {
25767 * Ext JS Library 1.1.1
25768 * Copyright(c) 2006-2007, Ext JS, LLC.
25770 * Originally Released Under LGPL - original licence link has changed is not relivant.
25773 * <script type="text/javascript">
25776 // Base class for reading structured data from a data source. This class is intended to be
25777 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25780 * @class Roo.data.DataReader
25782 * Base class for reading structured data from a data source. This class is intended to be
25783 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25786 Roo.data.DataReader = function(meta, recordType){
25790 this.recordType = recordType instanceof Array ?
25791 Roo.data.Record.create(recordType) : recordType;
25794 Roo.data.DataReader.prototype = {
25797 readerType : 'Data',
25799 * Create an empty record
25800 * @param {Object} data (optional) - overlay some values
25801 * @return {Roo.data.Record} record created.
25803 newRow : function(d) {
25805 this.recordType.prototype.fields.each(function(c) {
25807 case 'int' : da[c.name] = 0; break;
25808 case 'date' : da[c.name] = new Date(); break;
25809 case 'float' : da[c.name] = 0.0; break;
25810 case 'boolean' : da[c.name] = false; break;
25811 default : da[c.name] = ""; break;
25815 return new this.recordType(Roo.apply(da, d));
25821 * Ext JS Library 1.1.1
25822 * Copyright(c) 2006-2007, Ext JS, LLC.
25824 * Originally Released Under LGPL - original licence link has changed is not relivant.
25827 * <script type="text/javascript">
25831 * @class Roo.data.DataProxy
25832 * @extends Roo.util.Observable
25834 * This class is an abstract base class for implementations which provide retrieval of
25835 * unformatted data objects.<br>
25837 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25838 * (of the appropriate type which knows how to parse the data object) to provide a block of
25839 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25841 * Custom implementations must implement the load method as described in
25842 * {@link Roo.data.HttpProxy#load}.
25844 Roo.data.DataProxy = function(){
25847 * @event beforeload
25848 * Fires before a network request is made to retrieve a data object.
25849 * @param {Object} This DataProxy object.
25850 * @param {Object} params The params parameter to the load function.
25855 * Fires before the load method's callback is called.
25856 * @param {Object} This DataProxy object.
25857 * @param {Object} o The data object.
25858 * @param {Object} arg The callback argument object passed to the load function.
25862 * @event loadexception
25863 * Fires if an Exception occurs during data retrieval.
25864 * @param {Object} This DataProxy object.
25865 * @param {Object} o The data object.
25866 * @param {Object} arg The callback argument object passed to the load function.
25867 * @param {Object} e The Exception.
25869 loadexception : true
25871 Roo.data.DataProxy.superclass.constructor.call(this);
25874 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25877 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25881 * Ext JS Library 1.1.1
25882 * Copyright(c) 2006-2007, Ext JS, LLC.
25884 * Originally Released Under LGPL - original licence link has changed is not relivant.
25887 * <script type="text/javascript">
25890 * @class Roo.data.MemoryProxy
25891 * @extends Roo.data.DataProxy
25892 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25893 * to the Reader when its load method is called.
25895 * @param {Object} config A config object containing the objects needed for the Store to access data,
25897 Roo.data.MemoryProxy = function(config){
25899 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25900 data = config.data;
25902 Roo.data.MemoryProxy.superclass.constructor.call(this);
25906 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25909 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25912 * Load data from the requested source (in this case an in-memory
25913 * data object passed to the constructor), read the data object into
25914 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25915 * process that block using the passed callback.
25916 * @param {Object} params This parameter is not used by the MemoryProxy class.
25917 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25918 * object into a block of Roo.data.Records.
25919 * @param {Function} callback The function into which to pass the block of Roo.data.records.
25920 * The function must be passed <ul>
25921 * <li>The Record block object</li>
25922 * <li>The "arg" argument from the load function</li>
25923 * <li>A boolean success indicator</li>
25925 * @param {Object} scope The scope in which to call the callback
25926 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25928 load : function(params, reader, callback, scope, arg){
25929 params = params || {};
25932 result = reader.readRecords(params.data ? params.data :this.data);
25934 this.fireEvent("loadexception", this, arg, null, e);
25935 callback.call(scope, null, arg, false);
25938 callback.call(scope, result, arg, true);
25942 update : function(params, records){
25947 * Ext JS Library 1.1.1
25948 * Copyright(c) 2006-2007, Ext JS, LLC.
25950 * Originally Released Under LGPL - original licence link has changed is not relivant.
25953 * <script type="text/javascript">
25956 * @class Roo.data.HttpProxy
25957 * @extends Roo.data.DataProxy
25958 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25959 * configured to reference a certain URL.<br><br>
25961 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25962 * from which the running page was served.<br><br>
25964 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25966 * Be aware that to enable the browser to parse an XML document, the server must set
25967 * the Content-Type header in the HTTP response to "text/xml".
25969 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25970 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
25971 * will be used to make the request.
25973 Roo.data.HttpProxy = function(conn){
25974 Roo.data.HttpProxy.superclass.constructor.call(this);
25975 // is conn a conn config or a real conn?
25977 this.useAjax = !conn || !conn.events;
25981 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25982 // thse are take from connection...
25985 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25988 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25989 * extra parameters to each request made by this object. (defaults to undefined)
25992 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25993 * to each request made by this object. (defaults to undefined)
25996 * @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)
25999 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
26002 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
26008 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26012 * Return the {@link Roo.data.Connection} object being used by this Proxy.
26013 * @return {Connection} The Connection object. This object may be used to subscribe to events on
26014 * a finer-grained basis than the DataProxy events.
26016 getConnection : function(){
26017 return this.useAjax ? Roo.Ajax : this.conn;
26021 * Load data from the configured {@link Roo.data.Connection}, read the data object into
26022 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26023 * process that block using the passed callback.
26024 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26025 * for the request to the remote server.
26026 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26027 * object into a block of Roo.data.Records.
26028 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26029 * The function must be passed <ul>
26030 * <li>The Record block object</li>
26031 * <li>The "arg" argument from the load function</li>
26032 * <li>A boolean success indicator</li>
26034 * @param {Object} scope The scope in which to call the callback
26035 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26037 load : function(params, reader, callback, scope, arg){
26038 if(this.fireEvent("beforeload", this, params) !== false){
26040 params : params || {},
26042 callback : callback,
26047 callback : this.loadResponse,
26051 Roo.applyIf(o, this.conn);
26052 if(this.activeRequest){
26053 Roo.Ajax.abort(this.activeRequest);
26055 this.activeRequest = Roo.Ajax.request(o);
26057 this.conn.request(o);
26060 callback.call(scope||this, null, arg, false);
26065 loadResponse : function(o, success, response){
26066 delete this.activeRequest;
26068 this.fireEvent("loadexception", this, o, response);
26069 o.request.callback.call(o.request.scope, null, o.request.arg, false);
26074 result = o.reader.read(response);
26077 o.raw = { errorMsg : response.responseText };
26078 this.fireEvent("loadexception", this, o, response, e);
26079 o.request.callback.call(o.request.scope, o, o.request.arg, false);
26083 this.fireEvent("load", this, o, o.request.arg);
26084 o.request.callback.call(o.request.scope, result, o.request.arg, true);
26088 update : function(dataSet){
26093 updateResponse : function(dataSet){
26098 * Ext JS Library 1.1.1
26099 * Copyright(c) 2006-2007, Ext JS, LLC.
26101 * Originally Released Under LGPL - original licence link has changed is not relivant.
26104 * <script type="text/javascript">
26108 * @class Roo.data.ScriptTagProxy
26109 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26110 * other than the originating domain of the running page.<br><br>
26112 * <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
26113 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26115 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26116 * source code that is used as the source inside a <script> tag.<br><br>
26118 * In order for the browser to process the returned data, the server must wrap the data object
26119 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26120 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26121 * depending on whether the callback name was passed:
26124 boolean scriptTag = false;
26125 String cb = request.getParameter("callback");
26128 response.setContentType("text/javascript");
26130 response.setContentType("application/x-json");
26132 Writer out = response.getWriter();
26134 out.write(cb + "(");
26136 out.print(dataBlock.toJsonString());
26143 * @param {Object} config A configuration object.
26145 Roo.data.ScriptTagProxy = function(config){
26146 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26147 Roo.apply(this, config);
26148 this.head = document.getElementsByTagName("head")[0];
26151 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26153 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26155 * @cfg {String} url The URL from which to request the data object.
26158 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26162 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26163 * the server the name of the callback function set up by the load call to process the returned data object.
26164 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26165 * javascript output which calls this named function passing the data object as its only parameter.
26167 callbackParam : "callback",
26169 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26170 * name to the request.
26175 * Load data from the configured URL, read the data object into
26176 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26177 * process that block using the passed callback.
26178 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26179 * for the request to the remote server.
26180 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26181 * object into a block of Roo.data.Records.
26182 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26183 * The function must be passed <ul>
26184 * <li>The Record block object</li>
26185 * <li>The "arg" argument from the load function</li>
26186 * <li>A boolean success indicator</li>
26188 * @param {Object} scope The scope in which to call the callback
26189 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26191 load : function(params, reader, callback, scope, arg){
26192 if(this.fireEvent("beforeload", this, params) !== false){
26194 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26196 var url = this.url;
26197 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26199 url += "&_dc=" + (new Date().getTime());
26201 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26204 cb : "stcCallback"+transId,
26205 scriptId : "stcScript"+transId,
26209 callback : callback,
26215 window[trans.cb] = function(o){
26216 conn.handleResponse(o, trans);
26219 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26221 if(this.autoAbort !== false){
26225 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26227 var script = document.createElement("script");
26228 script.setAttribute("src", url);
26229 script.setAttribute("type", "text/javascript");
26230 script.setAttribute("id", trans.scriptId);
26231 this.head.appendChild(script);
26233 this.trans = trans;
26235 callback.call(scope||this, null, arg, false);
26240 isLoading : function(){
26241 return this.trans ? true : false;
26245 * Abort the current server request.
26247 abort : function(){
26248 if(this.isLoading()){
26249 this.destroyTrans(this.trans);
26254 destroyTrans : function(trans, isLoaded){
26255 this.head.removeChild(document.getElementById(trans.scriptId));
26256 clearTimeout(trans.timeoutId);
26258 window[trans.cb] = undefined;
26260 delete window[trans.cb];
26263 // if hasn't been loaded, wait for load to remove it to prevent script error
26264 window[trans.cb] = function(){
26265 window[trans.cb] = undefined;
26267 delete window[trans.cb];
26274 handleResponse : function(o, trans){
26275 this.trans = false;
26276 this.destroyTrans(trans, true);
26279 result = trans.reader.readRecords(o);
26281 this.fireEvent("loadexception", this, o, trans.arg, e);
26282 trans.callback.call(trans.scope||window, null, trans.arg, false);
26285 this.fireEvent("load", this, o, trans.arg);
26286 trans.callback.call(trans.scope||window, result, trans.arg, true);
26290 handleFailure : function(trans){
26291 this.trans = false;
26292 this.destroyTrans(trans, false);
26293 this.fireEvent("loadexception", this, null, trans.arg);
26294 trans.callback.call(trans.scope||window, null, trans.arg, false);
26298 * Ext JS Library 1.1.1
26299 * Copyright(c) 2006-2007, Ext JS, LLC.
26301 * Originally Released Under LGPL - original licence link has changed is not relivant.
26304 * <script type="text/javascript">
26308 * @class Roo.data.JsonReader
26309 * @extends Roo.data.DataReader
26310 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26311 * based on mappings in a provided Roo.data.Record constructor.
26313 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26314 * in the reply previously.
26319 var RecordDef = Roo.data.Record.create([
26320 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26321 {name: 'occupation'} // This field will use "occupation" as the mapping.
26323 var myReader = new Roo.data.JsonReader({
26324 totalProperty: "results", // The property which contains the total dataset size (optional)
26325 root: "rows", // The property which contains an Array of row objects
26326 id: "id" // The property within each row object that provides an ID for the record (optional)
26330 * This would consume a JSON file like this:
26332 { 'results': 2, 'rows': [
26333 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26334 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26337 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26338 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26339 * paged from the remote server.
26340 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26341 * @cfg {String} root name of the property which contains the Array of row objects.
26342 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26343 * @cfg {Array} fields Array of field definition objects
26345 * Create a new JsonReader
26346 * @param {Object} meta Metadata configuration options
26347 * @param {Object} recordType Either an Array of field definition objects,
26348 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26350 Roo.data.JsonReader = function(meta, recordType){
26353 // set some defaults:
26354 Roo.applyIf(meta, {
26355 totalProperty: 'total',
26356 successProperty : 'success',
26361 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26363 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26365 readerType : 'Json',
26368 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
26369 * Used by Store query builder to append _requestMeta to params.
26372 metaFromRemote : false,
26374 * This method is only used by a DataProxy which has retrieved data from a remote server.
26375 * @param {Object} response The XHR object which contains the JSON data in its responseText.
26376 * @return {Object} data A data block which is used by an Roo.data.Store object as
26377 * a cache of Roo.data.Records.
26379 read : function(response){
26380 var json = response.responseText;
26382 var o = /* eval:var:o */ eval("("+json+")");
26384 throw {message: "JsonReader.read: Json object not found"};
26390 this.metaFromRemote = true;
26391 this.meta = o.metaData;
26392 this.recordType = Roo.data.Record.create(o.metaData.fields);
26393 this.onMetaChange(this.meta, this.recordType, o);
26395 return this.readRecords(o);
26398 // private function a store will implement
26399 onMetaChange : function(meta, recordType, o){
26406 simpleAccess: function(obj, subsc) {
26413 getJsonAccessor: function(){
26415 return function(expr) {
26417 return(re.test(expr))
26418 ? new Function("obj", "return obj." + expr)
26423 return Roo.emptyFn;
26428 * Create a data block containing Roo.data.Records from an XML document.
26429 * @param {Object} o An object which contains an Array of row objects in the property specified
26430 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26431 * which contains the total size of the dataset.
26432 * @return {Object} data A data block which is used by an Roo.data.Store object as
26433 * a cache of Roo.data.Records.
26435 readRecords : function(o){
26437 * After any data loads, the raw JSON data is available for further custom processing.
26441 var s = this.meta, Record = this.recordType,
26442 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26444 // Generate extraction functions for the totalProperty, the root, the id, and for each field
26446 if(s.totalProperty) {
26447 this.getTotal = this.getJsonAccessor(s.totalProperty);
26449 if(s.successProperty) {
26450 this.getSuccess = this.getJsonAccessor(s.successProperty);
26452 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26454 var g = this.getJsonAccessor(s.id);
26455 this.getId = function(rec) {
26457 return (r === undefined || r === "") ? null : r;
26460 this.getId = function(){return null;};
26463 for(var jj = 0; jj < fl; jj++){
26465 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26466 this.ef[jj] = this.getJsonAccessor(map);
26470 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26471 if(s.totalProperty){
26472 var vt = parseInt(this.getTotal(o), 10);
26477 if(s.successProperty){
26478 var vs = this.getSuccess(o);
26479 if(vs === false || vs === 'false'){
26484 for(var i = 0; i < c; i++){
26487 var id = this.getId(n);
26488 for(var j = 0; j < fl; j++){
26490 var v = this.ef[j](n);
26492 Roo.log('missing convert for ' + f.name);
26496 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26500 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26506 var record = new Record(values, id);
26508 records[i] = record;
26514 totalRecords : totalRecords
26517 // used when loading children.. @see loadDataFromChildren
26518 toLoadData: function(rec)
26520 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26521 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26522 return { data : data, total : data.length };
26527 * Ext JS Library 1.1.1
26528 * Copyright(c) 2006-2007, Ext JS, LLC.
26530 * Originally Released Under LGPL - original licence link has changed is not relivant.
26533 * <script type="text/javascript">
26537 * @class Roo.data.XmlReader
26538 * @extends Roo.data.DataReader
26539 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26540 * based on mappings in a provided Roo.data.Record constructor.<br><br>
26542 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26543 * header in the HTTP response must be set to "text/xml".</em>
26547 var RecordDef = Roo.data.Record.create([
26548 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26549 {name: 'occupation'} // This field will use "occupation" as the mapping.
26551 var myReader = new Roo.data.XmlReader({
26552 totalRecords: "results", // The element which contains the total dataset size (optional)
26553 record: "row", // The repeated element which contains row information
26554 id: "id" // The element within the row that provides an ID for the record (optional)
26558 * This would consume an XML file like this:
26562 <results>2</results>
26565 <name>Bill</name>
26566 <occupation>Gardener</occupation>
26570 <name>Ben</name>
26571 <occupation>Horticulturalist</occupation>
26575 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26576 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26577 * paged from the remote server.
26578 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26579 * @cfg {String} success The DomQuery path to the success attribute used by forms.
26580 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26581 * a record identifier value.
26583 * Create a new XmlReader
26584 * @param {Object} meta Metadata configuration options
26585 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
26586 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26587 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
26589 Roo.data.XmlReader = function(meta, recordType){
26591 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26593 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26595 readerType : 'Xml',
26598 * This method is only used by a DataProxy which has retrieved data from a remote server.
26599 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
26600 * to contain a method called 'responseXML' that returns an XML document object.
26601 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26602 * a cache of Roo.data.Records.
26604 read : function(response){
26605 var doc = response.responseXML;
26607 throw {message: "XmlReader.read: XML Document not available"};
26609 return this.readRecords(doc);
26613 * Create a data block containing Roo.data.Records from an XML document.
26614 * @param {Object} doc A parsed XML document.
26615 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26616 * a cache of Roo.data.Records.
26618 readRecords : function(doc){
26620 * After any data loads/reads, the raw XML Document is available for further custom processing.
26621 * @type XMLDocument
26623 this.xmlData = doc;
26624 var root = doc.documentElement || doc;
26625 var q = Roo.DomQuery;
26626 var recordType = this.recordType, fields = recordType.prototype.fields;
26627 var sid = this.meta.id;
26628 var totalRecords = 0, success = true;
26629 if(this.meta.totalRecords){
26630 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26633 if(this.meta.success){
26634 var sv = q.selectValue(this.meta.success, root, true);
26635 success = sv !== false && sv !== 'false';
26638 var ns = q.select(this.meta.record, root);
26639 for(var i = 0, len = ns.length; i < len; i++) {
26642 var id = sid ? q.selectValue(sid, n) : undefined;
26643 for(var j = 0, jlen = fields.length; j < jlen; j++){
26644 var f = fields.items[j];
26645 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26647 values[f.name] = v;
26649 var record = new recordType(values, id);
26651 records[records.length] = record;
26657 totalRecords : totalRecords || records.length
26662 * Ext JS Library 1.1.1
26663 * Copyright(c) 2006-2007, Ext JS, LLC.
26665 * Originally Released Under LGPL - original licence link has changed is not relivant.
26668 * <script type="text/javascript">
26672 * @class Roo.data.ArrayReader
26673 * @extends Roo.data.DataReader
26674 * Data reader class to create an Array of Roo.data.Record objects from an Array.
26675 * Each element of that Array represents a row of data fields. The
26676 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26677 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26681 var RecordDef = Roo.data.Record.create([
26682 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
26683 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
26685 var myReader = new Roo.data.ArrayReader({
26686 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
26690 * This would consume an Array like this:
26692 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26696 * Create a new JsonReader
26697 * @param {Object} meta Metadata configuration options.
26698 * @param {Object|Array} recordType Either an Array of field definition objects
26700 * @cfg {Array} fields Array of field definition objects
26701 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26702 * as specified to {@link Roo.data.Record#create},
26703 * or an {@link Roo.data.Record} object
26706 * created using {@link Roo.data.Record#create}.
26708 Roo.data.ArrayReader = function(meta, recordType)
26710 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26713 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26716 * Create a data block containing Roo.data.Records from an XML document.
26717 * @param {Object} o An Array of row objects which represents the dataset.
26718 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26719 * a cache of Roo.data.Records.
26721 readRecords : function(o)
26723 var sid = this.meta ? this.meta.id : null;
26724 var recordType = this.recordType, fields = recordType.prototype.fields;
26727 for(var i = 0; i < root.length; i++){
26730 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26731 for(var j = 0, jlen = fields.length; j < jlen; j++){
26732 var f = fields.items[j];
26733 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26734 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26736 values[f.name] = v;
26738 var record = new recordType(values, id);
26740 records[records.length] = record;
26744 totalRecords : records.length
26747 // used when loading children.. @see loadDataFromChildren
26748 toLoadData: function(rec)
26750 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26751 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26758 * Ext JS Library 1.1.1
26759 * Copyright(c) 2006-2007, Ext JS, LLC.
26761 * Originally Released Under LGPL - original licence link has changed is not relivant.
26764 * <script type="text/javascript">
26769 * @class Roo.data.Tree
26770 * @extends Roo.util.Observable
26771 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26772 * in the tree have most standard DOM functionality.
26774 * @param {Node} root (optional) The root node
26776 Roo.data.Tree = function(root){
26777 this.nodeHash = {};
26779 * The root node for this tree
26784 this.setRootNode(root);
26789 * Fires when a new child node is appended to a node in this tree.
26790 * @param {Tree} tree The owner tree
26791 * @param {Node} parent The parent node
26792 * @param {Node} node The newly appended node
26793 * @param {Number} index The index of the newly appended node
26798 * Fires when a child node is removed from a node in this tree.
26799 * @param {Tree} tree The owner tree
26800 * @param {Node} parent The parent node
26801 * @param {Node} node The child node removed
26806 * Fires when a node is moved to a new location in the tree
26807 * @param {Tree} tree The owner tree
26808 * @param {Node} node The node moved
26809 * @param {Node} oldParent The old parent of this node
26810 * @param {Node} newParent The new parent of this node
26811 * @param {Number} index The index it was moved to
26816 * Fires when a new child node is inserted in a node in this tree.
26817 * @param {Tree} tree The owner tree
26818 * @param {Node} parent The parent node
26819 * @param {Node} node The child node inserted
26820 * @param {Node} refNode The child node the node was inserted before
26824 * @event beforeappend
26825 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26826 * @param {Tree} tree The owner tree
26827 * @param {Node} parent The parent node
26828 * @param {Node} node The child node to be appended
26830 "beforeappend" : true,
26832 * @event beforeremove
26833 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26834 * @param {Tree} tree The owner tree
26835 * @param {Node} parent The parent node
26836 * @param {Node} node The child node to be removed
26838 "beforeremove" : true,
26840 * @event beforemove
26841 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26842 * @param {Tree} tree The owner tree
26843 * @param {Node} node The node being moved
26844 * @param {Node} oldParent The parent of the node
26845 * @param {Node} newParent The new parent the node is moving to
26846 * @param {Number} index The index it is being moved to
26848 "beforemove" : true,
26850 * @event beforeinsert
26851 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26852 * @param {Tree} tree The owner tree
26853 * @param {Node} parent The parent node
26854 * @param {Node} node The child node to be inserted
26855 * @param {Node} refNode The child node the node is being inserted before
26857 "beforeinsert" : true
26860 Roo.data.Tree.superclass.constructor.call(this);
26863 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26864 pathSeparator: "/",
26866 proxyNodeEvent : function(){
26867 return this.fireEvent.apply(this, arguments);
26871 * Returns the root node for this tree.
26874 getRootNode : function(){
26879 * Sets the root node for this tree.
26880 * @param {Node} node
26883 setRootNode : function(node){
26885 node.ownerTree = this;
26886 node.isRoot = true;
26887 this.registerNode(node);
26892 * Gets a node in this tree by its id.
26893 * @param {String} id
26896 getNodeById : function(id){
26897 return this.nodeHash[id];
26900 registerNode : function(node){
26901 this.nodeHash[node.id] = node;
26904 unregisterNode : function(node){
26905 delete this.nodeHash[node.id];
26908 toString : function(){
26909 return "[Tree"+(this.id?" "+this.id:"")+"]";
26914 * @class Roo.data.Node
26915 * @extends Roo.util.Observable
26916 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26917 * @cfg {String} id The id for this node. If one is not specified, one is generated.
26919 * @param {Object} attributes The attributes/config for the node
26921 Roo.data.Node = function(attributes){
26923 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26926 this.attributes = attributes || {};
26927 this.leaf = this.attributes.leaf;
26929 * The node id. @type String
26931 this.id = this.attributes.id;
26933 this.id = Roo.id(null, "ynode-");
26934 this.attributes.id = this.id;
26939 * All child nodes of this node. @type Array
26941 this.childNodes = [];
26942 if(!this.childNodes.indexOf){ // indexOf is a must
26943 this.childNodes.indexOf = function(o){
26944 for(var i = 0, len = this.length; i < len; i++){
26953 * The parent node for this node. @type Node
26955 this.parentNode = null;
26957 * The first direct child node of this node, or null if this node has no child nodes. @type Node
26959 this.firstChild = null;
26961 * The last direct child node of this node, or null if this node has no child nodes. @type Node
26963 this.lastChild = null;
26965 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26967 this.previousSibling = null;
26969 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26971 this.nextSibling = null;
26976 * Fires when a new child node is appended
26977 * @param {Tree} tree The owner tree
26978 * @param {Node} this This node
26979 * @param {Node} node The newly appended node
26980 * @param {Number} index The index of the newly appended node
26985 * Fires when a child node is removed
26986 * @param {Tree} tree The owner tree
26987 * @param {Node} this This node
26988 * @param {Node} node The removed node
26993 * Fires when this node is moved to a new location in the tree
26994 * @param {Tree} tree The owner tree
26995 * @param {Node} this This node
26996 * @param {Node} oldParent The old parent of this node
26997 * @param {Node} newParent The new parent of this node
26998 * @param {Number} index The index it was moved to
27003 * Fires when a new child node is inserted.
27004 * @param {Tree} tree The owner tree
27005 * @param {Node} this This node
27006 * @param {Node} node The child node inserted
27007 * @param {Node} refNode The child node the node was inserted before
27011 * @event beforeappend
27012 * Fires before a new child is appended, return false to cancel the append.
27013 * @param {Tree} tree The owner tree
27014 * @param {Node} this This node
27015 * @param {Node} node The child node to be appended
27017 "beforeappend" : true,
27019 * @event beforeremove
27020 * Fires before a child is removed, return false to cancel the remove.
27021 * @param {Tree} tree The owner tree
27022 * @param {Node} this This node
27023 * @param {Node} node The child node to be removed
27025 "beforeremove" : true,
27027 * @event beforemove
27028 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27029 * @param {Tree} tree The owner tree
27030 * @param {Node} this This node
27031 * @param {Node} oldParent The parent of this node
27032 * @param {Node} newParent The new parent this node is moving to
27033 * @param {Number} index The index it is being moved to
27035 "beforemove" : true,
27037 * @event beforeinsert
27038 * Fires before a new child is inserted, return false to cancel the insert.
27039 * @param {Tree} tree The owner tree
27040 * @param {Node} this This node
27041 * @param {Node} node The child node to be inserted
27042 * @param {Node} refNode The child node the node is being inserted before
27044 "beforeinsert" : true
27046 this.listeners = this.attributes.listeners;
27047 Roo.data.Node.superclass.constructor.call(this);
27050 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27051 fireEvent : function(evtName){
27052 // first do standard event for this node
27053 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27056 // then bubble it up to the tree if the event wasn't cancelled
27057 var ot = this.getOwnerTree();
27059 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27067 * Returns true if this node is a leaf
27068 * @return {Boolean}
27070 isLeaf : function(){
27071 return this.leaf === true;
27075 setFirstChild : function(node){
27076 this.firstChild = node;
27080 setLastChild : function(node){
27081 this.lastChild = node;
27086 * Returns true if this node is the last child of its parent
27087 * @return {Boolean}
27089 isLast : function(){
27090 return (!this.parentNode ? true : this.parentNode.lastChild == this);
27094 * Returns true if this node is the first child of its parent
27095 * @return {Boolean}
27097 isFirst : function(){
27098 return (!this.parentNode ? true : this.parentNode.firstChild == this);
27101 hasChildNodes : function(){
27102 return !this.isLeaf() && this.childNodes.length > 0;
27106 * Insert node(s) as the last child node of this node.
27107 * @param {Node/Array} node The node or Array of nodes to append
27108 * @return {Node} The appended node if single append, or null if an array was passed
27110 appendChild : function(node){
27112 if(node instanceof Array){
27114 }else if(arguments.length > 1){
27118 // if passed an array or multiple args do them one by one
27120 for(var i = 0, len = multi.length; i < len; i++) {
27121 this.appendChild(multi[i]);
27124 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27127 var index = this.childNodes.length;
27128 var oldParent = node.parentNode;
27129 // it's a move, make sure we move it cleanly
27131 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27134 oldParent.removeChild(node);
27137 index = this.childNodes.length;
27139 this.setFirstChild(node);
27141 this.childNodes.push(node);
27142 node.parentNode = this;
27143 var ps = this.childNodes[index-1];
27145 node.previousSibling = ps;
27146 ps.nextSibling = node;
27148 node.previousSibling = null;
27150 node.nextSibling = null;
27151 this.setLastChild(node);
27152 node.setOwnerTree(this.getOwnerTree());
27153 this.fireEvent("append", this.ownerTree, this, node, index);
27154 if(this.ownerTree) {
27155 this.ownerTree.fireEvent("appendnode", this, node, index);
27158 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27165 * Removes a child node from this node.
27166 * @param {Node} node The node to remove
27167 * @return {Node} The removed node
27169 removeChild : function(node){
27170 var index = this.childNodes.indexOf(node);
27174 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27178 // remove it from childNodes collection
27179 this.childNodes.splice(index, 1);
27182 if(node.previousSibling){
27183 node.previousSibling.nextSibling = node.nextSibling;
27185 if(node.nextSibling){
27186 node.nextSibling.previousSibling = node.previousSibling;
27189 // update child refs
27190 if(this.firstChild == node){
27191 this.setFirstChild(node.nextSibling);
27193 if(this.lastChild == node){
27194 this.setLastChild(node.previousSibling);
27197 node.setOwnerTree(null);
27198 // clear any references from the node
27199 node.parentNode = null;
27200 node.previousSibling = null;
27201 node.nextSibling = null;
27202 this.fireEvent("remove", this.ownerTree, this, node);
27207 * Inserts the first node before the second node in this nodes childNodes collection.
27208 * @param {Node} node The node to insert
27209 * @param {Node} refNode The node to insert before (if null the node is appended)
27210 * @return {Node} The inserted node
27212 insertBefore : function(node, refNode){
27213 if(!refNode){ // like standard Dom, refNode can be null for append
27214 return this.appendChild(node);
27217 if(node == refNode){
27221 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27224 var index = this.childNodes.indexOf(refNode);
27225 var oldParent = node.parentNode;
27226 var refIndex = index;
27228 // when moving internally, indexes will change after remove
27229 if(oldParent == this && this.childNodes.indexOf(node) < index){
27233 // it's a move, make sure we move it cleanly
27235 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27238 oldParent.removeChild(node);
27241 this.setFirstChild(node);
27243 this.childNodes.splice(refIndex, 0, node);
27244 node.parentNode = this;
27245 var ps = this.childNodes[refIndex-1];
27247 node.previousSibling = ps;
27248 ps.nextSibling = node;
27250 node.previousSibling = null;
27252 node.nextSibling = refNode;
27253 refNode.previousSibling = node;
27254 node.setOwnerTree(this.getOwnerTree());
27255 this.fireEvent("insert", this.ownerTree, this, node, refNode);
27257 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27263 * Returns the child node at the specified index.
27264 * @param {Number} index
27267 item : function(index){
27268 return this.childNodes[index];
27272 * Replaces one child node in this node with another.
27273 * @param {Node} newChild The replacement node
27274 * @param {Node} oldChild The node to replace
27275 * @return {Node} The replaced node
27277 replaceChild : function(newChild, oldChild){
27278 this.insertBefore(newChild, oldChild);
27279 this.removeChild(oldChild);
27284 * Returns the index of a child node
27285 * @param {Node} node
27286 * @return {Number} The index of the node or -1 if it was not found
27288 indexOf : function(child){
27289 return this.childNodes.indexOf(child);
27293 * Returns the tree this node is in.
27296 getOwnerTree : function(){
27297 // if it doesn't have one, look for one
27298 if(!this.ownerTree){
27302 this.ownerTree = p.ownerTree;
27308 return this.ownerTree;
27312 * Returns depth of this node (the root node has a depth of 0)
27315 getDepth : function(){
27318 while(p.parentNode){
27326 setOwnerTree : function(tree){
27327 // if it's move, we need to update everyone
27328 if(tree != this.ownerTree){
27329 if(this.ownerTree){
27330 this.ownerTree.unregisterNode(this);
27332 this.ownerTree = tree;
27333 var cs = this.childNodes;
27334 for(var i = 0, len = cs.length; i < len; i++) {
27335 cs[i].setOwnerTree(tree);
27338 tree.registerNode(this);
27344 * Returns the path for this node. The path can be used to expand or select this node programmatically.
27345 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27346 * @return {String} The path
27348 getPath : function(attr){
27349 attr = attr || "id";
27350 var p = this.parentNode;
27351 var b = [this.attributes[attr]];
27353 b.unshift(p.attributes[attr]);
27356 var sep = this.getOwnerTree().pathSeparator;
27357 return sep + b.join(sep);
27361 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27362 * function call will be the scope provided or the current node. The arguments to the function
27363 * will be the args provided or the current node. If the function returns false at any point,
27364 * the bubble is stopped.
27365 * @param {Function} fn The function to call
27366 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27367 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27369 bubble : function(fn, scope, args){
27372 if(fn.call(scope || p, args || p) === false){
27380 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27381 * function call will be the scope provided or the current node. The arguments to the function
27382 * will be the args provided or the current node. If the function returns false at any point,
27383 * the cascade is stopped on that branch.
27384 * @param {Function} fn The function to call
27385 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27386 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27388 cascade : function(fn, scope, args){
27389 if(fn.call(scope || this, args || this) !== false){
27390 var cs = this.childNodes;
27391 for(var i = 0, len = cs.length; i < len; i++) {
27392 cs[i].cascade(fn, scope, args);
27398 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27399 * function call will be the scope provided or the current node. The arguments to the function
27400 * will be the args provided or the current node. If the function returns false at any point,
27401 * the iteration stops.
27402 * @param {Function} fn The function to call
27403 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27404 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27406 eachChild : function(fn, scope, args){
27407 var cs = this.childNodes;
27408 for(var i = 0, len = cs.length; i < len; i++) {
27409 if(fn.call(scope || this, args || cs[i]) === false){
27416 * Finds the first child that has the attribute with the specified value.
27417 * @param {String} attribute The attribute name
27418 * @param {Mixed} value The value to search for
27419 * @return {Node} The found child or null if none was found
27421 findChild : function(attribute, value){
27422 var cs = this.childNodes;
27423 for(var i = 0, len = cs.length; i < len; i++) {
27424 if(cs[i].attributes[attribute] == value){
27432 * Finds the first child by a custom function. The child matches if the function passed
27434 * @param {Function} fn
27435 * @param {Object} scope (optional)
27436 * @return {Node} The found child or null if none was found
27438 findChildBy : function(fn, scope){
27439 var cs = this.childNodes;
27440 for(var i = 0, len = cs.length; i < len; i++) {
27441 if(fn.call(scope||cs[i], cs[i]) === true){
27449 * Sorts this nodes children using the supplied sort function
27450 * @param {Function} fn
27451 * @param {Object} scope (optional)
27453 sort : function(fn, scope){
27454 var cs = this.childNodes;
27455 var len = cs.length;
27457 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27459 for(var i = 0; i < len; i++){
27461 n.previousSibling = cs[i-1];
27462 n.nextSibling = cs[i+1];
27464 this.setFirstChild(n);
27467 this.setLastChild(n);
27474 * Returns true if this node is an ancestor (at any point) of the passed node.
27475 * @param {Node} node
27476 * @return {Boolean}
27478 contains : function(node){
27479 return node.isAncestor(this);
27483 * Returns true if the passed node is an ancestor (at any point) of this node.
27484 * @param {Node} node
27485 * @return {Boolean}
27487 isAncestor : function(node){
27488 var p = this.parentNode;
27498 toString : function(){
27499 return "[Node"+(this.id?" "+this.id:"")+"]";
27503 * Ext JS Library 1.1.1
27504 * Copyright(c) 2006-2007, Ext JS, LLC.
27506 * Originally Released Under LGPL - original licence link has changed is not relivant.
27509 * <script type="text/javascript">
27514 * @class Roo.Shadow
27515 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
27516 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
27517 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27519 * Create a new Shadow
27520 * @param {Object} config The config object
27522 Roo.Shadow = function(config){
27523 Roo.apply(this, config);
27524 if(typeof this.mode != "string"){
27525 this.mode = this.defaultMode;
27527 var o = this.offset, a = {h: 0};
27528 var rad = Math.floor(this.offset/2);
27529 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27535 a.l -= this.offset + rad;
27536 a.t -= this.offset + rad;
27547 a.l -= (this.offset - rad);
27548 a.t -= this.offset + rad;
27550 a.w -= (this.offset - rad)*2;
27561 a.l -= (this.offset - rad);
27562 a.t -= (this.offset - rad);
27564 a.w -= (this.offset + rad + 1);
27565 a.h -= (this.offset + rad);
27574 Roo.Shadow.prototype = {
27576 * @cfg {String} mode
27577 * The shadow display mode. Supports the following options:<br />
27578 * sides: Shadow displays on both sides and bottom only<br />
27579 * frame: Shadow displays equally on all four sides<br />
27580 * drop: Traditional bottom-right drop shadow (default)
27584 * @cfg {String} offset
27585 * The number of pixels to offset the shadow from the element (defaults to 4)
27590 defaultMode: "drop",
27593 * Displays the shadow under the target element
27594 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27596 show : function(target){
27597 target = Roo.get(target);
27599 this.el = Roo.Shadow.Pool.pull();
27600 if(this.el.dom.nextSibling != target.dom){
27601 this.el.insertBefore(target);
27604 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27606 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27609 target.getLeft(true),
27610 target.getTop(true),
27614 this.el.dom.style.display = "block";
27618 * Returns true if the shadow is visible, else false
27620 isVisible : function(){
27621 return this.el ? true : false;
27625 * Direct alignment when values are already available. Show must be called at least once before
27626 * calling this method to ensure it is initialized.
27627 * @param {Number} left The target element left position
27628 * @param {Number} top The target element top position
27629 * @param {Number} width The target element width
27630 * @param {Number} height The target element height
27632 realign : function(l, t, w, h){
27636 var a = this.adjusts, d = this.el.dom, s = d.style;
27638 s.left = (l+a.l)+"px";
27639 s.top = (t+a.t)+"px";
27640 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27642 if(s.width != sws || s.height != shs){
27646 var cn = d.childNodes;
27647 var sww = Math.max(0, (sw-12))+"px";
27648 cn[0].childNodes[1].style.width = sww;
27649 cn[1].childNodes[1].style.width = sww;
27650 cn[2].childNodes[1].style.width = sww;
27651 cn[1].style.height = Math.max(0, (sh-12))+"px";
27657 * Hides this shadow
27661 this.el.dom.style.display = "none";
27662 Roo.Shadow.Pool.push(this.el);
27668 * Adjust the z-index of this shadow
27669 * @param {Number} zindex The new z-index
27671 setZIndex : function(z){
27674 this.el.setStyle("z-index", z);
27679 // Private utility class that manages the internal Shadow cache
27680 Roo.Shadow.Pool = function(){
27682 var markup = Roo.isIE ?
27683 '<div class="x-ie-shadow"></div>' :
27684 '<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>';
27687 var sh = p.shift();
27689 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27690 sh.autoBoxAdjust = false;
27695 push : function(sh){
27701 * Ext JS Library 1.1.1
27702 * Copyright(c) 2006-2007, Ext JS, LLC.
27704 * Originally Released Under LGPL - original licence link has changed is not relivant.
27707 * <script type="text/javascript">
27712 * @class Roo.SplitBar
27713 * @extends Roo.util.Observable
27714 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27718 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27719 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27720 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27721 split.minSize = 100;
27722 split.maxSize = 600;
27723 split.animate = true;
27724 split.on('moved', splitterMoved);
27727 * Create a new SplitBar
27728 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
27729 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
27730 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27731 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
27732 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27733 position of the SplitBar).
27735 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27738 this.el = Roo.get(dragElement, true);
27739 this.el.dom.unselectable = "on";
27741 this.resizingEl = Roo.get(resizingElement, true);
27745 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27746 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27749 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27752 * The minimum size of the resizing element. (Defaults to 0)
27758 * The maximum size of the resizing element. (Defaults to 2000)
27761 this.maxSize = 2000;
27764 * Whether to animate the transition to the new size
27767 this.animate = false;
27770 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27773 this.useShim = false;
27778 if(!existingProxy){
27780 this.proxy = Roo.SplitBar.createProxy(this.orientation);
27782 this.proxy = Roo.get(existingProxy).dom;
27785 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27788 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27791 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27794 this.dragSpecs = {};
27797 * @private The adapter to use to positon and resize elements
27799 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27800 this.adapter.init(this);
27802 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27804 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27805 this.el.addClass("x-splitbar-h");
27808 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27809 this.el.addClass("x-splitbar-v");
27815 * Fires when the splitter is moved (alias for {@link #event-moved})
27816 * @param {Roo.SplitBar} this
27817 * @param {Number} newSize the new width or height
27822 * Fires when the splitter is moved
27823 * @param {Roo.SplitBar} this
27824 * @param {Number} newSize the new width or height
27828 * @event beforeresize
27829 * Fires before the splitter is dragged
27830 * @param {Roo.SplitBar} this
27832 "beforeresize" : true,
27834 "beforeapply" : true
27837 Roo.util.Observable.call(this);
27840 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27841 onStartProxyDrag : function(x, y){
27842 this.fireEvent("beforeresize", this);
27844 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
27846 o.enableDisplayMode("block");
27847 // all splitbars share the same overlay
27848 Roo.SplitBar.prototype.overlay = o;
27850 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27851 this.overlay.show();
27852 Roo.get(this.proxy).setDisplayed("block");
27853 var size = this.adapter.getElementSize(this);
27854 this.activeMinSize = this.getMinimumSize();;
27855 this.activeMaxSize = this.getMaximumSize();;
27856 var c1 = size - this.activeMinSize;
27857 var c2 = Math.max(this.activeMaxSize - size, 0);
27858 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27859 this.dd.resetConstraints();
27860 this.dd.setXConstraint(
27861 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
27862 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27864 this.dd.setYConstraint(0, 0);
27866 this.dd.resetConstraints();
27867 this.dd.setXConstraint(0, 0);
27868 this.dd.setYConstraint(
27869 this.placement == Roo.SplitBar.TOP ? c1 : c2,
27870 this.placement == Roo.SplitBar.TOP ? c2 : c1
27873 this.dragSpecs.startSize = size;
27874 this.dragSpecs.startPoint = [x, y];
27875 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27879 * @private Called after the drag operation by the DDProxy
27881 onEndProxyDrag : function(e){
27882 Roo.get(this.proxy).setDisplayed(false);
27883 var endPoint = Roo.lib.Event.getXY(e);
27885 this.overlay.hide();
27888 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27889 newSize = this.dragSpecs.startSize +
27890 (this.placement == Roo.SplitBar.LEFT ?
27891 endPoint[0] - this.dragSpecs.startPoint[0] :
27892 this.dragSpecs.startPoint[0] - endPoint[0]
27895 newSize = this.dragSpecs.startSize +
27896 (this.placement == Roo.SplitBar.TOP ?
27897 endPoint[1] - this.dragSpecs.startPoint[1] :
27898 this.dragSpecs.startPoint[1] - endPoint[1]
27901 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27902 if(newSize != this.dragSpecs.startSize){
27903 if(this.fireEvent('beforeapply', this, newSize) !== false){
27904 this.adapter.setElementSize(this, newSize);
27905 this.fireEvent("moved", this, newSize);
27906 this.fireEvent("resize", this, newSize);
27912 * Get the adapter this SplitBar uses
27913 * @return The adapter object
27915 getAdapter : function(){
27916 return this.adapter;
27920 * Set the adapter this SplitBar uses
27921 * @param {Object} adapter A SplitBar adapter object
27923 setAdapter : function(adapter){
27924 this.adapter = adapter;
27925 this.adapter.init(this);
27929 * Gets the minimum size for the resizing element
27930 * @return {Number} The minimum size
27932 getMinimumSize : function(){
27933 return this.minSize;
27937 * Sets the minimum size for the resizing element
27938 * @param {Number} minSize The minimum size
27940 setMinimumSize : function(minSize){
27941 this.minSize = minSize;
27945 * Gets the maximum size for the resizing element
27946 * @return {Number} The maximum size
27948 getMaximumSize : function(){
27949 return this.maxSize;
27953 * Sets the maximum size for the resizing element
27954 * @param {Number} maxSize The maximum size
27956 setMaximumSize : function(maxSize){
27957 this.maxSize = maxSize;
27961 * Sets the initialize size for the resizing element
27962 * @param {Number} size The initial size
27964 setCurrentSize : function(size){
27965 var oldAnimate = this.animate;
27966 this.animate = false;
27967 this.adapter.setElementSize(this, size);
27968 this.animate = oldAnimate;
27972 * Destroy this splitbar.
27973 * @param {Boolean} removeEl True to remove the element
27975 destroy : function(removeEl){
27977 this.shim.remove();
27980 this.proxy.parentNode.removeChild(this.proxy);
27988 * @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.
27990 Roo.SplitBar.createProxy = function(dir){
27991 var proxy = new Roo.Element(document.createElement("div"));
27992 proxy.unselectable();
27993 var cls = 'x-splitbar-proxy';
27994 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27995 document.body.appendChild(proxy.dom);
28000 * @class Roo.SplitBar.BasicLayoutAdapter
28001 * Default Adapter. It assumes the splitter and resizing element are not positioned
28002 * elements and only gets/sets the width of the element. Generally used for table based layouts.
28004 Roo.SplitBar.BasicLayoutAdapter = function(){
28007 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28008 // do nothing for now
28009 init : function(s){
28013 * Called before drag operations to get the current size of the resizing element.
28014 * @param {Roo.SplitBar} s The SplitBar using this adapter
28016 getElementSize : function(s){
28017 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28018 return s.resizingEl.getWidth();
28020 return s.resizingEl.getHeight();
28025 * Called after drag operations to set the size of the resizing element.
28026 * @param {Roo.SplitBar} s The SplitBar using this adapter
28027 * @param {Number} newSize The new size to set
28028 * @param {Function} onComplete A function to be invoked when resizing is complete
28030 setElementSize : function(s, newSize, onComplete){
28031 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28033 s.resizingEl.setWidth(newSize);
28035 onComplete(s, newSize);
28038 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28043 s.resizingEl.setHeight(newSize);
28045 onComplete(s, newSize);
28048 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28055 *@class Roo.SplitBar.AbsoluteLayoutAdapter
28056 * @extends Roo.SplitBar.BasicLayoutAdapter
28057 * Adapter that moves the splitter element to align with the resized sizing element.
28058 * Used with an absolute positioned SplitBar.
28059 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28060 * document.body, make sure you assign an id to the body element.
28062 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28063 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28064 this.container = Roo.get(container);
28067 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28068 init : function(s){
28069 this.basic.init(s);
28072 getElementSize : function(s){
28073 return this.basic.getElementSize(s);
28076 setElementSize : function(s, newSize, onComplete){
28077 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28080 moveSplitter : function(s){
28081 var yes = Roo.SplitBar;
28082 switch(s.placement){
28084 s.el.setX(s.resizingEl.getRight());
28087 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28090 s.el.setY(s.resizingEl.getBottom());
28093 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28100 * Orientation constant - Create a vertical SplitBar
28104 Roo.SplitBar.VERTICAL = 1;
28107 * Orientation constant - Create a horizontal SplitBar
28111 Roo.SplitBar.HORIZONTAL = 2;
28114 * Placement constant - The resizing element is to the left of the splitter element
28118 Roo.SplitBar.LEFT = 1;
28121 * Placement constant - The resizing element is to the right of the splitter element
28125 Roo.SplitBar.RIGHT = 2;
28128 * Placement constant - The resizing element is positioned above the splitter element
28132 Roo.SplitBar.TOP = 3;
28135 * Placement constant - The resizing element is positioned under splitter element
28139 Roo.SplitBar.BOTTOM = 4;
28142 * Ext JS Library 1.1.1
28143 * Copyright(c) 2006-2007, Ext JS, LLC.
28145 * Originally Released Under LGPL - original licence link has changed is not relivant.
28148 * <script type="text/javascript">
28153 * @extends Roo.util.Observable
28154 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
28155 * This class also supports single and multi selection modes. <br>
28156 * Create a data model bound view:
28158 var store = new Roo.data.Store(...);
28160 var view = new Roo.View({
28162 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
28164 singleSelect: true,
28165 selectedClass: "ydataview-selected",
28169 // listen for node click?
28170 view.on("click", function(vw, index, node, e){
28171 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28175 dataModel.load("foobar.xml");
28177 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28179 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28180 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28182 * Note: old style constructor is still suported (container, template, config)
28185 * Create a new View
28186 * @param {Object} config The config object
28189 Roo.View = function(config, depreciated_tpl, depreciated_config){
28191 this.parent = false;
28193 if (typeof(depreciated_tpl) == 'undefined') {
28194 // new way.. - universal constructor.
28195 Roo.apply(this, config);
28196 this.el = Roo.get(this.el);
28199 this.el = Roo.get(config);
28200 this.tpl = depreciated_tpl;
28201 Roo.apply(this, depreciated_config);
28203 this.wrapEl = this.el.wrap().wrap();
28204 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28207 if(typeof(this.tpl) == "string"){
28208 this.tpl = new Roo.Template(this.tpl);
28210 // support xtype ctors..
28211 this.tpl = new Roo.factory(this.tpl, Roo);
28215 this.tpl.compile();
28220 * @event beforeclick
28221 * Fires before a click is processed. Returns false to cancel the default action.
28222 * @param {Roo.View} this
28223 * @param {Number} index The index of the target node
28224 * @param {HTMLElement} node The target node
28225 * @param {Roo.EventObject} e The raw event object
28227 "beforeclick" : true,
28230 * Fires when a template node is clicked.
28231 * @param {Roo.View} this
28232 * @param {Number} index The index of the target node
28233 * @param {HTMLElement} node The target node
28234 * @param {Roo.EventObject} e The raw event object
28239 * Fires when a template node is double clicked.
28240 * @param {Roo.View} this
28241 * @param {Number} index The index of the target node
28242 * @param {HTMLElement} node The target node
28243 * @param {Roo.EventObject} e The raw event object
28247 * @event contextmenu
28248 * Fires when a template node is right clicked.
28249 * @param {Roo.View} this
28250 * @param {Number} index The index of the target node
28251 * @param {HTMLElement} node The target node
28252 * @param {Roo.EventObject} e The raw event object
28254 "contextmenu" : true,
28256 * @event selectionchange
28257 * Fires when the selected nodes change.
28258 * @param {Roo.View} this
28259 * @param {Array} selections Array of the selected nodes
28261 "selectionchange" : true,
28264 * @event beforeselect
28265 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28266 * @param {Roo.View} this
28267 * @param {HTMLElement} node The node to be selected
28268 * @param {Array} selections Array of currently selected nodes
28270 "beforeselect" : true,
28272 * @event preparedata
28273 * Fires on every row to render, to allow you to change the data.
28274 * @param {Roo.View} this
28275 * @param {Object} data to be rendered (change this)
28277 "preparedata" : true
28285 "click": this.onClick,
28286 "dblclick": this.onDblClick,
28287 "contextmenu": this.onContextMenu,
28291 this.selections = [];
28293 this.cmp = new Roo.CompositeElementLite([]);
28295 this.store = Roo.factory(this.store, Roo.data);
28296 this.setStore(this.store, true);
28299 if ( this.footer && this.footer.xtype) {
28301 var fctr = this.wrapEl.appendChild(document.createElement("div"));
28303 this.footer.dataSource = this.store;
28304 this.footer.container = fctr;
28305 this.footer = Roo.factory(this.footer, Roo);
28306 fctr.insertFirst(this.el);
28308 // this is a bit insane - as the paging toolbar seems to detach the el..
28309 // dom.parentNode.parentNode.parentNode
28310 // they get detached?
28314 Roo.View.superclass.constructor.call(this);
28319 Roo.extend(Roo.View, Roo.util.Observable, {
28322 * @cfg {Roo.data.Store} store Data store to load data from.
28327 * @cfg {String|Roo.Element} el The container element.
28332 * @cfg {String|Roo.Template} tpl The template used by this View
28336 * @cfg {String} dataName the named area of the template to use as the data area
28337 * Works with domtemplates roo-name="name"
28341 * @cfg {String} selectedClass The css class to add to selected nodes
28343 selectedClass : "x-view-selected",
28345 * @cfg {String} emptyText The empty text to show when nothing is loaded.
28350 * @cfg {String} text to display on mask (default Loading)
28354 * @cfg {Boolean} multiSelect Allow multiple selection
28356 multiSelect : false,
28358 * @cfg {Boolean} singleSelect Allow single selection
28360 singleSelect: false,
28363 * @cfg {Boolean} toggleSelect - selecting
28365 toggleSelect : false,
28368 * @cfg {Boolean} tickable - selecting
28373 * Returns the element this view is bound to.
28374 * @return {Roo.Element}
28376 getEl : function(){
28377 return this.wrapEl;
28383 * Refreshes the view. - called by datachanged on the store. - do not call directly.
28385 refresh : function(){
28386 //Roo.log('refresh');
28389 // if we are using something like 'domtemplate', then
28390 // the what gets used is:
28391 // t.applySubtemplate(NAME, data, wrapping data..)
28392 // the outer template then get' applied with
28393 // the store 'extra data'
28394 // and the body get's added to the
28395 // roo-name="data" node?
28396 // <span class='roo-tpl-{name}'></span> ?????
28400 this.clearSelections();
28401 this.el.update("");
28403 var records = this.store.getRange();
28404 if(records.length < 1) {
28406 // is this valid?? = should it render a template??
28408 this.el.update(this.emptyText);
28412 if (this.dataName) {
28413 this.el.update(t.apply(this.store.meta)); //????
28414 el = this.el.child('.roo-tpl-' + this.dataName);
28417 for(var i = 0, len = records.length; i < len; i++){
28418 var data = this.prepareData(records[i].data, i, records[i]);
28419 this.fireEvent("preparedata", this, data, i, records[i]);
28421 var d = Roo.apply({}, data);
28424 Roo.apply(d, {'roo-id' : Roo.id()});
28428 Roo.each(this.parent.item, function(item){
28429 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28432 Roo.apply(d, {'roo-data-checked' : 'checked'});
28436 html[html.length] = Roo.util.Format.trim(
28438 t.applySubtemplate(this.dataName, d, this.store.meta) :
28445 el.update(html.join(""));
28446 this.nodes = el.dom.childNodes;
28447 this.updateIndexes(0);
28452 * Function to override to reformat the data that is sent to
28453 * the template for each node.
28454 * DEPRICATED - use the preparedata event handler.
28455 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28456 * a JSON object for an UpdateManager bound view).
28458 prepareData : function(data, index, record)
28460 this.fireEvent("preparedata", this, data, index, record);
28464 onUpdate : function(ds, record){
28465 // Roo.log('on update');
28466 this.clearSelections();
28467 var index = this.store.indexOf(record);
28468 var n = this.nodes[index];
28469 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28470 n.parentNode.removeChild(n);
28471 this.updateIndexes(index, index);
28477 onAdd : function(ds, records, index)
28479 //Roo.log(['on Add', ds, records, index] );
28480 this.clearSelections();
28481 if(this.nodes.length == 0){
28485 var n = this.nodes[index];
28486 for(var i = 0, len = records.length; i < len; i++){
28487 var d = this.prepareData(records[i].data, i, records[i]);
28489 this.tpl.insertBefore(n, d);
28492 this.tpl.append(this.el, d);
28495 this.updateIndexes(index);
28498 onRemove : function(ds, record, index){
28499 // Roo.log('onRemove');
28500 this.clearSelections();
28501 var el = this.dataName ?
28502 this.el.child('.roo-tpl-' + this.dataName) :
28505 el.dom.removeChild(this.nodes[index]);
28506 this.updateIndexes(index);
28510 * Refresh an individual node.
28511 * @param {Number} index
28513 refreshNode : function(index){
28514 this.onUpdate(this.store, this.store.getAt(index));
28517 updateIndexes : function(startIndex, endIndex){
28518 var ns = this.nodes;
28519 startIndex = startIndex || 0;
28520 endIndex = endIndex || ns.length - 1;
28521 for(var i = startIndex; i <= endIndex; i++){
28522 ns[i].nodeIndex = i;
28527 * Changes the data store this view uses and refresh the view.
28528 * @param {Store} store
28530 setStore : function(store, initial){
28531 if(!initial && this.store){
28532 this.store.un("datachanged", this.refresh);
28533 this.store.un("add", this.onAdd);
28534 this.store.un("remove", this.onRemove);
28535 this.store.un("update", this.onUpdate);
28536 this.store.un("clear", this.refresh);
28537 this.store.un("beforeload", this.onBeforeLoad);
28538 this.store.un("load", this.onLoad);
28539 this.store.un("loadexception", this.onLoad);
28543 store.on("datachanged", this.refresh, this);
28544 store.on("add", this.onAdd, this);
28545 store.on("remove", this.onRemove, this);
28546 store.on("update", this.onUpdate, this);
28547 store.on("clear", this.refresh, this);
28548 store.on("beforeload", this.onBeforeLoad, this);
28549 store.on("load", this.onLoad, this);
28550 store.on("loadexception", this.onLoad, this);
28558 * onbeforeLoad - masks the loading area.
28561 onBeforeLoad : function(store,opts)
28563 //Roo.log('onBeforeLoad');
28565 this.el.update("");
28567 this.el.mask(this.mask ? this.mask : "Loading" );
28569 onLoad : function ()
28576 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28577 * @param {HTMLElement} node
28578 * @return {HTMLElement} The template node
28580 findItemFromChild : function(node){
28581 var el = this.dataName ?
28582 this.el.child('.roo-tpl-' + this.dataName,true) :
28585 if(!node || node.parentNode == el){
28588 var p = node.parentNode;
28589 while(p && p != el){
28590 if(p.parentNode == el){
28599 onClick : function(e){
28600 var item = this.findItemFromChild(e.getTarget());
28602 var index = this.indexOf(item);
28603 if(this.onItemClick(item, index, e) !== false){
28604 this.fireEvent("click", this, index, item, e);
28607 this.clearSelections();
28612 onContextMenu : function(e){
28613 var item = this.findItemFromChild(e.getTarget());
28615 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28620 onDblClick : function(e){
28621 var item = this.findItemFromChild(e.getTarget());
28623 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28627 onItemClick : function(item, index, e)
28629 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28632 if (this.toggleSelect) {
28633 var m = this.isSelected(item) ? 'unselect' : 'select';
28636 _t[m](item, true, false);
28639 if(this.multiSelect || this.singleSelect){
28640 if(this.multiSelect && e.shiftKey && this.lastSelection){
28641 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28643 this.select(item, this.multiSelect && e.ctrlKey);
28644 this.lastSelection = item;
28647 if(!this.tickable){
28648 e.preventDefault();
28656 * Get the number of selected nodes.
28659 getSelectionCount : function(){
28660 return this.selections.length;
28664 * Get the currently selected nodes.
28665 * @return {Array} An array of HTMLElements
28667 getSelectedNodes : function(){
28668 return this.selections;
28672 * Get the indexes of the selected nodes.
28675 getSelectedIndexes : function(){
28676 var indexes = [], s = this.selections;
28677 for(var i = 0, len = s.length; i < len; i++){
28678 indexes.push(s[i].nodeIndex);
28684 * Clear all selections
28685 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28687 clearSelections : function(suppressEvent){
28688 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28689 this.cmp.elements = this.selections;
28690 this.cmp.removeClass(this.selectedClass);
28691 this.selections = [];
28692 if(!suppressEvent){
28693 this.fireEvent("selectionchange", this, this.selections);
28699 * Returns true if the passed node is selected
28700 * @param {HTMLElement/Number} node The node or node index
28701 * @return {Boolean}
28703 isSelected : function(node){
28704 var s = this.selections;
28708 node = this.getNode(node);
28709 return s.indexOf(node) !== -1;
28714 * @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
28715 * @param {Boolean} keepExisting (optional) true to keep existing selections
28716 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28718 select : function(nodeInfo, keepExisting, suppressEvent){
28719 if(nodeInfo instanceof Array){
28721 this.clearSelections(true);
28723 for(var i = 0, len = nodeInfo.length; i < len; i++){
28724 this.select(nodeInfo[i], true, true);
28728 var node = this.getNode(nodeInfo);
28729 if(!node || this.isSelected(node)){
28730 return; // already selected.
28733 this.clearSelections(true);
28736 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28737 Roo.fly(node).addClass(this.selectedClass);
28738 this.selections.push(node);
28739 if(!suppressEvent){
28740 this.fireEvent("selectionchange", this, this.selections);
28748 * @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
28749 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28750 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28752 unselect : function(nodeInfo, keepExisting, suppressEvent)
28754 if(nodeInfo instanceof Array){
28755 Roo.each(this.selections, function(s) {
28756 this.unselect(s, nodeInfo);
28760 var node = this.getNode(nodeInfo);
28761 if(!node || !this.isSelected(node)){
28762 //Roo.log("not selected");
28763 return; // not selected.
28767 Roo.each(this.selections, function(s) {
28769 Roo.fly(node).removeClass(this.selectedClass);
28776 this.selections= ns;
28777 this.fireEvent("selectionchange", this, this.selections);
28781 * Gets a template node.
28782 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28783 * @return {HTMLElement} The node or null if it wasn't found
28785 getNode : function(nodeInfo){
28786 if(typeof nodeInfo == "string"){
28787 return document.getElementById(nodeInfo);
28788 }else if(typeof nodeInfo == "number"){
28789 return this.nodes[nodeInfo];
28795 * Gets a range template nodes.
28796 * @param {Number} startIndex
28797 * @param {Number} endIndex
28798 * @return {Array} An array of nodes
28800 getNodes : function(start, end){
28801 var ns = this.nodes;
28802 start = start || 0;
28803 end = typeof end == "undefined" ? ns.length - 1 : end;
28806 for(var i = start; i <= end; i++){
28810 for(var i = start; i >= end; i--){
28818 * Finds the index of the passed node
28819 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28820 * @return {Number} The index of the node or -1
28822 indexOf : function(node){
28823 node = this.getNode(node);
28824 if(typeof node.nodeIndex == "number"){
28825 return node.nodeIndex;
28827 var ns = this.nodes;
28828 for(var i = 0, len = ns.length; i < len; i++){
28838 * Ext JS Library 1.1.1
28839 * Copyright(c) 2006-2007, Ext JS, LLC.
28841 * Originally Released Under LGPL - original licence link has changed is not relivant.
28844 * <script type="text/javascript">
28848 * @class Roo.JsonView
28849 * @extends Roo.View
28850 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28852 var view = new Roo.JsonView({
28853 container: "my-element",
28854 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
28859 // listen for node click?
28860 view.on("click", function(vw, index, node, e){
28861 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28864 // direct load of JSON data
28865 view.load("foobar.php");
28867 // Example from my blog list
28868 var tpl = new Roo.Template(
28869 '<div class="entry">' +
28870 '<a class="entry-title" href="{link}">{title}</a>' +
28871 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
28872 "</div><hr />"
28875 var moreView = new Roo.JsonView({
28876 container : "entry-list",
28880 moreView.on("beforerender", this.sortEntries, this);
28882 url: "/blog/get-posts.php",
28883 params: "allposts=true",
28884 text: "Loading Blog Entries..."
28888 * Note: old code is supported with arguments : (container, template, config)
28892 * Create a new JsonView
28894 * @param {Object} config The config object
28897 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28900 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28902 var um = this.el.getUpdateManager();
28903 um.setRenderer(this);
28904 um.on("update", this.onLoad, this);
28905 um.on("failure", this.onLoadException, this);
28908 * @event beforerender
28909 * Fires before rendering of the downloaded JSON data.
28910 * @param {Roo.JsonView} this
28911 * @param {Object} data The JSON data loaded
28915 * Fires when data is loaded.
28916 * @param {Roo.JsonView} this
28917 * @param {Object} data The JSON data loaded
28918 * @param {Object} response The raw Connect response object
28921 * @event loadexception
28922 * Fires when loading fails.
28923 * @param {Roo.JsonView} this
28924 * @param {Object} response The raw Connect response object
28927 'beforerender' : true,
28929 'loadexception' : true
28932 Roo.extend(Roo.JsonView, Roo.View, {
28934 * @type {String} The root property in the loaded JSON object that contains the data
28939 * Refreshes the view.
28941 refresh : function(){
28942 this.clearSelections();
28943 this.el.update("");
28945 var o = this.jsonData;
28946 if(o && o.length > 0){
28947 for(var i = 0, len = o.length; i < len; i++){
28948 var data = this.prepareData(o[i], i, o);
28949 html[html.length] = this.tpl.apply(data);
28952 html.push(this.emptyText);
28954 this.el.update(html.join(""));
28955 this.nodes = this.el.dom.childNodes;
28956 this.updateIndexes(0);
28960 * 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.
28961 * @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:
28964 url: "your-url.php",
28965 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28966 callback: yourFunction,
28967 scope: yourObject, //(optional scope)
28970 text: "Loading...",
28975 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28976 * 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.
28977 * @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}
28978 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28979 * @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.
28982 var um = this.el.getUpdateManager();
28983 um.update.apply(um, arguments);
28986 // note - render is a standard framework call...
28987 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28988 render : function(el, response){
28990 this.clearSelections();
28991 this.el.update("");
28994 if (response != '') {
28995 o = Roo.util.JSON.decode(response.responseText);
28998 o = o[this.jsonRoot];
29004 * The current JSON data or null
29007 this.beforeRender();
29012 * Get the number of records in the current JSON dataset
29015 getCount : function(){
29016 return this.jsonData ? this.jsonData.length : 0;
29020 * Returns the JSON object for the specified node(s)
29021 * @param {HTMLElement/Array} node The node or an array of nodes
29022 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29023 * you get the JSON object for the node
29025 getNodeData : function(node){
29026 if(node instanceof Array){
29028 for(var i = 0, len = node.length; i < len; i++){
29029 data.push(this.getNodeData(node[i]));
29033 return this.jsonData[this.indexOf(node)] || null;
29036 beforeRender : function(){
29037 this.snapshot = this.jsonData;
29039 this.sort.apply(this, this.sortInfo);
29041 this.fireEvent("beforerender", this, this.jsonData);
29044 onLoad : function(el, o){
29045 this.fireEvent("load", this, this.jsonData, o);
29048 onLoadException : function(el, o){
29049 this.fireEvent("loadexception", this, o);
29053 * Filter the data by a specific property.
29054 * @param {String} property A property on your JSON objects
29055 * @param {String/RegExp} value Either string that the property values
29056 * should start with, or a RegExp to test against the property
29058 filter : function(property, value){
29061 var ss = this.snapshot;
29062 if(typeof value == "string"){
29063 var vlen = value.length;
29065 this.clearFilter();
29068 value = value.toLowerCase();
29069 for(var i = 0, len = ss.length; i < len; i++){
29071 if(o[property].substr(0, vlen).toLowerCase() == value){
29075 } else if(value.exec){ // regex?
29076 for(var i = 0, len = ss.length; i < len; i++){
29078 if(value.test(o[property])){
29085 this.jsonData = data;
29091 * Filter by a function. The passed function will be called with each
29092 * object in the current dataset. If the function returns true the value is kept,
29093 * otherwise it is filtered.
29094 * @param {Function} fn
29095 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29097 filterBy : function(fn, scope){
29100 var ss = this.snapshot;
29101 for(var i = 0, len = ss.length; i < len; i++){
29103 if(fn.call(scope || this, o)){
29107 this.jsonData = data;
29113 * Clears the current filter.
29115 clearFilter : function(){
29116 if(this.snapshot && this.jsonData != this.snapshot){
29117 this.jsonData = this.snapshot;
29124 * Sorts the data for this view and refreshes it.
29125 * @param {String} property A property on your JSON objects to sort on
29126 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29127 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29129 sort : function(property, dir, sortType){
29130 this.sortInfo = Array.prototype.slice.call(arguments, 0);
29133 var dsc = dir && dir.toLowerCase() == "desc";
29134 var f = function(o1, o2){
29135 var v1 = sortType ? sortType(o1[p]) : o1[p];
29136 var v2 = sortType ? sortType(o2[p]) : o2[p];
29139 return dsc ? +1 : -1;
29140 } else if(v1 > v2){
29141 return dsc ? -1 : +1;
29146 this.jsonData.sort(f);
29148 if(this.jsonData != this.snapshot){
29149 this.snapshot.sort(f);
29155 * Ext JS Library 1.1.1
29156 * Copyright(c) 2006-2007, Ext JS, LLC.
29158 * Originally Released Under LGPL - original licence link has changed is not relivant.
29161 * <script type="text/javascript">
29166 * @class Roo.ColorPalette
29167 * @extends Roo.Component
29168 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
29169 * Here's an example of typical usage:
29171 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
29172 cp.render('my-div');
29174 cp.on('select', function(palette, selColor){
29175 // do something with selColor
29179 * Create a new ColorPalette
29180 * @param {Object} config The config object
29182 Roo.ColorPalette = function(config){
29183 Roo.ColorPalette.superclass.constructor.call(this, config);
29187 * Fires when a color is selected
29188 * @param {ColorPalette} this
29189 * @param {String} color The 6-digit color hex code (without the # symbol)
29195 this.on("select", this.handler, this.scope, true);
29198 Roo.extend(Roo.ColorPalette, Roo.Component, {
29200 * @cfg {String} itemCls
29201 * The CSS class to apply to the containing element (defaults to "x-color-palette")
29203 itemCls : "x-color-palette",
29205 * @cfg {String} value
29206 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
29207 * the hex codes are case-sensitive.
29210 clickEvent:'click',
29212 ctype: "Roo.ColorPalette",
29215 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29217 allowReselect : false,
29220 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
29221 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
29222 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29223 * of colors with the width setting until the box is symmetrical.</p>
29224 * <p>You can override individual colors if needed:</p>
29226 var cp = new Roo.ColorPalette();
29227 cp.colors[0] = "FF0000"; // change the first box to red
29230 Or you can provide a custom array of your own for complete control:
29232 var cp = new Roo.ColorPalette();
29233 cp.colors = ["000000", "993300", "333300"];
29238 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29239 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29240 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29241 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29242 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29246 onRender : function(container, position){
29247 var t = new Roo.MasterTemplate(
29248 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
29250 var c = this.colors;
29251 for(var i = 0, len = c.length; i < len; i++){
29254 var el = document.createElement("div");
29255 el.className = this.itemCls;
29257 container.dom.insertBefore(el, position);
29258 this.el = Roo.get(el);
29259 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
29260 if(this.clickEvent != 'click'){
29261 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
29266 afterRender : function(){
29267 Roo.ColorPalette.superclass.afterRender.call(this);
29269 var s = this.value;
29276 handleClick : function(e, t){
29277 e.preventDefault();
29278 if(!this.disabled){
29279 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29280 this.select(c.toUpperCase());
29285 * Selects the specified color in the palette (fires the select event)
29286 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29288 select : function(color){
29289 color = color.replace("#", "");
29290 if(color != this.value || this.allowReselect){
29293 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29295 el.child("a.color-"+color).addClass("x-color-palette-sel");
29296 this.value = color;
29297 this.fireEvent("select", this, color);
29302 * Ext JS Library 1.1.1
29303 * Copyright(c) 2006-2007, Ext JS, LLC.
29305 * Originally Released Under LGPL - original licence link has changed is not relivant.
29308 * <script type="text/javascript">
29312 * @class Roo.DatePicker
29313 * @extends Roo.Component
29314 * Simple date picker class.
29316 * Create a new DatePicker
29317 * @param {Object} config The config object
29319 Roo.DatePicker = function(config){
29320 Roo.DatePicker.superclass.constructor.call(this, config);
29322 this.value = config && config.value ?
29323 config.value.clearTime() : new Date().clearTime();
29328 * Fires when a date is selected
29329 * @param {DatePicker} this
29330 * @param {Date} date The selected date
29334 * @event monthchange
29335 * Fires when the displayed month changes
29336 * @param {DatePicker} this
29337 * @param {Date} date The selected month
29339 'monthchange': true
29343 this.on("select", this.handler, this.scope || this);
29345 // build the disabledDatesRE
29346 if(!this.disabledDatesRE && this.disabledDates){
29347 var dd = this.disabledDates;
29349 for(var i = 0; i < dd.length; i++){
29351 if(i != dd.length-1) {
29355 this.disabledDatesRE = new RegExp(re + ")");
29359 Roo.extend(Roo.DatePicker, Roo.Component, {
29361 * @cfg {String} todayText
29362 * The text to display on the button that selects the current date (defaults to "Today")
29364 todayText : "Today",
29366 * @cfg {String} okText
29367 * The text to display on the ok button
29369 okText : " OK ", //   to give the user extra clicking room
29371 * @cfg {String} cancelText
29372 * The text to display on the cancel button
29374 cancelText : "Cancel",
29376 * @cfg {String} todayTip
29377 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29379 todayTip : "{0} (Spacebar)",
29381 * @cfg {Date} minDate
29382 * Minimum allowable date (JavaScript date object, defaults to null)
29386 * @cfg {Date} maxDate
29387 * Maximum allowable date (JavaScript date object, defaults to null)
29391 * @cfg {String} minText
29392 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29394 minText : "This date is before the minimum date",
29396 * @cfg {String} maxText
29397 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29399 maxText : "This date is after the maximum date",
29401 * @cfg {String} format
29402 * The default date format string which can be overriden for localization support. The format must be
29403 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29407 * @cfg {Array} disabledDays
29408 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29410 disabledDays : null,
29412 * @cfg {String} disabledDaysText
29413 * The tooltip to display when the date falls on a disabled day (defaults to "")
29415 disabledDaysText : "",
29417 * @cfg {RegExp} disabledDatesRE
29418 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29420 disabledDatesRE : null,
29422 * @cfg {String} disabledDatesText
29423 * The tooltip text to display when the date falls on a disabled date (defaults to "")
29425 disabledDatesText : "",
29427 * @cfg {Boolean} constrainToViewport
29428 * True to constrain the date picker to the viewport (defaults to true)
29430 constrainToViewport : true,
29432 * @cfg {Array} monthNames
29433 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29435 monthNames : Date.monthNames,
29437 * @cfg {Array} dayNames
29438 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29440 dayNames : Date.dayNames,
29442 * @cfg {String} nextText
29443 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29445 nextText: 'Next Month (Control+Right)',
29447 * @cfg {String} prevText
29448 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29450 prevText: 'Previous Month (Control+Left)',
29452 * @cfg {String} monthYearText
29453 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29455 monthYearText: 'Choose a month (Control+Up/Down to move years)',
29457 * @cfg {Number} startDay
29458 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29462 * @cfg {Bool} showClear
29463 * Show a clear button (usefull for date form elements that can be blank.)
29469 * Sets the value of the date field
29470 * @param {Date} value The date to set
29472 setValue : function(value){
29473 var old = this.value;
29475 if (typeof(value) == 'string') {
29477 value = Date.parseDate(value, this.format);
29480 value = new Date();
29483 this.value = value.clearTime(true);
29485 this.update(this.value);
29490 * Gets the current selected value of the date field
29491 * @return {Date} The selected date
29493 getValue : function(){
29498 focus : function(){
29500 this.update(this.activeDate);
29505 onRender : function(container, position){
29508 '<table cellspacing="0">',
29509 '<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>',
29510 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29511 var dn = this.dayNames;
29512 for(var i = 0; i < 7; i++){
29513 var d = this.startDay+i;
29517 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29519 m[m.length] = "</tr></thead><tbody><tr>";
29520 for(var i = 0; i < 42; i++) {
29521 if(i % 7 == 0 && i != 0){
29522 m[m.length] = "</tr><tr>";
29524 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29526 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29527 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29529 var el = document.createElement("div");
29530 el.className = "x-date-picker";
29531 el.innerHTML = m.join("");
29533 container.dom.insertBefore(el, position);
29535 this.el = Roo.get(el);
29536 this.eventEl = Roo.get(el.firstChild);
29538 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29539 handler: this.showPrevMonth,
29541 preventDefault:true,
29545 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29546 handler: this.showNextMonth,
29548 preventDefault:true,
29552 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
29554 this.monthPicker = this.el.down('div.x-date-mp');
29555 this.monthPicker.enableDisplayMode('block');
29557 var kn = new Roo.KeyNav(this.eventEl, {
29558 "left" : function(e){
29560 this.showPrevMonth() :
29561 this.update(this.activeDate.add("d", -1));
29564 "right" : function(e){
29566 this.showNextMonth() :
29567 this.update(this.activeDate.add("d", 1));
29570 "up" : function(e){
29572 this.showNextYear() :
29573 this.update(this.activeDate.add("d", -7));
29576 "down" : function(e){
29578 this.showPrevYear() :
29579 this.update(this.activeDate.add("d", 7));
29582 "pageUp" : function(e){
29583 this.showNextMonth();
29586 "pageDown" : function(e){
29587 this.showPrevMonth();
29590 "enter" : function(e){
29591 e.stopPropagation();
29598 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
29600 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
29602 this.el.unselectable();
29604 this.cells = this.el.select("table.x-date-inner tbody td");
29605 this.textNodes = this.el.query("table.x-date-inner tbody span");
29607 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29609 tooltip: this.monthYearText
29612 this.mbtn.on('click', this.showMonthPicker, this);
29613 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29616 var today = (new Date()).dateFormat(this.format);
29618 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29619 if (this.showClear) {
29620 baseTb.add( new Roo.Toolbar.Fill());
29623 text: String.format(this.todayText, today),
29624 tooltip: String.format(this.todayTip, today),
29625 handler: this.selectToday,
29629 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29632 if (this.showClear) {
29634 baseTb.add( new Roo.Toolbar.Fill());
29637 cls: 'x-btn-icon x-btn-clear',
29638 handler: function() {
29640 this.fireEvent("select", this, '');
29650 this.update(this.value);
29653 createMonthPicker : function(){
29654 if(!this.monthPicker.dom.firstChild){
29655 var buf = ['<table border="0" cellspacing="0">'];
29656 for(var i = 0; i < 6; i++){
29658 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29659 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29661 '<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>' :
29662 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29666 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29668 '</button><button type="button" class="x-date-mp-cancel">',
29670 '</button></td></tr>',
29673 this.monthPicker.update(buf.join(''));
29674 this.monthPicker.on('click', this.onMonthClick, this);
29675 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29677 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29678 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29680 this.mpMonths.each(function(m, a, i){
29683 m.dom.xmonth = 5 + Math.round(i * .5);
29685 m.dom.xmonth = Math.round((i-1) * .5);
29691 showMonthPicker : function(){
29692 this.createMonthPicker();
29693 var size = this.el.getSize();
29694 this.monthPicker.setSize(size);
29695 this.monthPicker.child('table').setSize(size);
29697 this.mpSelMonth = (this.activeDate || this.value).getMonth();
29698 this.updateMPMonth(this.mpSelMonth);
29699 this.mpSelYear = (this.activeDate || this.value).getFullYear();
29700 this.updateMPYear(this.mpSelYear);
29702 this.monthPicker.slideIn('t', {duration:.2});
29705 updateMPYear : function(y){
29707 var ys = this.mpYears.elements;
29708 for(var i = 1; i <= 10; i++){
29709 var td = ys[i-1], y2;
29711 y2 = y + Math.round(i * .5);
29712 td.firstChild.innerHTML = y2;
29715 y2 = y - (5-Math.round(i * .5));
29716 td.firstChild.innerHTML = y2;
29719 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29723 updateMPMonth : function(sm){
29724 this.mpMonths.each(function(m, a, i){
29725 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29729 selectMPMonth: function(m){
29733 onMonthClick : function(e, t){
29735 var el = new Roo.Element(t), pn;
29736 if(el.is('button.x-date-mp-cancel')){
29737 this.hideMonthPicker();
29739 else if(el.is('button.x-date-mp-ok')){
29740 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29741 this.hideMonthPicker();
29743 else if(pn = el.up('td.x-date-mp-month', 2)){
29744 this.mpMonths.removeClass('x-date-mp-sel');
29745 pn.addClass('x-date-mp-sel');
29746 this.mpSelMonth = pn.dom.xmonth;
29748 else if(pn = el.up('td.x-date-mp-year', 2)){
29749 this.mpYears.removeClass('x-date-mp-sel');
29750 pn.addClass('x-date-mp-sel');
29751 this.mpSelYear = pn.dom.xyear;
29753 else if(el.is('a.x-date-mp-prev')){
29754 this.updateMPYear(this.mpyear-10);
29756 else if(el.is('a.x-date-mp-next')){
29757 this.updateMPYear(this.mpyear+10);
29761 onMonthDblClick : function(e, t){
29763 var el = new Roo.Element(t), pn;
29764 if(pn = el.up('td.x-date-mp-month', 2)){
29765 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29766 this.hideMonthPicker();
29768 else if(pn = el.up('td.x-date-mp-year', 2)){
29769 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29770 this.hideMonthPicker();
29774 hideMonthPicker : function(disableAnim){
29775 if(this.monthPicker){
29776 if(disableAnim === true){
29777 this.monthPicker.hide();
29779 this.monthPicker.slideOut('t', {duration:.2});
29785 showPrevMonth : function(e){
29786 this.update(this.activeDate.add("mo", -1));
29790 showNextMonth : function(e){
29791 this.update(this.activeDate.add("mo", 1));
29795 showPrevYear : function(){
29796 this.update(this.activeDate.add("y", -1));
29800 showNextYear : function(){
29801 this.update(this.activeDate.add("y", 1));
29805 handleMouseWheel : function(e){
29806 var delta = e.getWheelDelta();
29808 this.showPrevMonth();
29810 } else if(delta < 0){
29811 this.showNextMonth();
29817 handleDateClick : function(e, t){
29819 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29820 this.setValue(new Date(t.dateValue));
29821 this.fireEvent("select", this, this.value);
29826 selectToday : function(){
29827 this.setValue(new Date().clearTime());
29828 this.fireEvent("select", this, this.value);
29832 update : function(date)
29834 var vd = this.activeDate;
29835 this.activeDate = date;
29837 var t = date.getTime();
29838 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29839 this.cells.removeClass("x-date-selected");
29840 this.cells.each(function(c){
29841 if(c.dom.firstChild.dateValue == t){
29842 c.addClass("x-date-selected");
29843 setTimeout(function(){
29844 try{c.dom.firstChild.focus();}catch(e){}
29853 var days = date.getDaysInMonth();
29854 var firstOfMonth = date.getFirstDateOfMonth();
29855 var startingPos = firstOfMonth.getDay()-this.startDay;
29857 if(startingPos <= this.startDay){
29861 var pm = date.add("mo", -1);
29862 var prevStart = pm.getDaysInMonth()-startingPos;
29864 var cells = this.cells.elements;
29865 var textEls = this.textNodes;
29866 days += startingPos;
29868 // convert everything to numbers so it's fast
29869 var day = 86400000;
29870 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29871 var today = new Date().clearTime().getTime();
29872 var sel = date.clearTime().getTime();
29873 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29874 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29875 var ddMatch = this.disabledDatesRE;
29876 var ddText = this.disabledDatesText;
29877 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29878 var ddaysText = this.disabledDaysText;
29879 var format = this.format;
29881 var setCellClass = function(cal, cell){
29883 var t = d.getTime();
29884 cell.firstChild.dateValue = t;
29886 cell.className += " x-date-today";
29887 cell.title = cal.todayText;
29890 cell.className += " x-date-selected";
29891 setTimeout(function(){
29892 try{cell.firstChild.focus();}catch(e){}
29897 cell.className = " x-date-disabled";
29898 cell.title = cal.minText;
29902 cell.className = " x-date-disabled";
29903 cell.title = cal.maxText;
29907 if(ddays.indexOf(d.getDay()) != -1){
29908 cell.title = ddaysText;
29909 cell.className = " x-date-disabled";
29912 if(ddMatch && format){
29913 var fvalue = d.dateFormat(format);
29914 if(ddMatch.test(fvalue)){
29915 cell.title = ddText.replace("%0", fvalue);
29916 cell.className = " x-date-disabled";
29922 for(; i < startingPos; i++) {
29923 textEls[i].innerHTML = (++prevStart);
29924 d.setDate(d.getDate()+1);
29925 cells[i].className = "x-date-prevday";
29926 setCellClass(this, cells[i]);
29928 for(; i < days; i++){
29929 intDay = i - startingPos + 1;
29930 textEls[i].innerHTML = (intDay);
29931 d.setDate(d.getDate()+1);
29932 cells[i].className = "x-date-active";
29933 setCellClass(this, cells[i]);
29936 for(; i < 42; i++) {
29937 textEls[i].innerHTML = (++extraDays);
29938 d.setDate(d.getDate()+1);
29939 cells[i].className = "x-date-nextday";
29940 setCellClass(this, cells[i]);
29943 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29944 this.fireEvent('monthchange', this, date);
29946 if(!this.internalRender){
29947 var main = this.el.dom.firstChild;
29948 var w = main.offsetWidth;
29949 this.el.setWidth(w + this.el.getBorderWidth("lr"));
29950 Roo.fly(main).setWidth(w);
29951 this.internalRender = true;
29952 // opera does not respect the auto grow header center column
29953 // then, after it gets a width opera refuses to recalculate
29954 // without a second pass
29955 if(Roo.isOpera && !this.secondPass){
29956 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29957 this.secondPass = true;
29958 this.update.defer(10, this, [date]);
29966 * Ext JS Library 1.1.1
29967 * Copyright(c) 2006-2007, Ext JS, LLC.
29969 * Originally Released Under LGPL - original licence link has changed is not relivant.
29972 * <script type="text/javascript">
29975 * @class Roo.TabPanel
29976 * @extends Roo.util.Observable
29977 * A lightweight tab container.
29981 // basic tabs 1, built from existing content
29982 var tabs = new Roo.TabPanel("tabs1");
29983 tabs.addTab("script", "View Script");
29984 tabs.addTab("markup", "View Markup");
29985 tabs.activate("script");
29987 // more advanced tabs, built from javascript
29988 var jtabs = new Roo.TabPanel("jtabs");
29989 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29991 // set up the UpdateManager
29992 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29993 var updater = tab2.getUpdateManager();
29994 updater.setDefaultUrl("ajax1.htm");
29995 tab2.on('activate', updater.refresh, updater, true);
29997 // Use setUrl for Ajax loading
29998 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29999 tab3.setUrl("ajax2.htm", null, true);
30002 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
30005 jtabs.activate("jtabs-1");
30008 * Create a new TabPanel.
30009 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30010 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30012 Roo.TabPanel = function(container, config){
30014 * The container element for this TabPanel.
30015 * @type Roo.Element
30017 this.el = Roo.get(container, true);
30019 if(typeof config == "boolean"){
30020 this.tabPosition = config ? "bottom" : "top";
30022 Roo.apply(this, config);
30025 if(this.tabPosition == "bottom"){
30026 this.bodyEl = Roo.get(this.createBody(this.el.dom));
30027 this.el.addClass("x-tabs-bottom");
30029 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30030 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30031 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30033 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30035 if(this.tabPosition != "bottom"){
30036 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30037 * @type Roo.Element
30039 this.bodyEl = Roo.get(this.createBody(this.el.dom));
30040 this.el.addClass("x-tabs-top");
30044 this.bodyEl.setStyle("position", "relative");
30046 this.active = null;
30047 this.activateDelegate = this.activate.createDelegate(this);
30052 * Fires when the active tab changes
30053 * @param {Roo.TabPanel} this
30054 * @param {Roo.TabPanelItem} activePanel The new active tab
30058 * @event beforetabchange
30059 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30060 * @param {Roo.TabPanel} this
30061 * @param {Object} e Set cancel to true on this object to cancel the tab change
30062 * @param {Roo.TabPanelItem} tab The tab being changed to
30064 "beforetabchange" : true
30067 Roo.EventManager.onWindowResize(this.onResize, this);
30068 this.cpad = this.el.getPadding("lr");
30069 this.hiddenCount = 0;
30072 // toolbar on the tabbar support...
30073 if (this.toolbar) {
30074 var tcfg = this.toolbar;
30075 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
30076 this.toolbar = new Roo.Toolbar(tcfg);
30077 if (Roo.isSafari) {
30078 var tbl = tcfg.container.child('table', true);
30079 tbl.setAttribute('width', '100%');
30086 Roo.TabPanel.superclass.constructor.call(this);
30089 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30091 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30093 tabPosition : "top",
30095 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30097 currentTabWidth : 0,
30099 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30103 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30107 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30109 preferredTabWidth : 175,
30111 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30113 resizeTabs : false,
30115 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30117 monitorResize : true,
30119 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
30124 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30125 * @param {String} id The id of the div to use <b>or create</b>
30126 * @param {String} text The text for the tab
30127 * @param {String} content (optional) Content to put in the TabPanelItem body
30128 * @param {Boolean} closable (optional) True to create a close icon on the tab
30129 * @return {Roo.TabPanelItem} The created TabPanelItem
30131 addTab : function(id, text, content, closable){
30132 var item = new Roo.TabPanelItem(this, id, text, closable);
30133 this.addTabItem(item);
30135 item.setContent(content);
30141 * Returns the {@link Roo.TabPanelItem} with the specified id/index
30142 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30143 * @return {Roo.TabPanelItem}
30145 getTab : function(id){
30146 return this.items[id];
30150 * Hides the {@link Roo.TabPanelItem} with the specified id/index
30151 * @param {String/Number} id The id or index of the TabPanelItem to hide.
30153 hideTab : function(id){
30154 var t = this.items[id];
30157 this.hiddenCount++;
30158 this.autoSizeTabs();
30163 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30164 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30166 unhideTab : function(id){
30167 var t = this.items[id];
30169 t.setHidden(false);
30170 this.hiddenCount--;
30171 this.autoSizeTabs();
30176 * Adds an existing {@link Roo.TabPanelItem}.
30177 * @param {Roo.TabPanelItem} item The TabPanelItem to add
30179 addTabItem : function(item){
30180 this.items[item.id] = item;
30181 this.items.push(item);
30182 if(this.resizeTabs){
30183 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30184 this.autoSizeTabs();
30191 * Removes a {@link Roo.TabPanelItem}.
30192 * @param {String/Number} id The id or index of the TabPanelItem to remove.
30194 removeTab : function(id){
30195 var items = this.items;
30196 var tab = items[id];
30197 if(!tab) { return; }
30198 var index = items.indexOf(tab);
30199 if(this.active == tab && items.length > 1){
30200 var newTab = this.getNextAvailable(index);
30205 this.stripEl.dom.removeChild(tab.pnode.dom);
30206 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30207 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30209 items.splice(index, 1);
30210 delete this.items[tab.id];
30211 tab.fireEvent("close", tab);
30212 tab.purgeListeners();
30213 this.autoSizeTabs();
30216 getNextAvailable : function(start){
30217 var items = this.items;
30219 // look for a next tab that will slide over to
30220 // replace the one being removed
30221 while(index < items.length){
30222 var item = items[++index];
30223 if(item && !item.isHidden()){
30227 // if one isn't found select the previous tab (on the left)
30230 var item = items[--index];
30231 if(item && !item.isHidden()){
30239 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30240 * @param {String/Number} id The id or index of the TabPanelItem to disable.
30242 disableTab : function(id){
30243 var tab = this.items[id];
30244 if(tab && this.active != tab){
30250 * Enables a {@link Roo.TabPanelItem} that is disabled.
30251 * @param {String/Number} id The id or index of the TabPanelItem to enable.
30253 enableTab : function(id){
30254 var tab = this.items[id];
30259 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30260 * @param {String/Number} id The id or index of the TabPanelItem to activate.
30261 * @return {Roo.TabPanelItem} The TabPanelItem.
30263 activate : function(id){
30264 var tab = this.items[id];
30268 if(tab == this.active || tab.disabled){
30272 this.fireEvent("beforetabchange", this, e, tab);
30273 if(e.cancel !== true && !tab.disabled){
30275 this.active.hide();
30277 this.active = this.items[id];
30278 this.active.show();
30279 this.fireEvent("tabchange", this, this.active);
30285 * Gets the active {@link Roo.TabPanelItem}.
30286 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30288 getActiveTab : function(){
30289 return this.active;
30293 * Updates the tab body element to fit the height of the container element
30294 * for overflow scrolling
30295 * @param {Number} targetHeight (optional) Override the starting height from the elements height
30297 syncHeight : function(targetHeight){
30298 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30299 var bm = this.bodyEl.getMargins();
30300 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30301 this.bodyEl.setHeight(newHeight);
30305 onResize : function(){
30306 if(this.monitorResize){
30307 this.autoSizeTabs();
30312 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30314 beginUpdate : function(){
30315 this.updating = true;
30319 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30321 endUpdate : function(){
30322 this.updating = false;
30323 this.autoSizeTabs();
30327 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30329 autoSizeTabs : function(){
30330 var count = this.items.length;
30331 var vcount = count - this.hiddenCount;
30332 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30335 var w = Math.max(this.el.getWidth() - this.cpad, 10);
30336 var availWidth = Math.floor(w / vcount);
30337 var b = this.stripBody;
30338 if(b.getWidth() > w){
30339 var tabs = this.items;
30340 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30341 if(availWidth < this.minTabWidth){
30342 /*if(!this.sleft){ // incomplete scrolling code
30343 this.createScrollButtons();
30346 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30349 if(this.currentTabWidth < this.preferredTabWidth){
30350 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30356 * Returns the number of tabs in this TabPanel.
30359 getCount : function(){
30360 return this.items.length;
30364 * Resizes all the tabs to the passed width
30365 * @param {Number} The new width
30367 setTabWidth : function(width){
30368 this.currentTabWidth = width;
30369 for(var i = 0, len = this.items.length; i < len; i++) {
30370 if(!this.items[i].isHidden()) {
30371 this.items[i].setWidth(width);
30377 * Destroys this TabPanel
30378 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30380 destroy : function(removeEl){
30381 Roo.EventManager.removeResizeListener(this.onResize, this);
30382 for(var i = 0, len = this.items.length; i < len; i++){
30383 this.items[i].purgeListeners();
30385 if(removeEl === true){
30386 this.el.update("");
30393 * @class Roo.TabPanelItem
30394 * @extends Roo.util.Observable
30395 * Represents an individual item (tab plus body) in a TabPanel.
30396 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30397 * @param {String} id The id of this TabPanelItem
30398 * @param {String} text The text for the tab of this TabPanelItem
30399 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30401 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30403 * The {@link Roo.TabPanel} this TabPanelItem belongs to
30404 * @type Roo.TabPanel
30406 this.tabPanel = tabPanel;
30408 * The id for this TabPanelItem
30413 this.disabled = false;
30417 this.loaded = false;
30418 this.closable = closable;
30421 * The body element for this TabPanelItem.
30422 * @type Roo.Element
30424 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30425 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30426 this.bodyEl.setStyle("display", "block");
30427 this.bodyEl.setStyle("zoom", "1");
30430 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30432 this.el = Roo.get(els.el, true);
30433 this.inner = Roo.get(els.inner, true);
30434 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30435 this.pnode = Roo.get(els.el.parentNode, true);
30436 this.el.on("mousedown", this.onTabMouseDown, this);
30437 this.el.on("click", this.onTabClick, this);
30440 var c = Roo.get(els.close, true);
30441 c.dom.title = this.closeText;
30442 c.addClassOnOver("close-over");
30443 c.on("click", this.closeClick, this);
30449 * Fires when this tab becomes the active tab.
30450 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30451 * @param {Roo.TabPanelItem} this
30455 * @event beforeclose
30456 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30457 * @param {Roo.TabPanelItem} this
30458 * @param {Object} e Set cancel to true on this object to cancel the close.
30460 "beforeclose": true,
30463 * Fires when this tab is closed.
30464 * @param {Roo.TabPanelItem} this
30468 * @event deactivate
30469 * Fires when this tab is no longer the active tab.
30470 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30471 * @param {Roo.TabPanelItem} this
30473 "deactivate" : true
30475 this.hidden = false;
30477 Roo.TabPanelItem.superclass.constructor.call(this);
30480 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30481 purgeListeners : function(){
30482 Roo.util.Observable.prototype.purgeListeners.call(this);
30483 this.el.removeAllListeners();
30486 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30489 this.pnode.addClass("on");
30492 this.tabPanel.stripWrap.repaint();
30494 this.fireEvent("activate", this.tabPanel, this);
30498 * Returns true if this tab is the active tab.
30499 * @return {Boolean}
30501 isActive : function(){
30502 return this.tabPanel.getActiveTab() == this;
30506 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30509 this.pnode.removeClass("on");
30511 this.fireEvent("deactivate", this.tabPanel, this);
30514 hideAction : function(){
30515 this.bodyEl.hide();
30516 this.bodyEl.setStyle("position", "absolute");
30517 this.bodyEl.setLeft("-20000px");
30518 this.bodyEl.setTop("-20000px");
30521 showAction : function(){
30522 this.bodyEl.setStyle("position", "relative");
30523 this.bodyEl.setTop("");
30524 this.bodyEl.setLeft("");
30525 this.bodyEl.show();
30529 * Set the tooltip for the tab.
30530 * @param {String} tooltip The tab's tooltip
30532 setTooltip : function(text){
30533 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30534 this.textEl.dom.qtip = text;
30535 this.textEl.dom.removeAttribute('title');
30537 this.textEl.dom.title = text;
30541 onTabClick : function(e){
30542 e.preventDefault();
30543 this.tabPanel.activate(this.id);
30546 onTabMouseDown : function(e){
30547 e.preventDefault();
30548 this.tabPanel.activate(this.id);
30551 getWidth : function(){
30552 return this.inner.getWidth();
30555 setWidth : function(width){
30556 var iwidth = width - this.pnode.getPadding("lr");
30557 this.inner.setWidth(iwidth);
30558 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30559 this.pnode.setWidth(width);
30563 * Show or hide the tab
30564 * @param {Boolean} hidden True to hide or false to show.
30566 setHidden : function(hidden){
30567 this.hidden = hidden;
30568 this.pnode.setStyle("display", hidden ? "none" : "");
30572 * Returns true if this tab is "hidden"
30573 * @return {Boolean}
30575 isHidden : function(){
30576 return this.hidden;
30580 * Returns the text for this tab
30583 getText : function(){
30587 autoSize : function(){
30588 //this.el.beginMeasure();
30589 this.textEl.setWidth(1);
30591 * #2804 [new] Tabs in Roojs
30592 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30594 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30595 //this.el.endMeasure();
30599 * Sets the text for the tab (Note: this also sets the tooltip text)
30600 * @param {String} text The tab's text and tooltip
30602 setText : function(text){
30604 this.textEl.update(text);
30605 this.setTooltip(text);
30606 if(!this.tabPanel.resizeTabs){
30611 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30613 activate : function(){
30614 this.tabPanel.activate(this.id);
30618 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30620 disable : function(){
30621 if(this.tabPanel.active != this){
30622 this.disabled = true;
30623 this.pnode.addClass("disabled");
30628 * Enables this TabPanelItem if it was previously disabled.
30630 enable : function(){
30631 this.disabled = false;
30632 this.pnode.removeClass("disabled");
30636 * Sets the content for this TabPanelItem.
30637 * @param {String} content The content
30638 * @param {Boolean} loadScripts true to look for and load scripts
30640 setContent : function(content, loadScripts){
30641 this.bodyEl.update(content, loadScripts);
30645 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30646 * @return {Roo.UpdateManager} The UpdateManager
30648 getUpdateManager : function(){
30649 return this.bodyEl.getUpdateManager();
30653 * Set a URL to be used to load the content for this TabPanelItem.
30654 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30655 * @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)
30656 * @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)
30657 * @return {Roo.UpdateManager} The UpdateManager
30659 setUrl : function(url, params, loadOnce){
30660 if(this.refreshDelegate){
30661 this.un('activate', this.refreshDelegate);
30663 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30664 this.on("activate", this.refreshDelegate);
30665 return this.bodyEl.getUpdateManager();
30669 _handleRefresh : function(url, params, loadOnce){
30670 if(!loadOnce || !this.loaded){
30671 var updater = this.bodyEl.getUpdateManager();
30672 updater.update(url, params, this._setLoaded.createDelegate(this));
30677 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
30678 * Will fail silently if the setUrl method has not been called.
30679 * This does not activate the panel, just updates its content.
30681 refresh : function(){
30682 if(this.refreshDelegate){
30683 this.loaded = false;
30684 this.refreshDelegate();
30689 _setLoaded : function(){
30690 this.loaded = true;
30694 closeClick : function(e){
30697 this.fireEvent("beforeclose", this, o);
30698 if(o.cancel !== true){
30699 this.tabPanel.removeTab(this.id);
30703 * The text displayed in the tooltip for the close icon.
30706 closeText : "Close this tab"
30710 Roo.TabPanel.prototype.createStrip = function(container){
30711 var strip = document.createElement("div");
30712 strip.className = "x-tabs-wrap";
30713 container.appendChild(strip);
30717 Roo.TabPanel.prototype.createStripList = function(strip){
30718 // div wrapper for retard IE
30719 // returns the "tr" element.
30720 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30721 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30722 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30723 return strip.firstChild.firstChild.firstChild.firstChild;
30726 Roo.TabPanel.prototype.createBody = function(container){
30727 var body = document.createElement("div");
30728 Roo.id(body, "tab-body");
30729 Roo.fly(body).addClass("x-tabs-body");
30730 container.appendChild(body);
30734 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30735 var body = Roo.getDom(id);
30737 body = document.createElement("div");
30740 Roo.fly(body).addClass("x-tabs-item-body");
30741 bodyEl.insertBefore(body, bodyEl.firstChild);
30745 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30746 var td = document.createElement("td");
30747 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30748 //stripEl.appendChild(td);
30750 td.className = "x-tabs-closable";
30751 if(!this.closeTpl){
30752 this.closeTpl = new Roo.Template(
30753 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30754 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30755 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
30758 var el = this.closeTpl.overwrite(td, {"text": text});
30759 var close = el.getElementsByTagName("div")[0];
30760 var inner = el.getElementsByTagName("em")[0];
30761 return {"el": el, "close": close, "inner": inner};
30764 this.tabTpl = new Roo.Template(
30765 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30766 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30769 var el = this.tabTpl.overwrite(td, {"text": text});
30770 var inner = el.getElementsByTagName("em")[0];
30771 return {"el": el, "inner": inner};
30775 * Ext JS Library 1.1.1
30776 * Copyright(c) 2006-2007, Ext JS, LLC.
30778 * Originally Released Under LGPL - original licence link has changed is not relivant.
30781 * <script type="text/javascript">
30785 * @class Roo.Button
30786 * @extends Roo.util.Observable
30787 * Simple Button class
30788 * @cfg {String} text The button text
30789 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30790 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30791 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30792 * @cfg {Object} scope The scope of the handler
30793 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30794 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30795 * @cfg {Boolean} hidden True to start hidden (defaults to false)
30796 * @cfg {Boolean} disabled True to start disabled (defaults to false)
30797 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30798 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30799 applies if enableToggle = true)
30800 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30801 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30802 an {@link Roo.util.ClickRepeater} config object (defaults to false).
30804 * Create a new button
30805 * @param {Object} config The config object
30807 Roo.Button = function(renderTo, config)
30811 renderTo = config.renderTo || false;
30814 Roo.apply(this, config);
30818 * Fires when this button is clicked
30819 * @param {Button} this
30820 * @param {EventObject} e The click event
30825 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30826 * @param {Button} this
30827 * @param {Boolean} pressed
30832 * Fires when the mouse hovers over the button
30833 * @param {Button} this
30834 * @param {Event} e The event object
30836 'mouseover' : true,
30839 * Fires when the mouse exits the button
30840 * @param {Button} this
30841 * @param {Event} e The event object
30846 * Fires when the button is rendered
30847 * @param {Button} this
30852 this.menu = Roo.menu.MenuMgr.get(this.menu);
30854 // register listeners first!! - so render can be captured..
30855 Roo.util.Observable.call(this);
30857 this.render(renderTo);
30863 Roo.extend(Roo.Button, Roo.util.Observable, {
30869 * Read-only. True if this button is hidden
30874 * Read-only. True if this button is disabled
30879 * Read-only. True if this button is pressed (only if enableToggle = true)
30885 * @cfg {Number} tabIndex
30886 * The DOM tabIndex for this button (defaults to undefined)
30888 tabIndex : undefined,
30891 * @cfg {Boolean} enableToggle
30892 * True to enable pressed/not pressed toggling (defaults to false)
30894 enableToggle: false,
30896 * @cfg {Roo.menu.Menu} menu
30897 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30901 * @cfg {String} menuAlign
30902 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30904 menuAlign : "tl-bl?",
30907 * @cfg {String} iconCls
30908 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30910 iconCls : undefined,
30912 * @cfg {String} type
30913 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
30918 menuClassTarget: 'tr',
30921 * @cfg {String} clickEvent
30922 * The type of event to map to the button's event handler (defaults to 'click')
30924 clickEvent : 'click',
30927 * @cfg {Boolean} handleMouseEvents
30928 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30930 handleMouseEvents : true,
30933 * @cfg {String} tooltipType
30934 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30936 tooltipType : 'qtip',
30939 * @cfg {String} cls
30940 * A CSS class to apply to the button's main element.
30944 * @cfg {Roo.Template} template (Optional)
30945 * An {@link Roo.Template} with which to create the Button's main element. This Template must
30946 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30947 * require code modifications if required elements (e.g. a button) aren't present.
30951 render : function(renderTo){
30953 if(this.hideParent){
30954 this.parentEl = Roo.get(renderTo);
30956 if(!this.dhconfig){
30957 if(!this.template){
30958 if(!Roo.Button.buttonTemplate){
30959 // hideous table template
30960 Roo.Button.buttonTemplate = new Roo.Template(
30961 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30962 '<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>',
30963 "</tr></tbody></table>");
30965 this.template = Roo.Button.buttonTemplate;
30967 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
30968 var btnEl = btn.child("button:first");
30969 btnEl.on('focus', this.onFocus, this);
30970 btnEl.on('blur', this.onBlur, this);
30972 btn.addClass(this.cls);
30975 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30978 btnEl.addClass(this.iconCls);
30980 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30983 if(this.tabIndex !== undefined){
30984 btnEl.dom.tabIndex = this.tabIndex;
30987 if(typeof this.tooltip == 'object'){
30988 Roo.QuickTips.tips(Roo.apply({
30992 btnEl.dom[this.tooltipType] = this.tooltip;
30996 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
31000 this.el.dom.id = this.el.id = this.id;
31003 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
31004 this.menu.on("show", this.onMenuShow, this);
31005 this.menu.on("hide", this.onMenuHide, this);
31007 btn.addClass("x-btn");
31008 if(Roo.isIE && !Roo.isIE7){
31009 this.autoWidth.defer(1, this);
31013 if(this.handleMouseEvents){
31014 btn.on("mouseover", this.onMouseOver, this);
31015 btn.on("mouseout", this.onMouseOut, this);
31016 btn.on("mousedown", this.onMouseDown, this);
31018 btn.on(this.clickEvent, this.onClick, this);
31019 //btn.on("mouseup", this.onMouseUp, this);
31026 Roo.ButtonToggleMgr.register(this);
31028 this.el.addClass("x-btn-pressed");
31031 var repeater = new Roo.util.ClickRepeater(btn,
31032 typeof this.repeat == "object" ? this.repeat : {}
31034 repeater.on("click", this.onClick, this);
31037 this.fireEvent('render', this);
31041 * Returns the button's underlying element
31042 * @return {Roo.Element} The element
31044 getEl : function(){
31049 * Destroys this Button and removes any listeners.
31051 destroy : function(){
31052 Roo.ButtonToggleMgr.unregister(this);
31053 this.el.removeAllListeners();
31054 this.purgeListeners();
31059 autoWidth : function(){
31061 this.el.setWidth("auto");
31062 if(Roo.isIE7 && Roo.isStrict){
31063 var ib = this.el.child('button');
31064 if(ib && ib.getWidth() > 20){
31066 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31071 this.el.beginMeasure();
31073 if(this.el.getWidth() < this.minWidth){
31074 this.el.setWidth(this.minWidth);
31077 this.el.endMeasure();
31084 * Assigns this button's click handler
31085 * @param {Function} handler The function to call when the button is clicked
31086 * @param {Object} scope (optional) Scope for the function passed in
31088 setHandler : function(handler, scope){
31089 this.handler = handler;
31090 this.scope = scope;
31094 * Sets this button's text
31095 * @param {String} text The button text
31097 setText : function(text){
31100 this.el.child("td.x-btn-center button.x-btn-text").update(text);
31106 * Gets the text for this button
31107 * @return {String} The button text
31109 getText : function(){
31117 this.hidden = false;
31119 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31127 this.hidden = true;
31129 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31134 * Convenience function for boolean show/hide
31135 * @param {Boolean} visible True to show, false to hide
31137 setVisible: function(visible){
31145 * Similar to toggle, but does not trigger event.
31146 * @param {Boolean} state [required] Force a particular state
31148 setPressed : function(state)
31150 if(state != this.pressed){
31152 this.el.addClass("x-btn-pressed");
31153 this.pressed = true;
31155 this.el.removeClass("x-btn-pressed");
31156 this.pressed = false;
31162 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31163 * @param {Boolean} state (optional) Force a particular state
31165 toggle : function(state){
31166 state = state === undefined ? !this.pressed : state;
31167 if(state != this.pressed){
31169 this.el.addClass("x-btn-pressed");
31170 this.pressed = true;
31171 this.fireEvent("toggle", this, true);
31173 this.el.removeClass("x-btn-pressed");
31174 this.pressed = false;
31175 this.fireEvent("toggle", this, false);
31177 if(this.toggleHandler){
31178 this.toggleHandler.call(this.scope || this, this, state);
31188 focus : function(){
31189 this.el.child('button:first').focus();
31193 * Disable this button
31195 disable : function(){
31197 this.el.addClass("x-btn-disabled");
31199 this.disabled = true;
31203 * Enable this button
31205 enable : function(){
31207 this.el.removeClass("x-btn-disabled");
31209 this.disabled = false;
31213 * Convenience function for boolean enable/disable
31214 * @param {Boolean} enabled True to enable, false to disable
31216 setDisabled : function(v){
31217 this[v !== true ? "enable" : "disable"]();
31221 onClick : function(e)
31224 e.preventDefault();
31229 if(!this.disabled){
31230 if(this.enableToggle){
31233 if(this.menu && !this.menu.isVisible()){
31234 this.menu.show(this.el, this.menuAlign);
31236 this.fireEvent("click", this, e);
31238 this.el.removeClass("x-btn-over");
31239 this.handler.call(this.scope || this, this, e);
31244 onMouseOver : function(e){
31245 if(!this.disabled){
31246 this.el.addClass("x-btn-over");
31247 this.fireEvent('mouseover', this, e);
31251 onMouseOut : function(e){
31252 if(!e.within(this.el, true)){
31253 this.el.removeClass("x-btn-over");
31254 this.fireEvent('mouseout', this, e);
31258 onFocus : function(e){
31259 if(!this.disabled){
31260 this.el.addClass("x-btn-focus");
31264 onBlur : function(e){
31265 this.el.removeClass("x-btn-focus");
31268 onMouseDown : function(e){
31269 if(!this.disabled && e.button == 0){
31270 this.el.addClass("x-btn-click");
31271 Roo.get(document).on('mouseup', this.onMouseUp, this);
31275 onMouseUp : function(e){
31277 this.el.removeClass("x-btn-click");
31278 Roo.get(document).un('mouseup', this.onMouseUp, this);
31282 onMenuShow : function(e){
31283 this.el.addClass("x-btn-menu-active");
31286 onMenuHide : function(e){
31287 this.el.removeClass("x-btn-menu-active");
31291 // Private utility class used by Button
31292 Roo.ButtonToggleMgr = function(){
31295 function toggleGroup(btn, state){
31297 var g = groups[btn.toggleGroup];
31298 for(var i = 0, l = g.length; i < l; i++){
31300 g[i].toggle(false);
31307 register : function(btn){
31308 if(!btn.toggleGroup){
31311 var g = groups[btn.toggleGroup];
31313 g = groups[btn.toggleGroup] = [];
31316 btn.on("toggle", toggleGroup);
31319 unregister : function(btn){
31320 if(!btn.toggleGroup){
31323 var g = groups[btn.toggleGroup];
31326 btn.un("toggle", toggleGroup);
31332 * Ext JS Library 1.1.1
31333 * Copyright(c) 2006-2007, Ext JS, LLC.
31335 * Originally Released Under LGPL - original licence link has changed is not relivant.
31338 * <script type="text/javascript">
31342 * @class Roo.SplitButton
31343 * @extends Roo.Button
31344 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31345 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
31346 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31347 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31348 * @cfg {String} arrowTooltip The title attribute of the arrow
31350 * Create a new menu button
31351 * @param {String/HTMLElement/Element} renderTo The element to append the button to
31352 * @param {Object} config The config object
31354 Roo.SplitButton = function(renderTo, config){
31355 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31357 * @event arrowclick
31358 * Fires when this button's arrow is clicked
31359 * @param {SplitButton} this
31360 * @param {EventObject} e The click event
31362 this.addEvents({"arrowclick":true});
31365 Roo.extend(Roo.SplitButton, Roo.Button, {
31366 render : function(renderTo){
31367 // this is one sweet looking template!
31368 var tpl = new Roo.Template(
31369 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31370 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31371 '<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>',
31372 "</tbody></table></td><td>",
31373 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31374 '<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>',
31375 "</tbody></table></td></tr></table>"
31377 var btn = tpl.append(renderTo, [this.text, this.type], true);
31378 var btnEl = btn.child("button");
31380 btn.addClass(this.cls);
31383 btnEl.setStyle('background-image', 'url(' +this.icon +')');
31386 btnEl.addClass(this.iconCls);
31388 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31392 if(this.handleMouseEvents){
31393 btn.on("mouseover", this.onMouseOver, this);
31394 btn.on("mouseout", this.onMouseOut, this);
31395 btn.on("mousedown", this.onMouseDown, this);
31396 btn.on("mouseup", this.onMouseUp, this);
31398 btn.on(this.clickEvent, this.onClick, this);
31400 if(typeof this.tooltip == 'object'){
31401 Roo.QuickTips.tips(Roo.apply({
31405 btnEl.dom[this.tooltipType] = this.tooltip;
31408 if(this.arrowTooltip){
31409 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31418 this.el.addClass("x-btn-pressed");
31420 if(Roo.isIE && !Roo.isIE7){
31421 this.autoWidth.defer(1, this);
31426 this.menu.on("show", this.onMenuShow, this);
31427 this.menu.on("hide", this.onMenuHide, this);
31429 this.fireEvent('render', this);
31433 autoWidth : function(){
31435 var tbl = this.el.child("table:first");
31436 var tbl2 = this.el.child("table:last");
31437 this.el.setWidth("auto");
31438 tbl.setWidth("auto");
31439 if(Roo.isIE7 && Roo.isStrict){
31440 var ib = this.el.child('button:first');
31441 if(ib && ib.getWidth() > 20){
31443 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31448 this.el.beginMeasure();
31450 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31451 tbl.setWidth(this.minWidth-tbl2.getWidth());
31454 this.el.endMeasure();
31457 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31461 * Sets this button's click handler
31462 * @param {Function} handler The function to call when the button is clicked
31463 * @param {Object} scope (optional) Scope for the function passed above
31465 setHandler : function(handler, scope){
31466 this.handler = handler;
31467 this.scope = scope;
31471 * Sets this button's arrow click handler
31472 * @param {Function} handler The function to call when the arrow is clicked
31473 * @param {Object} scope (optional) Scope for the function passed above
31475 setArrowHandler : function(handler, scope){
31476 this.arrowHandler = handler;
31477 this.scope = scope;
31483 focus : function(){
31485 this.el.child("button:first").focus();
31490 onClick : function(e){
31491 e.preventDefault();
31492 if(!this.disabled){
31493 if(e.getTarget(".x-btn-menu-arrow-wrap")){
31494 if(this.menu && !this.menu.isVisible()){
31495 this.menu.show(this.el, this.menuAlign);
31497 this.fireEvent("arrowclick", this, e);
31498 if(this.arrowHandler){
31499 this.arrowHandler.call(this.scope || this, this, e);
31502 this.fireEvent("click", this, e);
31504 this.handler.call(this.scope || this, this, e);
31510 onMouseDown : function(e){
31511 if(!this.disabled){
31512 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31516 onMouseUp : function(e){
31517 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31522 // backwards compat
31523 Roo.MenuButton = Roo.SplitButton;/*
31525 * Ext JS Library 1.1.1
31526 * Copyright(c) 2006-2007, Ext JS, LLC.
31528 * Originally Released Under LGPL - original licence link has changed is not relivant.
31531 * <script type="text/javascript">
31535 * @class Roo.Toolbar
31536 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31537 * Basic Toolbar class.
31539 * Creates a new Toolbar
31540 * @param {Object} container The config object
31542 Roo.Toolbar = function(container, buttons, config)
31544 /// old consturctor format still supported..
31545 if(container instanceof Array){ // omit the container for later rendering
31546 buttons = container;
31550 if (typeof(container) == 'object' && container.xtype) {
31551 config = container;
31552 container = config.container;
31553 buttons = config.buttons || []; // not really - use items!!
31556 if (config && config.items) {
31557 xitems = config.items;
31558 delete config.items;
31560 Roo.apply(this, config);
31561 this.buttons = buttons;
31564 this.render(container);
31566 this.xitems = xitems;
31567 Roo.each(xitems, function(b) {
31573 Roo.Toolbar.prototype = {
31575 * @cfg {Array} items
31576 * array of button configs or elements to add (will be converted to a MixedCollection)
31580 * @cfg {String/HTMLElement/Element} container
31581 * The id or element that will contain the toolbar
31584 render : function(ct){
31585 this.el = Roo.get(ct);
31587 this.el.addClass(this.cls);
31589 // using a table allows for vertical alignment
31590 // 100% width is needed by Safari...
31591 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31592 this.tr = this.el.child("tr", true);
31594 this.items = new Roo.util.MixedCollection(false, function(o){
31595 return o.id || ("item" + (++autoId));
31598 this.add.apply(this, this.buttons);
31599 delete this.buttons;
31604 * Adds element(s) to the toolbar -- this function takes a variable number of
31605 * arguments of mixed type and adds them to the toolbar.
31606 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31608 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31609 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31610 * <li>Field: Any form field (equivalent to {@link #addField})</li>
31611 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31612 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31613 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31614 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31615 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31616 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31618 * @param {Mixed} arg2
31619 * @param {Mixed} etc.
31622 var a = arguments, l = a.length;
31623 for(var i = 0; i < l; i++){
31628 _add : function(el) {
31631 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31634 if (el.applyTo){ // some kind of form field
31635 return this.addField(el);
31637 if (el.render){ // some kind of Toolbar.Item
31638 return this.addItem(el);
31640 if (typeof el == "string"){ // string
31641 if(el == "separator" || el == "-"){
31642 return this.addSeparator();
31645 return this.addSpacer();
31648 return this.addFill();
31650 return this.addText(el);
31653 if(el.tagName){ // element
31654 return this.addElement(el);
31656 if(typeof el == "object"){ // must be button config?
31657 return this.addButton(el);
31659 // and now what?!?!
31665 * Add an Xtype element
31666 * @param {Object} xtype Xtype Object
31667 * @return {Object} created Object
31669 addxtype : function(e){
31670 return this.add(e);
31674 * Returns the Element for this toolbar.
31675 * @return {Roo.Element}
31677 getEl : function(){
31683 * @return {Roo.Toolbar.Item} The separator item
31685 addSeparator : function(){
31686 return this.addItem(new Roo.Toolbar.Separator());
31690 * Adds a spacer element
31691 * @return {Roo.Toolbar.Spacer} The spacer item
31693 addSpacer : function(){
31694 return this.addItem(new Roo.Toolbar.Spacer());
31698 * Adds a fill element that forces subsequent additions to the right side of the toolbar
31699 * @return {Roo.Toolbar.Fill} The fill item
31701 addFill : function(){
31702 return this.addItem(new Roo.Toolbar.Fill());
31706 * Adds any standard HTML element to the toolbar
31707 * @param {String/HTMLElement/Element} el The element or id of the element to add
31708 * @return {Roo.Toolbar.Item} The element's item
31710 addElement : function(el){
31711 return this.addItem(new Roo.Toolbar.Item(el));
31714 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31715 * @type Roo.util.MixedCollection
31720 * Adds any Toolbar.Item or subclass
31721 * @param {Roo.Toolbar.Item} item
31722 * @return {Roo.Toolbar.Item} The item
31724 addItem : function(item){
31725 var td = this.nextBlock();
31727 this.items.add(item);
31732 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31733 * @param {Object/Array} config A button config or array of configs
31734 * @return {Roo.Toolbar.Button/Array}
31736 addButton : function(config){
31737 if(config instanceof Array){
31739 for(var i = 0, len = config.length; i < len; i++) {
31740 buttons.push(this.addButton(config[i]));
31745 if(!(config instanceof Roo.Toolbar.Button)){
31747 new Roo.Toolbar.SplitButton(config) :
31748 new Roo.Toolbar.Button(config);
31750 var td = this.nextBlock();
31757 * Adds text to the toolbar
31758 * @param {String} text The text to add
31759 * @return {Roo.Toolbar.Item} The element's item
31761 addText : function(text){
31762 return this.addItem(new Roo.Toolbar.TextItem(text));
31766 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31767 * @param {Number} index The index where the item is to be inserted
31768 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31769 * @return {Roo.Toolbar.Button/Item}
31771 insertButton : function(index, item){
31772 if(item instanceof Array){
31774 for(var i = 0, len = item.length; i < len; i++) {
31775 buttons.push(this.insertButton(index + i, item[i]));
31779 if (!(item instanceof Roo.Toolbar.Button)){
31780 item = new Roo.Toolbar.Button(item);
31782 var td = document.createElement("td");
31783 this.tr.insertBefore(td, this.tr.childNodes[index]);
31785 this.items.insert(index, item);
31790 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31791 * @param {Object} config
31792 * @return {Roo.Toolbar.Item} The element's item
31794 addDom : function(config, returnEl){
31795 var td = this.nextBlock();
31796 Roo.DomHelper.overwrite(td, config);
31797 var ti = new Roo.Toolbar.Item(td.firstChild);
31799 this.items.add(ti);
31804 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31805 * @type Roo.util.MixedCollection
31810 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31811 * Note: the field should not have been rendered yet. For a field that has already been
31812 * rendered, use {@link #addElement}.
31813 * @param {Roo.form.Field} field
31814 * @return {Roo.ToolbarItem}
31818 addField : function(field) {
31819 if (!this.fields) {
31821 this.fields = new Roo.util.MixedCollection(false, function(o){
31822 return o.id || ("item" + (++autoId));
31827 var td = this.nextBlock();
31829 var ti = new Roo.Toolbar.Item(td.firstChild);
31831 this.items.add(ti);
31832 this.fields.add(field);
31843 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31844 this.el.child('div').hide();
31852 this.el.child('div').show();
31856 nextBlock : function(){
31857 var td = document.createElement("td");
31858 this.tr.appendChild(td);
31863 destroy : function(){
31864 if(this.items){ // rendered?
31865 Roo.destroy.apply(Roo, this.items.items);
31867 if(this.fields){ // rendered?
31868 Roo.destroy.apply(Roo, this.fields.items);
31870 Roo.Element.uncache(this.el, this.tr);
31875 * @class Roo.Toolbar.Item
31876 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31878 * Creates a new Item
31879 * @param {HTMLElement} el
31881 Roo.Toolbar.Item = function(el){
31883 if (typeof (el.xtype) != 'undefined') {
31888 this.el = Roo.getDom(el);
31889 this.id = Roo.id(this.el);
31890 this.hidden = false;
31895 * Fires when the button is rendered
31896 * @param {Button} this
31900 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31902 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31903 //Roo.Toolbar.Item.prototype = {
31906 * Get this item's HTML Element
31907 * @return {HTMLElement}
31909 getEl : function(){
31914 render : function(td){
31917 td.appendChild(this.el);
31919 this.fireEvent('render', this);
31923 * Removes and destroys this item.
31925 destroy : function(){
31926 this.td.parentNode.removeChild(this.td);
31933 this.hidden = false;
31934 this.td.style.display = "";
31941 this.hidden = true;
31942 this.td.style.display = "none";
31946 * Convenience function for boolean show/hide.
31947 * @param {Boolean} visible true to show/false to hide
31949 setVisible: function(visible){
31958 * Try to focus this item.
31960 focus : function(){
31961 Roo.fly(this.el).focus();
31965 * Disables this item.
31967 disable : function(){
31968 Roo.fly(this.td).addClass("x-item-disabled");
31969 this.disabled = true;
31970 this.el.disabled = true;
31974 * Enables this item.
31976 enable : function(){
31977 Roo.fly(this.td).removeClass("x-item-disabled");
31978 this.disabled = false;
31979 this.el.disabled = false;
31985 * @class Roo.Toolbar.Separator
31986 * @extends Roo.Toolbar.Item
31987 * A simple toolbar separator class
31989 * Creates a new Separator
31991 Roo.Toolbar.Separator = function(cfg){
31993 var s = document.createElement("span");
31994 s.className = "ytb-sep";
31999 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
32001 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
32002 enable:Roo.emptyFn,
32003 disable:Roo.emptyFn,
32008 * @class Roo.Toolbar.Spacer
32009 * @extends Roo.Toolbar.Item
32010 * A simple element that adds extra horizontal space to a toolbar.
32012 * Creates a new Spacer
32014 Roo.Toolbar.Spacer = function(cfg){
32015 var s = document.createElement("div");
32016 s.className = "ytb-spacer";
32020 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
32022 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
32023 enable:Roo.emptyFn,
32024 disable:Roo.emptyFn,
32029 * @class Roo.Toolbar.Fill
32030 * @extends Roo.Toolbar.Spacer
32031 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32033 * Creates a new Spacer
32035 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32037 render : function(td){
32038 td.style.width = '100%';
32039 Roo.Toolbar.Fill.superclass.render.call(this, td);
32044 * @class Roo.Toolbar.TextItem
32045 * @extends Roo.Toolbar.Item
32046 * A simple class that renders text directly into a toolbar.
32048 * Creates a new TextItem
32049 * @cfg {string} text
32051 Roo.Toolbar.TextItem = function(cfg){
32052 var text = cfg || "";
32053 if (typeof(cfg) == 'object') {
32054 text = cfg.text || "";
32058 var s = document.createElement("span");
32059 s.className = "ytb-text";
32060 s.innerHTML = text;
32065 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
32067 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32070 enable:Roo.emptyFn,
32071 disable:Roo.emptyFn,
32074 * Shows this button
32077 this.hidden = false;
32078 this.el.style.display = "";
32082 * Hides this button
32085 this.hidden = true;
32086 this.el.style.display = "none";
32092 * @class Roo.Toolbar.Button
32093 * @extends Roo.Button
32094 * A button that renders into a toolbar.
32096 * Creates a new Button
32097 * @param {Object} config A standard {@link Roo.Button} config object
32099 Roo.Toolbar.Button = function(config){
32100 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32102 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32106 render : function(td){
32108 Roo.Toolbar.Button.superclass.render.call(this, td);
32112 * Removes and destroys this button
32114 destroy : function(){
32115 Roo.Toolbar.Button.superclass.destroy.call(this);
32116 this.td.parentNode.removeChild(this.td);
32120 * Shows this button
32123 this.hidden = false;
32124 this.td.style.display = "";
32128 * Hides this button
32131 this.hidden = true;
32132 this.td.style.display = "none";
32136 * Disables this item
32138 disable : function(){
32139 Roo.fly(this.td).addClass("x-item-disabled");
32140 this.disabled = true;
32144 * Enables this item
32146 enable : function(){
32147 Roo.fly(this.td).removeClass("x-item-disabled");
32148 this.disabled = false;
32151 // backwards compat
32152 Roo.ToolbarButton = Roo.Toolbar.Button;
32155 * @class Roo.Toolbar.SplitButton
32156 * @extends Roo.SplitButton
32157 * A menu button that renders into a toolbar.
32159 * Creates a new SplitButton
32160 * @param {Object} config A standard {@link Roo.SplitButton} config object
32162 Roo.Toolbar.SplitButton = function(config){
32163 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32165 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32166 render : function(td){
32168 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32172 * Removes and destroys this button
32174 destroy : function(){
32175 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32176 this.td.parentNode.removeChild(this.td);
32180 * Shows this button
32183 this.hidden = false;
32184 this.td.style.display = "";
32188 * Hides this button
32191 this.hidden = true;
32192 this.td.style.display = "none";
32196 // backwards compat
32197 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32199 * Ext JS Library 1.1.1
32200 * Copyright(c) 2006-2007, Ext JS, LLC.
32202 * Originally Released Under LGPL - original licence link has changed is not relivant.
32205 * <script type="text/javascript">
32209 * @class Roo.PagingToolbar
32210 * @extends Roo.Toolbar
32211 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32212 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32214 * Create a new PagingToolbar
32215 * @param {Object} config The config object
32217 Roo.PagingToolbar = function(el, ds, config)
32219 // old args format still supported... - xtype is prefered..
32220 if (typeof(el) == 'object' && el.xtype) {
32221 // created from xtype...
32223 ds = el.dataSource;
32224 el = config.container;
32227 if (config.items) {
32228 items = config.items;
32232 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32235 this.renderButtons(this.el);
32238 // supprot items array.
32240 Roo.each(items, function(e) {
32241 this.add(Roo.factory(e));
32246 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32249 * @cfg {String/HTMLElement/Element} container
32250 * container The id or element that will contain the toolbar
32253 * @cfg {Boolean} displayInfo
32254 * True to display the displayMsg (defaults to false)
32259 * @cfg {Number} pageSize
32260 * The number of records to display per page (defaults to 20)
32264 * @cfg {String} displayMsg
32265 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32267 displayMsg : 'Displaying {0} - {1} of {2}',
32269 * @cfg {String} emptyMsg
32270 * The message to display when no records are found (defaults to "No data to display")
32272 emptyMsg : 'No data to display',
32274 * Customizable piece of the default paging text (defaults to "Page")
32277 beforePageText : "Page",
32279 * Customizable piece of the default paging text (defaults to "of %0")
32282 afterPageText : "of {0}",
32284 * Customizable piece of the default paging text (defaults to "First Page")
32287 firstText : "First Page",
32289 * Customizable piece of the default paging text (defaults to "Previous Page")
32292 prevText : "Previous Page",
32294 * Customizable piece of the default paging text (defaults to "Next Page")
32297 nextText : "Next Page",
32299 * Customizable piece of the default paging text (defaults to "Last Page")
32302 lastText : "Last Page",
32304 * Customizable piece of the default paging text (defaults to "Refresh")
32307 refreshText : "Refresh",
32310 renderButtons : function(el){
32311 Roo.PagingToolbar.superclass.render.call(this, el);
32312 this.first = this.addButton({
32313 tooltip: this.firstText,
32314 cls: "x-btn-icon x-grid-page-first",
32316 handler: this.onClick.createDelegate(this, ["first"])
32318 this.prev = this.addButton({
32319 tooltip: this.prevText,
32320 cls: "x-btn-icon x-grid-page-prev",
32322 handler: this.onClick.createDelegate(this, ["prev"])
32324 //this.addSeparator();
32325 this.add(this.beforePageText);
32326 this.field = Roo.get(this.addDom({
32331 cls: "x-grid-page-number"
32333 this.field.on("keydown", this.onPagingKeydown, this);
32334 this.field.on("focus", function(){this.dom.select();});
32335 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32336 this.field.setHeight(18);
32337 //this.addSeparator();
32338 this.next = this.addButton({
32339 tooltip: this.nextText,
32340 cls: "x-btn-icon x-grid-page-next",
32342 handler: this.onClick.createDelegate(this, ["next"])
32344 this.last = this.addButton({
32345 tooltip: this.lastText,
32346 cls: "x-btn-icon x-grid-page-last",
32348 handler: this.onClick.createDelegate(this, ["last"])
32350 //this.addSeparator();
32351 this.loading = this.addButton({
32352 tooltip: this.refreshText,
32353 cls: "x-btn-icon x-grid-loading",
32354 handler: this.onClick.createDelegate(this, ["refresh"])
32357 if(this.displayInfo){
32358 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32363 updateInfo : function(){
32364 if(this.displayEl){
32365 var count = this.ds.getCount();
32366 var msg = count == 0 ?
32370 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32372 this.displayEl.update(msg);
32377 onLoad : function(ds, r, o){
32378 this.cursor = o.params ? o.params.start : 0;
32379 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32381 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32382 this.field.dom.value = ap;
32383 this.first.setDisabled(ap == 1);
32384 this.prev.setDisabled(ap == 1);
32385 this.next.setDisabled(ap == ps);
32386 this.last.setDisabled(ap == ps);
32387 this.loading.enable();
32392 getPageData : function(){
32393 var total = this.ds.getTotalCount();
32396 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32397 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32402 onLoadError : function(){
32403 this.loading.enable();
32407 onPagingKeydown : function(e){
32408 var k = e.getKey();
32409 var d = this.getPageData();
32411 var v = this.field.dom.value, pageNum;
32412 if(!v || isNaN(pageNum = parseInt(v, 10))){
32413 this.field.dom.value = d.activePage;
32416 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32417 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32420 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))
32422 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32423 this.field.dom.value = pageNum;
32424 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32427 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32429 var v = this.field.dom.value, pageNum;
32430 var increment = (e.shiftKey) ? 10 : 1;
32431 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32434 if(!v || isNaN(pageNum = parseInt(v, 10))) {
32435 this.field.dom.value = d.activePage;
32438 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32440 this.field.dom.value = parseInt(v, 10) + increment;
32441 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32442 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32449 beforeLoad : function(){
32451 this.loading.disable();
32455 * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32456 * @param {String} which (first|prev|next|last|refresh) which button to press.
32460 onClick : function(which){
32464 ds.load({params:{start: 0, limit: this.pageSize}});
32467 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32470 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32473 var total = ds.getTotalCount();
32474 var extra = total % this.pageSize;
32475 var lastStart = extra ? (total - extra) : total-this.pageSize;
32476 ds.load({params:{start: lastStart, limit: this.pageSize}});
32479 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32485 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32486 * @param {Roo.data.Store} store The data store to unbind
32488 unbind : function(ds){
32489 ds.un("beforeload", this.beforeLoad, this);
32490 ds.un("load", this.onLoad, this);
32491 ds.un("loadexception", this.onLoadError, this);
32492 ds.un("remove", this.updateInfo, this);
32493 ds.un("add", this.updateInfo, this);
32494 this.ds = undefined;
32498 * Binds the paging toolbar to the specified {@link Roo.data.Store}
32499 * @param {Roo.data.Store} store The data store to bind
32501 bind : function(ds){
32502 ds.on("beforeload", this.beforeLoad, this);
32503 ds.on("load", this.onLoad, this);
32504 ds.on("loadexception", this.onLoadError, this);
32505 ds.on("remove", this.updateInfo, this);
32506 ds.on("add", this.updateInfo, this);
32511 * Ext JS Library 1.1.1
32512 * Copyright(c) 2006-2007, Ext JS, LLC.
32514 * Originally Released Under LGPL - original licence link has changed is not relivant.
32517 * <script type="text/javascript">
32521 * @class Roo.Resizable
32522 * @extends Roo.util.Observable
32523 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32524 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32525 * 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
32526 * the element will be wrapped for you automatically.</p>
32527 * <p>Here is the list of valid resize handles:</p>
32530 ------ -------------------
32539 'hd' horizontal drag
32542 * <p>Here's an example showing the creation of a typical Resizable:</p>
32544 var resizer = new Roo.Resizable("element-id", {
32552 resizer.on("resize", myHandler);
32554 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32555 * resizer.east.setDisplayed(false);</p>
32556 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32557 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32558 * resize operation's new size (defaults to [0, 0])
32559 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32560 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32561 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32562 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32563 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32564 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32565 * @cfg {Number} width The width of the element in pixels (defaults to null)
32566 * @cfg {Number} height The height of the element in pixels (defaults to null)
32567 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32568 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32569 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32570 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32571 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
32572 * in favor of the handles config option (defaults to false)
32573 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32574 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32575 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32576 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32577 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32578 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32579 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32580 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32581 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32582 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32583 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32585 * Create a new resizable component
32586 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32587 * @param {Object} config configuration options
32589 Roo.Resizable = function(el, config)
32591 this.el = Roo.get(el);
32593 if(config && config.wrap){
32594 config.resizeChild = this.el;
32595 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32596 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32597 this.el.setStyle("overflow", "hidden");
32598 this.el.setPositioning(config.resizeChild.getPositioning());
32599 config.resizeChild.clearPositioning();
32600 if(!config.width || !config.height){
32601 var csize = config.resizeChild.getSize();
32602 this.el.setSize(csize.width, csize.height);
32604 if(config.pinned && !config.adjustments){
32605 config.adjustments = "auto";
32609 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32610 this.proxy.unselectable();
32611 this.proxy.enableDisplayMode('block');
32613 Roo.apply(this, config);
32616 this.disableTrackOver = true;
32617 this.el.addClass("x-resizable-pinned");
32619 // if the element isn't positioned, make it relative
32620 var position = this.el.getStyle("position");
32621 if(position != "absolute" && position != "fixed"){
32622 this.el.setStyle("position", "relative");
32624 if(!this.handles){ // no handles passed, must be legacy style
32625 this.handles = 's,e,se';
32626 if(this.multiDirectional){
32627 this.handles += ',n,w';
32630 if(this.handles == "all"){
32631 this.handles = "n s e w ne nw se sw";
32633 var hs = this.handles.split(/\s*?[,;]\s*?| /);
32634 var ps = Roo.Resizable.positions;
32635 for(var i = 0, len = hs.length; i < len; i++){
32636 if(hs[i] && ps[hs[i]]){
32637 var pos = ps[hs[i]];
32638 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32642 this.corner = this.southeast;
32644 // updateBox = the box can move..
32645 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32646 this.updateBox = true;
32649 this.activeHandle = null;
32651 if(this.resizeChild){
32652 if(typeof this.resizeChild == "boolean"){
32653 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32655 this.resizeChild = Roo.get(this.resizeChild, true);
32659 if(this.adjustments == "auto"){
32660 var rc = this.resizeChild;
32661 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32662 if(rc && (hw || hn)){
32663 rc.position("relative");
32664 rc.setLeft(hw ? hw.el.getWidth() : 0);
32665 rc.setTop(hn ? hn.el.getHeight() : 0);
32667 this.adjustments = [
32668 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32669 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32673 if(this.draggable){
32674 this.dd = this.dynamic ?
32675 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32676 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32682 * @event beforeresize
32683 * Fired before resize is allowed. Set enabled to false to cancel resize.
32684 * @param {Roo.Resizable} this
32685 * @param {Roo.EventObject} e The mousedown event
32687 "beforeresize" : true,
32690 * Fired a resizing.
32691 * @param {Roo.Resizable} this
32692 * @param {Number} x The new x position
32693 * @param {Number} y The new y position
32694 * @param {Number} w The new w width
32695 * @param {Number} h The new h hight
32696 * @param {Roo.EventObject} e The mouseup event
32701 * Fired after a resize.
32702 * @param {Roo.Resizable} this
32703 * @param {Number} width The new width
32704 * @param {Number} height The new height
32705 * @param {Roo.EventObject} e The mouseup event
32710 if(this.width !== null && this.height !== null){
32711 this.resizeTo(this.width, this.height);
32713 this.updateChildSize();
32716 this.el.dom.style.zoom = 1;
32718 Roo.Resizable.superclass.constructor.call(this);
32721 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32722 resizeChild : false,
32723 adjustments : [0, 0],
32733 multiDirectional : false,
32734 disableTrackOver : false,
32735 easing : 'easeOutStrong',
32736 widthIncrement : 0,
32737 heightIncrement : 0,
32741 preserveRatio : false,
32742 transparent: false,
32748 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32750 constrainTo: undefined,
32752 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32754 resizeRegion: undefined,
32758 * Perform a manual resize
32759 * @param {Number} width
32760 * @param {Number} height
32762 resizeTo : function(width, height){
32763 this.el.setSize(width, height);
32764 this.updateChildSize();
32765 this.fireEvent("resize", this, width, height, null);
32769 startSizing : function(e, handle){
32770 this.fireEvent("beforeresize", this, e);
32771 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32774 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
32775 this.overlay.unselectable();
32776 this.overlay.enableDisplayMode("block");
32777 this.overlay.on("mousemove", this.onMouseMove, this);
32778 this.overlay.on("mouseup", this.onMouseUp, this);
32780 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32782 this.resizing = true;
32783 this.startBox = this.el.getBox();
32784 this.startPoint = e.getXY();
32785 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32786 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32788 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32789 this.overlay.show();
32791 if(this.constrainTo) {
32792 var ct = Roo.get(this.constrainTo);
32793 this.resizeRegion = ct.getRegion().adjust(
32794 ct.getFrameWidth('t'),
32795 ct.getFrameWidth('l'),
32796 -ct.getFrameWidth('b'),
32797 -ct.getFrameWidth('r')
32801 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32803 this.proxy.setBox(this.startBox);
32805 this.proxy.setStyle('visibility', 'visible');
32811 onMouseDown : function(handle, e){
32814 this.activeHandle = handle;
32815 this.startSizing(e, handle);
32820 onMouseUp : function(e){
32821 var size = this.resizeElement();
32822 this.resizing = false;
32824 this.overlay.hide();
32826 this.fireEvent("resize", this, size.width, size.height, e);
32830 updateChildSize : function(){
32832 if(this.resizeChild){
32834 var child = this.resizeChild;
32835 var adj = this.adjustments;
32836 if(el.dom.offsetWidth){
32837 var b = el.getSize(true);
32838 child.setSize(b.width+adj[0], b.height+adj[1]);
32840 // Second call here for IE
32841 // The first call enables instant resizing and
32842 // the second call corrects scroll bars if they
32845 setTimeout(function(){
32846 if(el.dom.offsetWidth){
32847 var b = el.getSize(true);
32848 child.setSize(b.width+adj[0], b.height+adj[1]);
32856 snap : function(value, inc, min){
32857 if(!inc || !value) {
32860 var newValue = value;
32861 var m = value % inc;
32864 newValue = value + (inc-m);
32866 newValue = value - m;
32869 return Math.max(min, newValue);
32873 resizeElement : function(){
32874 var box = this.proxy.getBox();
32875 if(this.updateBox){
32876 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32878 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32880 this.updateChildSize();
32888 constrain : function(v, diff, m, mx){
32891 }else if(v - diff > mx){
32898 onMouseMove : function(e){
32901 try{// try catch so if something goes wrong the user doesn't get hung
32903 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32907 //var curXY = this.startPoint;
32908 var curSize = this.curSize || this.startBox;
32909 var x = this.startBox.x, y = this.startBox.y;
32910 var ox = x, oy = y;
32911 var w = curSize.width, h = curSize.height;
32912 var ow = w, oh = h;
32913 var mw = this.minWidth, mh = this.minHeight;
32914 var mxw = this.maxWidth, mxh = this.maxHeight;
32915 var wi = this.widthIncrement;
32916 var hi = this.heightIncrement;
32918 var eventXY = e.getXY();
32919 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32920 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32922 var pos = this.activeHandle.position;
32927 w = Math.min(Math.max(mw, w), mxw);
32932 h = Math.min(Math.max(mh, h), mxh);
32937 w = Math.min(Math.max(mw, w), mxw);
32938 h = Math.min(Math.max(mh, h), mxh);
32941 diffY = this.constrain(h, diffY, mh, mxh);
32948 var adiffX = Math.abs(diffX);
32949 var sub = (adiffX % wi); // how much
32950 if (sub > (wi/2)) { // far enough to snap
32951 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32953 // remove difference..
32954 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32958 x = Math.max(this.minX, x);
32961 diffX = this.constrain(w, diffX, mw, mxw);
32967 w = Math.min(Math.max(mw, w), mxw);
32968 diffY = this.constrain(h, diffY, mh, mxh);
32973 diffX = this.constrain(w, diffX, mw, mxw);
32974 diffY = this.constrain(h, diffY, mh, mxh);
32981 diffX = this.constrain(w, diffX, mw, mxw);
32983 h = Math.min(Math.max(mh, h), mxh);
32989 var sw = this.snap(w, wi, mw);
32990 var sh = this.snap(h, hi, mh);
32991 if(sw != w || sh != h){
33014 if(this.preserveRatio){
33019 h = Math.min(Math.max(mh, h), mxh);
33024 w = Math.min(Math.max(mw, w), mxw);
33029 w = Math.min(Math.max(mw, w), mxw);
33035 w = Math.min(Math.max(mw, w), mxw);
33041 h = Math.min(Math.max(mh, h), mxh);
33049 h = Math.min(Math.max(mh, h), mxh);
33059 h = Math.min(Math.max(mh, h), mxh);
33067 if (pos == 'hdrag') {
33070 this.proxy.setBounds(x, y, w, h);
33072 this.resizeElement();
33076 this.fireEvent("resizing", this, x, y, w, h, e);
33080 handleOver : function(){
33082 this.el.addClass("x-resizable-over");
33087 handleOut : function(){
33088 if(!this.resizing){
33089 this.el.removeClass("x-resizable-over");
33094 * Returns the element this component is bound to.
33095 * @return {Roo.Element}
33097 getEl : function(){
33102 * Returns the resizeChild element (or null).
33103 * @return {Roo.Element}
33105 getResizeChild : function(){
33106 return this.resizeChild;
33108 groupHandler : function()
33113 * Destroys this resizable. If the element was wrapped and
33114 * removeEl is not true then the element remains.
33115 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33117 destroy : function(removeEl){
33118 this.proxy.remove();
33120 this.overlay.removeAllListeners();
33121 this.overlay.remove();
33123 var ps = Roo.Resizable.positions;
33125 if(typeof ps[k] != "function" && this[ps[k]]){
33126 var h = this[ps[k]];
33127 h.el.removeAllListeners();
33132 this.el.update("");
33139 // hash to map config positions to true positions
33140 Roo.Resizable.positions = {
33141 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
33146 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33148 // only initialize the template if resizable is used
33149 var tpl = Roo.DomHelper.createTemplate(
33150 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33153 Roo.Resizable.Handle.prototype.tpl = tpl;
33155 this.position = pos;
33157 // show north drag fro topdra
33158 var handlepos = pos == 'hdrag' ? 'north' : pos;
33160 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33161 if (pos == 'hdrag') {
33162 this.el.setStyle('cursor', 'pointer');
33164 this.el.unselectable();
33166 this.el.setOpacity(0);
33168 this.el.on("mousedown", this.onMouseDown, this);
33169 if(!disableTrackOver){
33170 this.el.on("mouseover", this.onMouseOver, this);
33171 this.el.on("mouseout", this.onMouseOut, this);
33176 Roo.Resizable.Handle.prototype = {
33177 afterResize : function(rz){
33182 onMouseDown : function(e){
33183 this.rz.onMouseDown(this, e);
33186 onMouseOver : function(e){
33187 this.rz.handleOver(this, e);
33190 onMouseOut : function(e){
33191 this.rz.handleOut(this, e);
33195 * Ext JS Library 1.1.1
33196 * Copyright(c) 2006-2007, Ext JS, LLC.
33198 * Originally Released Under LGPL - original licence link has changed is not relivant.
33201 * <script type="text/javascript">
33205 * @class Roo.Editor
33206 * @extends Roo.Component
33207 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33209 * Create a new Editor
33210 * @param {Roo.form.Field} field The Field object (or descendant)
33211 * @param {Object} config The config object
33213 Roo.Editor = function(field, config){
33214 Roo.Editor.superclass.constructor.call(this, config);
33215 this.field = field;
33218 * @event beforestartedit
33219 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
33220 * false from the handler of this event.
33221 * @param {Editor} this
33222 * @param {Roo.Element} boundEl The underlying element bound to this editor
33223 * @param {Mixed} value The field value being set
33225 "beforestartedit" : true,
33228 * Fires when this editor is displayed
33229 * @param {Roo.Element} boundEl The underlying element bound to this editor
33230 * @param {Mixed} value The starting field value
33232 "startedit" : true,
33234 * @event beforecomplete
33235 * Fires after a change has been made to the field, but before the change is reflected in the underlying
33236 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
33237 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33238 * event will not fire since no edit actually occurred.
33239 * @param {Editor} this
33240 * @param {Mixed} value The current field value
33241 * @param {Mixed} startValue The original field value
33243 "beforecomplete" : true,
33246 * Fires after editing is complete and any changed value has been written to the underlying field.
33247 * @param {Editor} this
33248 * @param {Mixed} value The current field value
33249 * @param {Mixed} startValue The original field value
33253 * @event specialkey
33254 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
33255 * {@link Roo.EventObject#getKey} to determine which key was pressed.
33256 * @param {Roo.form.Field} this
33257 * @param {Roo.EventObject} e The event object
33259 "specialkey" : true
33263 Roo.extend(Roo.Editor, Roo.Component, {
33265 * @cfg {Boolean/String} autosize
33266 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33267 * or "height" to adopt the height only (defaults to false)
33270 * @cfg {Boolean} revertInvalid
33271 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33272 * validation fails (defaults to true)
33275 * @cfg {Boolean} ignoreNoChange
33276 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33277 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
33278 * will never be ignored.
33281 * @cfg {Boolean} hideEl
33282 * False to keep the bound element visible while the editor is displayed (defaults to true)
33285 * @cfg {Mixed} value
33286 * The data value of the underlying field (defaults to "")
33290 * @cfg {String} alignment
33291 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33295 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33296 * for bottom-right shadow (defaults to "frame")
33300 * @cfg {Boolean} constrain True to constrain the editor to the viewport
33304 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33306 completeOnEnter : false,
33308 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33310 cancelOnEsc : false,
33312 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33317 onRender : function(ct, position){
33318 this.el = new Roo.Layer({
33319 shadow: this.shadow,
33325 constrain: this.constrain
33327 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33328 if(this.field.msgTarget != 'title'){
33329 this.field.msgTarget = 'qtip';
33331 this.field.render(this.el);
33333 this.field.el.dom.setAttribute('autocomplete', 'off');
33335 this.field.on("specialkey", this.onSpecialKey, this);
33336 if(this.swallowKeys){
33337 this.field.el.swallowEvent(['keydown','keypress']);
33340 this.field.on("blur", this.onBlur, this);
33341 if(this.field.grow){
33342 this.field.on("autosize", this.el.sync, this.el, {delay:1});
33346 onSpecialKey : function(field, e)
33348 //Roo.log('editor onSpecialKey');
33349 if(this.completeOnEnter && e.getKey() == e.ENTER){
33351 this.completeEdit();
33354 // do not fire special key otherwise it might hide close the editor...
33355 if(e.getKey() == e.ENTER){
33358 if(this.cancelOnEsc && e.getKey() == e.ESC){
33362 this.fireEvent('specialkey', field, e);
33367 * Starts the editing process and shows the editor.
33368 * @param {String/HTMLElement/Element} el The element to edit
33369 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33370 * to the innerHTML of el.
33372 startEdit : function(el, value){
33374 this.completeEdit();
33376 this.boundEl = Roo.get(el);
33377 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33378 if(!this.rendered){
33379 this.render(this.parentEl || document.body);
33381 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33384 this.startValue = v;
33385 this.field.setValue(v);
33387 var sz = this.boundEl.getSize();
33388 switch(this.autoSize){
33390 this.setSize(sz.width, "");
33393 this.setSize("", sz.height);
33396 this.setSize(sz.width, sz.height);
33399 this.el.alignTo(this.boundEl, this.alignment);
33400 this.editing = true;
33402 Roo.QuickTips.disable();
33408 * Sets the height and width of this editor.
33409 * @param {Number} width The new width
33410 * @param {Number} height The new height
33412 setSize : function(w, h){
33413 this.field.setSize(w, h);
33420 * Realigns the editor to the bound field based on the current alignment config value.
33422 realign : function(){
33423 this.el.alignTo(this.boundEl, this.alignment);
33427 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33428 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33430 completeEdit : function(remainVisible){
33434 var v = this.getValue();
33435 if(this.revertInvalid !== false && !this.field.isValid()){
33436 v = this.startValue;
33437 this.cancelEdit(true);
33439 if(String(v) === String(this.startValue) && this.ignoreNoChange){
33440 this.editing = false;
33444 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33445 this.editing = false;
33446 if(this.updateEl && this.boundEl){
33447 this.boundEl.update(v);
33449 if(remainVisible !== true){
33452 this.fireEvent("complete", this, v, this.startValue);
33457 onShow : function(){
33459 if(this.hideEl !== false){
33460 this.boundEl.hide();
33463 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33464 this.fixIEFocus = true;
33465 this.deferredFocus.defer(50, this);
33467 this.field.focus();
33469 this.fireEvent("startedit", this.boundEl, this.startValue);
33472 deferredFocus : function(){
33474 this.field.focus();
33479 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
33480 * reverted to the original starting value.
33481 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33482 * cancel (defaults to false)
33484 cancelEdit : function(remainVisible){
33486 this.setValue(this.startValue);
33487 if(remainVisible !== true){
33494 onBlur : function(){
33495 if(this.allowBlur !== true && this.editing){
33496 this.completeEdit();
33501 onHide : function(){
33503 this.completeEdit();
33507 if(this.field.collapse){
33508 this.field.collapse();
33511 if(this.hideEl !== false){
33512 this.boundEl.show();
33515 Roo.QuickTips.enable();
33520 * Sets the data value of the editor
33521 * @param {Mixed} value Any valid value supported by the underlying field
33523 setValue : function(v){
33524 this.field.setValue(v);
33528 * Gets the data value of the editor
33529 * @return {Mixed} The data value
33531 getValue : function(){
33532 return this.field.getValue();
33536 * Ext JS Library 1.1.1
33537 * Copyright(c) 2006-2007, Ext JS, LLC.
33539 * Originally Released Under LGPL - original licence link has changed is not relivant.
33542 * <script type="text/javascript">
33546 * @class Roo.BasicDialog
33547 * @extends Roo.util.Observable
33548 * @parent none builder
33549 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
33551 var dlg = new Roo.BasicDialog("my-dlg", {
33560 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33561 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
33562 dlg.addButton('Cancel', dlg.hide, dlg);
33565 <b>A Dialog should always be a direct child of the body element.</b>
33566 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33567 * @cfg {String} title Default text to display in the title bar (defaults to null)
33568 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33569 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33570 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33571 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33572 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33573 * (defaults to null with no animation)
33574 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33575 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33576 * property for valid values (defaults to 'all')
33577 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33578 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33579 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33580 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33581 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33582 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33583 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33584 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33585 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33586 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33587 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33588 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33589 * draggable = true (defaults to false)
33590 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33591 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33592 * shadow (defaults to false)
33593 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33594 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33595 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33596 * @cfg {Array} buttons Array of buttons
33597 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33599 * Create a new BasicDialog.
33600 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33601 * @param {Object} config Configuration options
33603 Roo.BasicDialog = function(el, config){
33604 this.el = Roo.get(el);
33605 var dh = Roo.DomHelper;
33606 if(!this.el && config && config.autoCreate){
33607 if(typeof config.autoCreate == "object"){
33608 if(!config.autoCreate.id){
33609 config.autoCreate.id = el;
33611 this.el = dh.append(document.body,
33612 config.autoCreate, true);
33614 this.el = dh.append(document.body,
33615 {tag: "div", id: el, style:'visibility:hidden;'}, true);
33619 el.setDisplayed(true);
33620 el.hide = this.hideAction;
33622 el.addClass("x-dlg");
33624 Roo.apply(this, config);
33626 this.proxy = el.createProxy("x-dlg-proxy");
33627 this.proxy.hide = this.hideAction;
33628 this.proxy.setOpacity(.5);
33632 el.setWidth(config.width);
33635 el.setHeight(config.height);
33637 this.size = el.getSize();
33638 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33639 this.xy = [config.x,config.y];
33641 this.xy = el.getCenterXY(true);
33643 /** The header element @type Roo.Element */
33644 this.header = el.child("> .x-dlg-hd");
33645 /** The body element @type Roo.Element */
33646 this.body = el.child("> .x-dlg-bd");
33647 /** The footer element @type Roo.Element */
33648 this.footer = el.child("> .x-dlg-ft");
33651 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
33654 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33657 this.header.unselectable();
33659 this.header.update(this.title);
33661 // this element allows the dialog to be focused for keyboard event
33662 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33663 this.focusEl.swallowEvent("click", true);
33665 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33667 // wrap the body and footer for special rendering
33668 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33670 this.bwrap.dom.appendChild(this.footer.dom);
33673 this.bg = this.el.createChild({
33674 tag: "div", cls:"x-dlg-bg",
33675 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
33677 this.centerBg = this.bg.child("div.x-dlg-bg-center");
33680 if(this.autoScroll !== false && !this.autoTabs){
33681 this.body.setStyle("overflow", "auto");
33684 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33686 if(this.closable !== false){
33687 this.el.addClass("x-dlg-closable");
33688 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33689 this.close.on("click", this.closeClick, this);
33690 this.close.addClassOnOver("x-dlg-close-over");
33692 if(this.collapsible !== false){
33693 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33694 this.collapseBtn.on("click", this.collapseClick, this);
33695 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33696 this.header.on("dblclick", this.collapseClick, this);
33698 if(this.resizable !== false){
33699 this.el.addClass("x-dlg-resizable");
33700 this.resizer = new Roo.Resizable(el, {
33701 minWidth: this.minWidth || 80,
33702 minHeight:this.minHeight || 80,
33703 handles: this.resizeHandles || "all",
33706 this.resizer.on("beforeresize", this.beforeResize, this);
33707 this.resizer.on("resize", this.onResize, this);
33709 if(this.draggable !== false){
33710 el.addClass("x-dlg-draggable");
33711 if (!this.proxyDrag) {
33712 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33715 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33717 dd.setHandleElId(this.header.id);
33718 dd.endDrag = this.endMove.createDelegate(this);
33719 dd.startDrag = this.startMove.createDelegate(this);
33720 dd.onDrag = this.onDrag.createDelegate(this);
33725 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33726 this.mask.enableDisplayMode("block");
33728 this.el.addClass("x-dlg-modal");
33731 this.shadow = new Roo.Shadow({
33732 mode : typeof this.shadow == "string" ? this.shadow : "sides",
33733 offset : this.shadowOffset
33736 this.shadowOffset = 0;
33738 if(Roo.useShims && this.shim !== false){
33739 this.shim = this.el.createShim();
33740 this.shim.hide = this.hideAction;
33748 if (this.buttons) {
33749 var bts= this.buttons;
33751 Roo.each(bts, function(b) {
33760 * Fires when a key is pressed
33761 * @param {Roo.BasicDialog} this
33762 * @param {Roo.EventObject} e
33767 * Fires when this dialog is moved by the user.
33768 * @param {Roo.BasicDialog} this
33769 * @param {Number} x The new page X
33770 * @param {Number} y The new page Y
33775 * Fires when this dialog is resized by the user.
33776 * @param {Roo.BasicDialog} this
33777 * @param {Number} width The new width
33778 * @param {Number} height The new height
33782 * @event beforehide
33783 * Fires before this dialog is hidden.
33784 * @param {Roo.BasicDialog} this
33786 "beforehide" : true,
33789 * Fires when this dialog is hidden.
33790 * @param {Roo.BasicDialog} this
33794 * @event beforeshow
33795 * Fires before this dialog is shown.
33796 * @param {Roo.BasicDialog} this
33798 "beforeshow" : true,
33801 * Fires when this dialog is shown.
33802 * @param {Roo.BasicDialog} this
33806 el.on("keydown", this.onKeyDown, this);
33807 el.on("mousedown", this.toFront, this);
33808 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33810 Roo.DialogManager.register(this);
33811 Roo.BasicDialog.superclass.constructor.call(this);
33814 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33815 shadowOffset: Roo.isIE ? 6 : 5,
33818 minButtonWidth: 75,
33819 defaultButton: null,
33820 buttonAlign: "right",
33825 * Sets the dialog title text
33826 * @param {String} text The title text to display
33827 * @return {Roo.BasicDialog} this
33829 setTitle : function(text){
33830 this.header.update(text);
33835 closeClick : function(){
33840 collapseClick : function(){
33841 this[this.collapsed ? "expand" : "collapse"]();
33845 * Collapses the dialog to its minimized state (only the title bar is visible).
33846 * Equivalent to the user clicking the collapse dialog button.
33848 collapse : function(){
33849 if(!this.collapsed){
33850 this.collapsed = true;
33851 this.el.addClass("x-dlg-collapsed");
33852 this.restoreHeight = this.el.getHeight();
33853 this.resizeTo(this.el.getWidth(), this.header.getHeight());
33858 * Expands a collapsed dialog back to its normal state. Equivalent to the user
33859 * clicking the expand dialog button.
33861 expand : function(){
33862 if(this.collapsed){
33863 this.collapsed = false;
33864 this.el.removeClass("x-dlg-collapsed");
33865 this.resizeTo(this.el.getWidth(), this.restoreHeight);
33870 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33871 * @return {Roo.TabPanel} The tabs component
33873 initTabs : function(){
33874 var tabs = this.getTabs();
33875 while(tabs.getTab(0)){
33878 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33880 tabs.addTab(Roo.id(dom), dom.title);
33888 beforeResize : function(){
33889 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33893 onResize : function(){
33894 this.refreshSize();
33895 this.syncBodyHeight();
33896 this.adjustAssets();
33898 this.fireEvent("resize", this, this.size.width, this.size.height);
33902 onKeyDown : function(e){
33903 if(this.isVisible()){
33904 this.fireEvent("keydown", this, e);
33909 * Resizes the dialog.
33910 * @param {Number} width
33911 * @param {Number} height
33912 * @return {Roo.BasicDialog} this
33914 resizeTo : function(width, height){
33915 this.el.setSize(width, height);
33916 this.size = {width: width, height: height};
33917 this.syncBodyHeight();
33918 if(this.fixedcenter){
33921 if(this.isVisible()){
33922 this.constrainXY();
33923 this.adjustAssets();
33925 this.fireEvent("resize", this, width, height);
33931 * Resizes the dialog to fit the specified content size.
33932 * @param {Number} width
33933 * @param {Number} height
33934 * @return {Roo.BasicDialog} this
33936 setContentSize : function(w, h){
33937 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33938 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33939 //if(!this.el.isBorderBox()){
33940 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33941 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33944 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33945 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33947 this.resizeTo(w, h);
33952 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
33953 * executed in response to a particular key being pressed while the dialog is active.
33954 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33955 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33956 * @param {Function} fn The function to call
33957 * @param {Object} scope (optional) The scope of the function
33958 * @return {Roo.BasicDialog} this
33960 addKeyListener : function(key, fn, scope){
33961 var keyCode, shift, ctrl, alt;
33962 if(typeof key == "object" && !(key instanceof Array)){
33963 keyCode = key["key"];
33964 shift = key["shift"];
33965 ctrl = key["ctrl"];
33970 var handler = function(dlg, e){
33971 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
33972 var k = e.getKey();
33973 if(keyCode instanceof Array){
33974 for(var i = 0, len = keyCode.length; i < len; i++){
33975 if(keyCode[i] == k){
33976 fn.call(scope || window, dlg, k, e);
33982 fn.call(scope || window, dlg, k, e);
33987 this.on("keydown", handler);
33992 * Returns the TabPanel component (creates it if it doesn't exist).
33993 * Note: If you wish to simply check for the existence of tabs without creating them,
33994 * check for a null 'tabs' property.
33995 * @return {Roo.TabPanel} The tabs component
33997 getTabs : function(){
33999 this.el.addClass("x-dlg-auto-tabs");
34000 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
34001 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
34007 * Adds a button to the footer section of the dialog.
34008 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
34009 * object or a valid Roo.DomHelper element config
34010 * @param {Function} handler The function called when the button is clicked
34011 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
34012 * @return {Roo.Button} The new button
34014 addButton : function(config, handler, scope){
34015 var dh = Roo.DomHelper;
34017 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
34019 if(!this.btnContainer){
34020 var tb = this.footer.createChild({
34022 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
34023 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34025 this.btnContainer = tb.firstChild.firstChild.firstChild;
34030 minWidth: this.minButtonWidth,
34033 if(typeof config == "string"){
34034 bconfig.text = config;
34037 bconfig.dhconfig = config;
34039 Roo.apply(bconfig, config);
34043 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34044 bconfig.position = Math.max(0, bconfig.position);
34045 fc = this.btnContainer.childNodes[bconfig.position];
34048 var btn = new Roo.Button(
34050 this.btnContainer.insertBefore(document.createElement("td"),fc)
34051 : this.btnContainer.appendChild(document.createElement("td")),
34052 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
34055 this.syncBodyHeight();
34058 * Array of all the buttons that have been added to this dialog via addButton
34063 this.buttons.push(btn);
34068 * Sets the default button to be focused when the dialog is displayed.
34069 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34070 * @return {Roo.BasicDialog} this
34072 setDefaultButton : function(btn){
34073 this.defaultButton = btn;
34078 getHeaderFooterHeight : function(safe){
34081 height += this.header.getHeight();
34084 var fm = this.footer.getMargins();
34085 height += (this.footer.getHeight()+fm.top+fm.bottom);
34087 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34088 height += this.centerBg.getPadding("tb");
34093 syncBodyHeight : function()
34095 var bd = this.body, // the text
34096 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34098 var height = this.size.height - this.getHeaderFooterHeight(false);
34099 bd.setHeight(height-bd.getMargins("tb"));
34100 var hh = this.header.getHeight();
34101 var h = this.size.height-hh;
34104 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34105 bw.setHeight(h-cb.getPadding("tb"));
34107 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34108 bd.setWidth(bw.getWidth(true));
34110 this.tabs.syncHeight();
34112 this.tabs.el.repaint();
34118 * Restores the previous state of the dialog if Roo.state is configured.
34119 * @return {Roo.BasicDialog} this
34121 restoreState : function(){
34122 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34123 if(box && box.width){
34124 this.xy = [box.x, box.y];
34125 this.resizeTo(box.width, box.height);
34131 beforeShow : function(){
34133 if(this.fixedcenter){
34134 this.xy = this.el.getCenterXY(true);
34137 Roo.get(document.body).addClass("x-body-masked");
34138 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34141 this.constrainXY();
34145 animShow : function(){
34146 var b = Roo.get(this.animateTarget).getBox();
34147 this.proxy.setSize(b.width, b.height);
34148 this.proxy.setLocation(b.x, b.y);
34150 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34151 true, .35, this.showEl.createDelegate(this));
34155 * Shows the dialog.
34156 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34157 * @return {Roo.BasicDialog} this
34159 show : function(animateTarget){
34160 if (this.fireEvent("beforeshow", this) === false){
34163 if(this.syncHeightBeforeShow){
34164 this.syncBodyHeight();
34165 }else if(this.firstShow){
34166 this.firstShow = false;
34167 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34169 this.animateTarget = animateTarget || this.animateTarget;
34170 if(!this.el.isVisible()){
34172 if(this.animateTarget && Roo.get(this.animateTarget)){
34182 showEl : function(){
34184 this.el.setXY(this.xy);
34186 this.adjustAssets(true);
34189 // IE peekaboo bug - fix found by Dave Fenwick
34193 this.fireEvent("show", this);
34197 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
34198 * dialog itself will receive focus.
34200 focus : function(){
34201 if(this.defaultButton){
34202 this.defaultButton.focus();
34204 this.focusEl.focus();
34209 constrainXY : function(){
34210 if(this.constraintoviewport !== false){
34211 if(!this.viewSize){
34212 if(this.container){
34213 var s = this.container.getSize();
34214 this.viewSize = [s.width, s.height];
34216 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34219 var s = Roo.get(this.container||document).getScroll();
34221 var x = this.xy[0], y = this.xy[1];
34222 var w = this.size.width, h = this.size.height;
34223 var vw = this.viewSize[0], vh = this.viewSize[1];
34224 // only move it if it needs it
34226 // first validate right/bottom
34227 if(x + w > vw+s.left){
34231 if(y + h > vh+s.top){
34235 // then make sure top/left isn't negative
34247 if(this.isVisible()){
34248 this.el.setLocation(x, y);
34249 this.adjustAssets();
34256 onDrag : function(){
34257 if(!this.proxyDrag){
34258 this.xy = this.el.getXY();
34259 this.adjustAssets();
34264 adjustAssets : function(doShow){
34265 var x = this.xy[0], y = this.xy[1];
34266 var w = this.size.width, h = this.size.height;
34267 if(doShow === true){
34269 this.shadow.show(this.el);
34275 if(this.shadow && this.shadow.isVisible()){
34276 this.shadow.show(this.el);
34278 if(this.shim && this.shim.isVisible()){
34279 this.shim.setBounds(x, y, w, h);
34284 adjustViewport : function(w, h){
34286 w = Roo.lib.Dom.getViewWidth();
34287 h = Roo.lib.Dom.getViewHeight();
34290 this.viewSize = [w, h];
34291 if(this.modal && this.mask.isVisible()){
34292 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34293 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34295 if(this.isVisible()){
34296 this.constrainXY();
34301 * Destroys this dialog and all its supporting elements (including any tabs, shim,
34302 * shadow, proxy, mask, etc.) Also removes all event listeners.
34303 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34305 destroy : function(removeEl){
34306 if(this.isVisible()){
34307 this.animateTarget = null;
34310 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34312 this.tabs.destroy(removeEl);
34325 for(var i = 0, len = this.buttons.length; i < len; i++){
34326 this.buttons[i].destroy();
34329 this.el.removeAllListeners();
34330 if(removeEl === true){
34331 this.el.update("");
34334 Roo.DialogManager.unregister(this);
34338 startMove : function(){
34339 if(this.proxyDrag){
34342 if(this.constraintoviewport !== false){
34343 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34348 endMove : function(){
34349 if(!this.proxyDrag){
34350 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34352 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34355 this.refreshSize();
34356 this.adjustAssets();
34358 this.fireEvent("move", this, this.xy[0], this.xy[1]);
34362 * Brings this dialog to the front of any other visible dialogs
34363 * @return {Roo.BasicDialog} this
34365 toFront : function(){
34366 Roo.DialogManager.bringToFront(this);
34371 * Sends this dialog to the back (under) of any other visible dialogs
34372 * @return {Roo.BasicDialog} this
34374 toBack : function(){
34375 Roo.DialogManager.sendToBack(this);
34380 * Centers this dialog in the viewport
34381 * @return {Roo.BasicDialog} this
34383 center : function(){
34384 var xy = this.el.getCenterXY(true);
34385 this.moveTo(xy[0], xy[1]);
34390 * Moves the dialog's top-left corner to the specified point
34391 * @param {Number} x
34392 * @param {Number} y
34393 * @return {Roo.BasicDialog} this
34395 moveTo : function(x, y){
34397 if(this.isVisible()){
34398 this.el.setXY(this.xy);
34399 this.adjustAssets();
34405 * Aligns the dialog to the specified element
34406 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34407 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34408 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34409 * @return {Roo.BasicDialog} this
34411 alignTo : function(element, position, offsets){
34412 this.xy = this.el.getAlignToXY(element, position, offsets);
34413 if(this.isVisible()){
34414 this.el.setXY(this.xy);
34415 this.adjustAssets();
34421 * Anchors an element to another element and realigns it when the window is resized.
34422 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34423 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34424 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34425 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34426 * is a number, it is used as the buffer delay (defaults to 50ms).
34427 * @return {Roo.BasicDialog} this
34429 anchorTo : function(el, alignment, offsets, monitorScroll){
34430 var action = function(){
34431 this.alignTo(el, alignment, offsets);
34433 Roo.EventManager.onWindowResize(action, this);
34434 var tm = typeof monitorScroll;
34435 if(tm != 'undefined'){
34436 Roo.EventManager.on(window, 'scroll', action, this,
34437 {buffer: tm == 'number' ? monitorScroll : 50});
34444 * Returns true if the dialog is visible
34445 * @return {Boolean}
34447 isVisible : function(){
34448 return this.el.isVisible();
34452 animHide : function(callback){
34453 var b = Roo.get(this.animateTarget).getBox();
34455 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34457 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34458 this.hideEl.createDelegate(this, [callback]));
34462 * Hides the dialog.
34463 * @param {Function} callback (optional) Function to call when the dialog is hidden
34464 * @return {Roo.BasicDialog} this
34466 hide : function(callback){
34467 if (this.fireEvent("beforehide", this) === false){
34471 this.shadow.hide();
34476 // sometimes animateTarget seems to get set.. causing problems...
34477 // this just double checks..
34478 if(this.animateTarget && Roo.get(this.animateTarget)) {
34479 this.animHide(callback);
34482 this.hideEl(callback);
34488 hideEl : function(callback){
34492 Roo.get(document.body).removeClass("x-body-masked");
34494 this.fireEvent("hide", this);
34495 if(typeof callback == "function"){
34501 hideAction : function(){
34502 this.setLeft("-10000px");
34503 this.setTop("-10000px");
34504 this.setStyle("visibility", "hidden");
34508 refreshSize : function(){
34509 this.size = this.el.getSize();
34510 this.xy = this.el.getXY();
34511 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34515 // z-index is managed by the DialogManager and may be overwritten at any time
34516 setZIndex : function(index){
34518 this.mask.setStyle("z-index", index);
34521 this.shim.setStyle("z-index", ++index);
34524 this.shadow.setZIndex(++index);
34526 this.el.setStyle("z-index", ++index);
34528 this.proxy.setStyle("z-index", ++index);
34531 this.resizer.proxy.setStyle("z-index", ++index);
34534 this.lastZIndex = index;
34538 * Returns the element for this dialog
34539 * @return {Roo.Element} The underlying dialog Element
34541 getEl : function(){
34547 * @class Roo.DialogManager
34548 * Provides global access to BasicDialogs that have been created and
34549 * support for z-indexing (layering) multiple open dialogs.
34551 Roo.DialogManager = function(){
34553 var accessList = [];
34557 var sortDialogs = function(d1, d2){
34558 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34562 var orderDialogs = function(){
34563 accessList.sort(sortDialogs);
34564 var seed = Roo.DialogManager.zseed;
34565 for(var i = 0, len = accessList.length; i < len; i++){
34566 var dlg = accessList[i];
34568 dlg.setZIndex(seed + (i*10));
34575 * The starting z-index for BasicDialogs (defaults to 9000)
34576 * @type Number The z-index value
34581 register : function(dlg){
34582 list[dlg.id] = dlg;
34583 accessList.push(dlg);
34587 unregister : function(dlg){
34588 delete list[dlg.id];
34591 if(!accessList.indexOf){
34592 for( i = 0, len = accessList.length; i < len; i++){
34593 if(accessList[i] == dlg){
34594 accessList.splice(i, 1);
34599 i = accessList.indexOf(dlg);
34601 accessList.splice(i, 1);
34607 * Gets a registered dialog by id
34608 * @param {String/Object} id The id of the dialog or a dialog
34609 * @return {Roo.BasicDialog} this
34611 get : function(id){
34612 return typeof id == "object" ? id : list[id];
34616 * Brings the specified dialog to the front
34617 * @param {String/Object} dlg The id of the dialog or a dialog
34618 * @return {Roo.BasicDialog} this
34620 bringToFront : function(dlg){
34621 dlg = this.get(dlg);
34624 dlg._lastAccess = new Date().getTime();
34631 * Sends the specified dialog to the back
34632 * @param {String/Object} dlg The id of the dialog or a dialog
34633 * @return {Roo.BasicDialog} this
34635 sendToBack : function(dlg){
34636 dlg = this.get(dlg);
34637 dlg._lastAccess = -(new Date().getTime());
34643 * Hides all dialogs
34645 hideAll : function(){
34646 for(var id in list){
34647 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34656 * @class Roo.LayoutDialog
34657 * @extends Roo.BasicDialog
34658 * @children Roo.ContentPanel
34659 * @parent builder none
34660 * Dialog which provides adjustments for working with a layout in a Dialog.
34661 * Add your necessary layout config options to the dialog's config.<br>
34662 * Example usage (including a nested layout):
34665 dialog = new Roo.LayoutDialog("download-dlg", {
34674 // layout config merges with the dialog config
34676 tabPosition: "top",
34677 alwaysShowTabs: true
34680 dialog.addKeyListener(27, dialog.hide, dialog);
34681 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34682 dialog.addButton("Build It!", this.getDownload, this);
34684 // we can even add nested layouts
34685 var innerLayout = new Roo.BorderLayout("dl-inner", {
34695 innerLayout.beginUpdate();
34696 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34697 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34698 innerLayout.endUpdate(true);
34700 var layout = dialog.getLayout();
34701 layout.beginUpdate();
34702 layout.add("center", new Roo.ContentPanel("standard-panel",
34703 {title: "Download the Source", fitToFrame:true}));
34704 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34705 {title: "Build your own roo.js"}));
34706 layout.getRegion("center").showPanel(sp);
34707 layout.endUpdate();
34711 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34712 * @param {Object} config configuration options
34714 Roo.LayoutDialog = function(el, cfg){
34717 if (typeof(cfg) == 'undefined') {
34718 config = Roo.apply({}, el);
34719 // not sure why we use documentElement here.. - it should always be body.
34720 // IE7 borks horribly if we use documentElement.
34721 // webkit also does not like documentElement - it creates a body element...
34722 el = Roo.get( document.body || document.documentElement ).createChild();
34723 //config.autoCreate = true;
34727 config.autoTabs = false;
34728 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34729 this.body.setStyle({overflow:"hidden", position:"relative"});
34730 this.layout = new Roo.BorderLayout(this.body.dom, config);
34731 this.layout.monitorWindowResize = false;
34732 this.el.addClass("x-dlg-auto-layout");
34733 // fix case when center region overwrites center function
34734 this.center = Roo.BasicDialog.prototype.center;
34735 this.on("show", this.layout.layout, this.layout, true);
34736 if (config.items) {
34737 var xitems = config.items;
34738 delete config.items;
34739 Roo.each(xitems, this.addxtype, this);
34744 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34748 * @cfg {Roo.LayoutRegion} east
34751 * @cfg {Roo.LayoutRegion} west
34754 * @cfg {Roo.LayoutRegion} south
34757 * @cfg {Roo.LayoutRegion} north
34760 * @cfg {Roo.LayoutRegion} center
34763 * @cfg {Roo.Button} buttons[] Bottom buttons..
34768 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34771 endUpdate : function(){
34772 this.layout.endUpdate();
34776 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34779 beginUpdate : function(){
34780 this.layout.beginUpdate();
34784 * Get the BorderLayout for this dialog
34785 * @return {Roo.BorderLayout}
34787 getLayout : function(){
34788 return this.layout;
34791 showEl : function(){
34792 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34794 this.layout.layout();
34799 // Use the syncHeightBeforeShow config option to control this automatically
34800 syncBodyHeight : function(){
34801 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34802 if(this.layout){this.layout.layout();}
34806 * Add an xtype element (actually adds to the layout.)
34807 * @return {Object} xdata xtype object data.
34810 addxtype : function(c) {
34811 return this.layout.addxtype(c);
34815 * Ext JS Library 1.1.1
34816 * Copyright(c) 2006-2007, Ext JS, LLC.
34818 * Originally Released Under LGPL - original licence link has changed is not relivant.
34821 * <script type="text/javascript">
34825 * @class Roo.MessageBox
34827 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
34831 Roo.Msg.alert('Status', 'Changes saved successfully.');
34833 // Prompt for user data:
34834 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34836 // process text value...
34840 // Show a dialog using config options:
34842 title:'Save Changes?',
34843 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34844 buttons: Roo.Msg.YESNOCANCEL,
34851 Roo.MessageBox = function(){
34852 var dlg, opt, mask, waitTimer;
34853 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34854 var buttons, activeTextEl, bwidth;
34857 var handleButton = function(button){
34859 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34863 var handleHide = function(){
34864 if(opt && opt.cls){
34865 dlg.el.removeClass(opt.cls);
34868 Roo.TaskMgr.stop(waitTimer);
34874 var updateButtons = function(b){
34877 buttons["ok"].hide();
34878 buttons["cancel"].hide();
34879 buttons["yes"].hide();
34880 buttons["no"].hide();
34881 dlg.footer.dom.style.display = 'none';
34884 dlg.footer.dom.style.display = '';
34885 for(var k in buttons){
34886 if(typeof buttons[k] != "function"){
34889 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34890 width += buttons[k].el.getWidth()+15;
34900 var handleEsc = function(d, k, e){
34901 if(opt && opt.closable !== false){
34911 * Returns a reference to the underlying {@link Roo.BasicDialog} element
34912 * @return {Roo.BasicDialog} The BasicDialog element
34914 getDialog : function(){
34916 dlg = new Roo.BasicDialog("x-msg-box", {
34921 constraintoviewport:false,
34923 collapsible : false,
34926 width:400, height:100,
34927 buttonAlign:"center",
34928 closeClick : function(){
34929 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34930 handleButton("no");
34932 handleButton("cancel");
34937 dlg.on("hide", handleHide);
34939 dlg.addKeyListener(27, handleEsc);
34941 var bt = this.buttonText;
34942 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34943 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34944 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34945 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34946 bodyEl = dlg.body.createChild({
34948 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>'
34950 msgEl = bodyEl.dom.firstChild;
34951 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34952 textboxEl.enableDisplayMode();
34953 textboxEl.addKeyListener([10,13], function(){
34954 if(dlg.isVisible() && opt && opt.buttons){
34955 if(opt.buttons.ok){
34956 handleButton("ok");
34957 }else if(opt.buttons.yes){
34958 handleButton("yes");
34962 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34963 textareaEl.enableDisplayMode();
34964 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34965 progressEl.enableDisplayMode();
34966 var pf = progressEl.dom.firstChild;
34968 pp = Roo.get(pf.firstChild);
34969 pp.setHeight(pf.offsetHeight);
34977 * Updates the message box body text
34978 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34979 * the XHTML-compliant non-breaking space character '&#160;')
34980 * @return {Roo.MessageBox} This message box
34982 updateText : function(text){
34983 if(!dlg.isVisible() && !opt.width){
34984 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34986 msgEl.innerHTML = text || ' ';
34988 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34989 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34991 Math.min(opt.width || cw , this.maxWidth),
34992 Math.max(opt.minWidth || this.minWidth, bwidth)
34995 activeTextEl.setWidth(w);
34997 if(dlg.isVisible()){
34998 dlg.fixedcenter = false;
35000 // to big, make it scroll. = But as usual stupid IE does not support
35003 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
35004 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
35005 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
35007 bodyEl.dom.style.height = '';
35008 bodyEl.dom.style.overflowY = '';
35011 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
35013 bodyEl.dom.style.overflowX = '';
35016 dlg.setContentSize(w, bodyEl.getHeight());
35017 if(dlg.isVisible()){
35018 dlg.fixedcenter = true;
35024 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
35025 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35026 * @param {Number} value Any number between 0 and 1 (e.g., .5)
35027 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35028 * @return {Roo.MessageBox} This message box
35030 updateProgress : function(value, text){
35032 this.updateText(text);
35034 if (pp) { // weird bug on my firefox - for some reason this is not defined
35035 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35041 * Returns true if the message box is currently displayed
35042 * @return {Boolean} True if the message box is visible, else false
35044 isVisible : function(){
35045 return dlg && dlg.isVisible();
35049 * Hides the message box if it is displayed
35052 if(this.isVisible()){
35058 * Displays a new message box, or reinitializes an existing message box, based on the config options
35059 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35060 * The following config object properties are supported:
35062 Property Type Description
35063 ---------- --------------- ------------------------------------------------------------------------------------
35064 animEl String/Element An id or Element from which the message box should animate as it opens and
35065 closes (defaults to undefined)
35066 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35067 cancel:'Bar'}), or false to not show any buttons (defaults to false)
35068 closable Boolean False to hide the top-right close button (defaults to true). Note that
35069 progress and wait dialogs will ignore this property and always hide the
35070 close button as they can only be closed programmatically.
35071 cls String A custom CSS class to apply to the message box element
35072 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
35073 displayed (defaults to 75)
35074 fn Function A callback function to execute after closing the dialog. The arguments to the
35075 function will be btn (the name of the button that was clicked, if applicable,
35076 e.g. "ok"), and text (the value of the active text field, if applicable).
35077 Progress and wait dialogs will ignore this option since they do not respond to
35078 user actions and can only be closed programmatically, so any required function
35079 should be called by the same code after it closes the dialog.
35080 icon String A CSS class that provides a background image to be used as an icon for
35081 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35082 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
35083 minWidth Number The minimum width in pixels of the message box (defaults to 100)
35084 modal Boolean False to allow user interaction with the page while the message box is
35085 displayed (defaults to true)
35086 msg String A string that will replace the existing message box body text (defaults
35087 to the XHTML-compliant non-breaking space character ' ')
35088 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
35089 progress Boolean True to display a progress bar (defaults to false)
35090 progressText String The text to display inside the progress bar if progress = true (defaults to '')
35091 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
35092 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
35093 title String The title text
35094 value String The string value to set into the active textbox element if displayed
35095 wait Boolean True to display a progress bar (defaults to false)
35096 width Number The width of the dialog in pixels
35103 msg: 'Please enter your address:',
35105 buttons: Roo.MessageBox.OKCANCEL,
35108 animEl: 'addAddressBtn'
35111 * @param {Object} config Configuration options
35112 * @return {Roo.MessageBox} This message box
35114 show : function(options)
35117 // this causes nightmares if you show one dialog after another
35118 // especially on callbacks..
35120 if(this.isVisible()){
35123 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35124 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
35125 Roo.log("New Dialog Message:" + options.msg )
35126 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35127 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35130 var d = this.getDialog();
35132 d.setTitle(opt.title || " ");
35133 d.close.setDisplayed(opt.closable !== false);
35134 activeTextEl = textboxEl;
35135 opt.prompt = opt.prompt || (opt.multiline ? true : false);
35140 textareaEl.setHeight(typeof opt.multiline == "number" ?
35141 opt.multiline : this.defaultTextHeight);
35142 activeTextEl = textareaEl;
35151 progressEl.setDisplayed(opt.progress === true);
35152 this.updateProgress(0);
35153 activeTextEl.dom.value = opt.value || "";
35155 dlg.setDefaultButton(activeTextEl);
35157 var bs = opt.buttons;
35160 db = buttons["ok"];
35161 }else if(bs && bs.yes){
35162 db = buttons["yes"];
35164 dlg.setDefaultButton(db);
35166 bwidth = updateButtons(opt.buttons);
35167 this.updateText(opt.msg);
35169 d.el.addClass(opt.cls);
35171 d.proxyDrag = opt.proxyDrag === true;
35172 d.modal = opt.modal !== false;
35173 d.mask = opt.modal !== false ? mask : false;
35174 if(!d.isVisible()){
35175 // force it to the end of the z-index stack so it gets a cursor in FF
35176 document.body.appendChild(dlg.el.dom);
35177 d.animateTarget = null;
35178 d.show(options.animEl);
35185 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
35186 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35187 * and closing the message box when the process is complete.
35188 * @param {String} title The title bar text
35189 * @param {String} msg The message box body text
35190 * @return {Roo.MessageBox} This message box
35192 progress : function(title, msg){
35199 minWidth: this.minProgressWidth,
35206 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35207 * If a callback function is passed it will be called after the user clicks the button, and the
35208 * id of the button that was clicked will be passed as the only parameter to the callback
35209 * (could also be the top-right close button).
35210 * @param {String} title The title bar text
35211 * @param {String} msg The message box body text
35212 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35213 * @param {Object} scope (optional) The scope of the callback function
35214 * @return {Roo.MessageBox} This message box
35216 alert : function(title, msg, fn, scope){
35229 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
35230 * interaction while waiting for a long-running process to complete that does not have defined intervals.
35231 * You are responsible for closing the message box when the process is complete.
35232 * @param {String} msg The message box body text
35233 * @param {String} title (optional) The title bar text
35234 * @return {Roo.MessageBox} This message box
35236 wait : function(msg, title){
35247 waitTimer = Roo.TaskMgr.start({
35249 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35257 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35258 * If a callback function is passed it will be called after the user clicks either button, and the id of the
35259 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
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 * @return {Roo.MessageBox} This message box
35266 confirm : function(title, msg, fn, scope){
35270 buttons: this.YESNO,
35279 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35280 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
35281 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35282 * (could also be the top-right close button) and the text that was entered will be passed as the two
35283 * parameters to the callback.
35284 * @param {String} title The title bar text
35285 * @param {String} msg The message box body text
35286 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35287 * @param {Object} scope (optional) The scope of the callback function
35288 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35289 * property, or the height in pixels to create the textbox (defaults to false / single-line)
35290 * @return {Roo.MessageBox} This message box
35292 prompt : function(title, msg, fn, scope, multiline){
35296 buttons: this.OKCANCEL,
35301 multiline: multiline,
35308 * Button config that displays a single OK button
35313 * Button config that displays Yes and No buttons
35316 YESNO : {yes:true, no:true},
35318 * Button config that displays OK and Cancel buttons
35321 OKCANCEL : {ok:true, cancel:true},
35323 * Button config that displays Yes, No and Cancel buttons
35326 YESNOCANCEL : {yes:true, no:true, cancel:true},
35329 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35332 defaultTextHeight : 75,
35334 * The maximum width in pixels of the message box (defaults to 600)
35339 * The minimum width in pixels of the message box (defaults to 100)
35344 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
35345 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35348 minProgressWidth : 250,
35350 * An object containing the default button text strings that can be overriden for localized language support.
35351 * Supported properties are: ok, cancel, yes and no.
35352 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35365 * Shorthand for {@link Roo.MessageBox}
35367 Roo.Msg = Roo.MessageBox;/*
35369 * Ext JS Library 1.1.1
35370 * Copyright(c) 2006-2007, Ext JS, LLC.
35372 * Originally Released Under LGPL - original licence link has changed is not relivant.
35375 * <script type="text/javascript">
35378 * @class Roo.QuickTips
35379 * Provides attractive and customizable tooltips for any element.
35382 Roo.QuickTips = function(){
35383 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35384 var ce, bd, xy, dd;
35385 var visible = false, disabled = true, inited = false;
35386 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35388 var onOver = function(e){
35392 var t = e.getTarget();
35393 if(!t || t.nodeType !== 1 || t == document || t == document.body){
35396 if(ce && t == ce.el){
35397 clearTimeout(hideProc);
35400 if(t && tagEls[t.id]){
35401 tagEls[t.id].el = t;
35402 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35405 var ttp, et = Roo.fly(t);
35406 var ns = cfg.namespace;
35407 if(tm.interceptTitles && t.title){
35410 t.removeAttribute("title");
35411 e.preventDefault();
35413 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35416 showProc = show.defer(tm.showDelay, tm, [{
35418 text: ttp.replace(/\\n/g,'<br/>'),
35419 width: et.getAttributeNS(ns, cfg.width),
35420 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35421 title: et.getAttributeNS(ns, cfg.title),
35422 cls: et.getAttributeNS(ns, cfg.cls)
35427 var onOut = function(e){
35428 clearTimeout(showProc);
35429 var t = e.getTarget();
35430 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35431 hideProc = setTimeout(hide, tm.hideDelay);
35435 var onMove = function(e){
35441 if(tm.trackMouse && ce){
35446 var onDown = function(e){
35447 clearTimeout(showProc);
35448 clearTimeout(hideProc);
35450 if(tm.hideOnClick){
35453 tm.enable.defer(100, tm);
35458 var getPad = function(){
35459 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35462 var show = function(o){
35466 clearTimeout(dismissProc);
35468 if(removeCls){ // in case manually hidden
35469 el.removeClass(removeCls);
35473 el.addClass(ce.cls);
35474 removeCls = ce.cls;
35477 tipTitle.update(ce.title);
35480 tipTitle.update('');
35483 el.dom.style.width = tm.maxWidth+'px';
35484 //tipBody.dom.style.width = '';
35485 tipBodyText.update(o.text);
35486 var p = getPad(), w = ce.width;
35488 var td = tipBodyText.dom;
35489 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35490 if(aw > tm.maxWidth){
35492 }else if(aw < tm.minWidth){
35498 //tipBody.setWidth(w);
35499 el.setWidth(parseInt(w, 10) + p);
35500 if(ce.autoHide === false){
35501 close.setDisplayed(true);
35506 close.setDisplayed(false);
35512 el.avoidY = xy[1]-18;
35517 el.setStyle("visibility", "visible");
35518 el.fadeIn({callback: afterShow});
35524 var afterShow = function(){
35528 if(tm.autoDismiss && ce.autoHide !== false){
35529 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35534 var hide = function(noanim){
35535 clearTimeout(dismissProc);
35536 clearTimeout(hideProc);
35538 if(el.isVisible()){
35540 if(noanim !== true && tm.animate){
35541 el.fadeOut({callback: afterHide});
35548 var afterHide = function(){
35551 el.removeClass(removeCls);
35558 * @cfg {Number} minWidth
35559 * The minimum width of the quick tip (defaults to 40)
35563 * @cfg {Number} maxWidth
35564 * The maximum width of the quick tip (defaults to 300)
35568 * @cfg {Boolean} interceptTitles
35569 * True to automatically use the element's DOM title value if available (defaults to false)
35571 interceptTitles : false,
35573 * @cfg {Boolean} trackMouse
35574 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35576 trackMouse : false,
35578 * @cfg {Boolean} hideOnClick
35579 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35581 hideOnClick : true,
35583 * @cfg {Number} showDelay
35584 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35588 * @cfg {Number} hideDelay
35589 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35593 * @cfg {Boolean} autoHide
35594 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35595 * Used in conjunction with hideDelay.
35600 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35601 * (defaults to true). Used in conjunction with autoDismissDelay.
35603 autoDismiss : true,
35606 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35608 autoDismissDelay : 5000,
35610 * @cfg {Boolean} animate
35611 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35616 * @cfg {String} title
35617 * Title text to display (defaults to ''). This can be any valid HTML markup.
35621 * @cfg {String} text
35622 * Body text to display (defaults to ''). This can be any valid HTML markup.
35626 * @cfg {String} cls
35627 * A CSS class to apply to the base quick tip element (defaults to '').
35631 * @cfg {Number} width
35632 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
35633 * minWidth or maxWidth.
35638 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
35639 * or display QuickTips in a page.
35642 tm = Roo.QuickTips;
35643 cfg = tm.tagConfig;
35645 if(!Roo.isReady){ // allow calling of init() before onReady
35646 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35649 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35650 el.fxDefaults = {stopFx: true};
35651 // maximum custom styling
35652 //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>');
35653 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>');
35654 tipTitle = el.child('h3');
35655 tipTitle.enableDisplayMode("block");
35656 tipBody = el.child('div.x-tip-bd');
35657 tipBodyText = el.child('div.x-tip-bd-inner');
35658 //bdLeft = el.child('div.x-tip-bd-left');
35659 //bdRight = el.child('div.x-tip-bd-right');
35660 close = el.child('div.x-tip-close');
35661 close.enableDisplayMode("block");
35662 close.on("click", hide);
35663 var d = Roo.get(document);
35664 d.on("mousedown", onDown);
35665 d.on("mouseover", onOver);
35666 d.on("mouseout", onOut);
35667 d.on("mousemove", onMove);
35668 esc = d.addKeyListener(27, hide);
35671 dd = el.initDD("default", null, {
35672 onDrag : function(){
35676 dd.setHandleElId(tipTitle.id);
35685 * Configures a new quick tip instance and assigns it to a target element. The following config options
35688 Property Type Description
35689 ---------- --------------------- ------------------------------------------------------------------------
35690 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
35692 * @param {Object} config The config object
35694 register : function(config){
35695 var cs = config instanceof Array ? config : arguments;
35696 for(var i = 0, len = cs.length; i < len; i++) {
35698 var target = c.target;
35700 if(target instanceof Array){
35701 for(var j = 0, jlen = target.length; j < jlen; j++){
35702 tagEls[target[j]] = c;
35705 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35712 * Removes this quick tip from its element and destroys it.
35713 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35715 unregister : function(el){
35716 delete tagEls[Roo.id(el)];
35720 * Enable this quick tip.
35722 enable : function(){
35723 if(inited && disabled){
35725 if(locks.length < 1){
35732 * Disable this quick tip.
35734 disable : function(){
35736 clearTimeout(showProc);
35737 clearTimeout(hideProc);
35738 clearTimeout(dismissProc);
35746 * Returns true if the quick tip is enabled, else false.
35748 isEnabled : function(){
35754 namespace : "roo", // was ext?? this may break..
35755 alt_namespace : "ext",
35756 attribute : "qtip",
35766 // backwards compat
35767 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35769 * Ext JS Library 1.1.1
35770 * Copyright(c) 2006-2007, Ext JS, LLC.
35772 * Originally Released Under LGPL - original licence link has changed is not relivant.
35775 * <script type="text/javascript">
35780 * @class Roo.tree.TreePanel
35781 * @extends Roo.data.Tree
35782 * @cfg {Roo.tree.TreeNode} root The root node
35783 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35784 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35785 * @cfg {Boolean} enableDD true to enable drag and drop
35786 * @cfg {Boolean} enableDrag true to enable just drag
35787 * @cfg {Boolean} enableDrop true to enable just drop
35788 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35789 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35790 * @cfg {String} ddGroup The DD group this TreePanel belongs to
35791 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35792 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35793 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35794 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35795 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35796 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35797 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35798 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35799 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35800 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35801 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35802 * @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>
35803 * @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>
35806 * @param {String/HTMLElement/Element} el The container element
35807 * @param {Object} config
35809 Roo.tree.TreePanel = function(el, config){
35811 var loader = false;
35813 root = config.root;
35814 delete config.root;
35816 if (config.loader) {
35817 loader = config.loader;
35818 delete config.loader;
35821 Roo.apply(this, config);
35822 Roo.tree.TreePanel.superclass.constructor.call(this);
35823 this.el = Roo.get(el);
35824 this.el.addClass('x-tree');
35825 //console.log(root);
35827 this.setRootNode( Roo.factory(root, Roo.tree));
35830 this.loader = Roo.factory(loader, Roo.tree);
35833 * Read-only. The id of the container element becomes this TreePanel's id.
35835 this.id = this.el.id;
35838 * @event beforeload
35839 * Fires before a node is loaded, return false to cancel
35840 * @param {Node} node The node being loaded
35842 "beforeload" : true,
35845 * Fires when a node is loaded
35846 * @param {Node} node The node that was loaded
35850 * @event textchange
35851 * Fires when the text for a node is changed
35852 * @param {Node} node The node
35853 * @param {String} text The new text
35854 * @param {String} oldText The old text
35856 "textchange" : true,
35858 * @event beforeexpand
35859 * Fires before a node is expanded, return false to cancel.
35860 * @param {Node} node The node
35861 * @param {Boolean} deep
35862 * @param {Boolean} anim
35864 "beforeexpand" : true,
35866 * @event beforecollapse
35867 * Fires before a node is collapsed, return false to cancel.
35868 * @param {Node} node The node
35869 * @param {Boolean} deep
35870 * @param {Boolean} anim
35872 "beforecollapse" : true,
35875 * Fires when a node is expanded
35876 * @param {Node} node The node
35880 * @event disabledchange
35881 * Fires when the disabled status of a node changes
35882 * @param {Node} node The node
35883 * @param {Boolean} disabled
35885 "disabledchange" : true,
35888 * Fires when a node is collapsed
35889 * @param {Node} node The node
35893 * @event beforeclick
35894 * Fires before click processing on a node. Return false to cancel the default action.
35895 * @param {Node} node The node
35896 * @param {Roo.EventObject} e The event object
35898 "beforeclick":true,
35900 * @event checkchange
35901 * Fires when a node with a checkbox's checked property changes
35902 * @param {Node} this This node
35903 * @param {Boolean} checked
35905 "checkchange":true,
35908 * Fires when a node is clicked
35909 * @param {Node} node The node
35910 * @param {Roo.EventObject} e The event object
35915 * Fires when a node is double clicked
35916 * @param {Node} node The node
35917 * @param {Roo.EventObject} e The event object
35921 * @event contextmenu
35922 * Fires when a node is right clicked
35923 * @param {Node} node The node
35924 * @param {Roo.EventObject} e The event object
35926 "contextmenu":true,
35928 * @event beforechildrenrendered
35929 * Fires right before the child nodes for a node are rendered
35930 * @param {Node} node The node
35932 "beforechildrenrendered":true,
35935 * Fires when a node starts being dragged
35936 * @param {Roo.tree.TreePanel} this
35937 * @param {Roo.tree.TreeNode} node
35938 * @param {event} e The raw browser event
35940 "startdrag" : true,
35943 * Fires when a drag operation is complete
35944 * @param {Roo.tree.TreePanel} this
35945 * @param {Roo.tree.TreeNode} node
35946 * @param {event} e The raw browser event
35951 * Fires when a dragged node is dropped on a valid DD target
35952 * @param {Roo.tree.TreePanel} this
35953 * @param {Roo.tree.TreeNode} node
35954 * @param {DD} dd The dd it was dropped on
35955 * @param {event} e The raw browser event
35959 * @event beforenodedrop
35960 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35961 * passed to handlers has the following properties:<br />
35962 * <ul style="padding:5px;padding-left:16px;">
35963 * <li>tree - The TreePanel</li>
35964 * <li>target - The node being targeted for the drop</li>
35965 * <li>data - The drag data from the drag source</li>
35966 * <li>point - The point of the drop - append, above or below</li>
35967 * <li>source - The drag source</li>
35968 * <li>rawEvent - Raw mouse event</li>
35969 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35970 * to be inserted by setting them on this object.</li>
35971 * <li>cancel - Set this to true to cancel the drop.</li>
35973 * @param {Object} dropEvent
35975 "beforenodedrop" : true,
35978 * Fires after a DD object is dropped on a node in this tree. The dropEvent
35979 * passed to handlers has the following properties:<br />
35980 * <ul style="padding:5px;padding-left:16px;">
35981 * <li>tree - The TreePanel</li>
35982 * <li>target - The node being targeted for the drop</li>
35983 * <li>data - The drag data from the drag source</li>
35984 * <li>point - The point of the drop - append, above or below</li>
35985 * <li>source - The drag source</li>
35986 * <li>rawEvent - Raw mouse event</li>
35987 * <li>dropNode - Dropped node(s).</li>
35989 * @param {Object} dropEvent
35993 * @event nodedragover
35994 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35995 * passed to handlers has the following properties:<br />
35996 * <ul style="padding:5px;padding-left:16px;">
35997 * <li>tree - The TreePanel</li>
35998 * <li>target - The node being targeted for the drop</li>
35999 * <li>data - The drag data from the drag source</li>
36000 * <li>point - The point of the drop - append, above or below</li>
36001 * <li>source - The drag source</li>
36002 * <li>rawEvent - Raw mouse event</li>
36003 * <li>dropNode - Drop node(s) provided by the source.</li>
36004 * <li>cancel - Set this to true to signal drop not allowed.</li>
36006 * @param {Object} dragOverEvent
36008 "nodedragover" : true,
36010 * @event appendnode
36011 * Fires when append node to the tree
36012 * @param {Roo.tree.TreePanel} this
36013 * @param {Roo.tree.TreeNode} node
36014 * @param {Number} index The index of the newly appended node
36016 "appendnode" : true
36019 if(this.singleExpand){
36020 this.on("beforeexpand", this.restrictExpand, this);
36023 this.editor.tree = this;
36024 this.editor = Roo.factory(this.editor, Roo.tree);
36027 if (this.selModel) {
36028 this.selModel = Roo.factory(this.selModel, Roo.tree);
36032 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36033 rootVisible : true,
36034 animate: Roo.enableFx,
36037 hlDrop : Roo.enableFx,
36041 rendererTip: false,
36043 restrictExpand : function(node){
36044 var p = node.parentNode;
36046 if(p.expandedChild && p.expandedChild.parentNode == p){
36047 p.expandedChild.collapse();
36049 p.expandedChild = node;
36053 // private override
36054 setRootNode : function(node){
36055 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36056 if(!this.rootVisible){
36057 node.ui = new Roo.tree.RootTreeNodeUI(node);
36063 * Returns the container element for this TreePanel
36065 getEl : function(){
36070 * Returns the default TreeLoader for this TreePanel
36072 getLoader : function(){
36073 return this.loader;
36079 expandAll : function(){
36080 this.root.expand(true);
36084 * Collapse all nodes
36086 collapseAll : function(){
36087 this.root.collapse(true);
36091 * Returns the selection model used by this TreePanel
36093 getSelectionModel : function(){
36094 if(!this.selModel){
36095 this.selModel = new Roo.tree.DefaultSelectionModel();
36097 return this.selModel;
36101 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36102 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36103 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36106 getChecked : function(a, startNode){
36107 startNode = startNode || this.root;
36109 var f = function(){
36110 if(this.attributes.checked){
36111 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36114 startNode.cascade(f);
36119 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36120 * @param {String} path
36121 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36122 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36123 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36125 expandPath : function(path, attr, callback){
36126 attr = attr || "id";
36127 var keys = path.split(this.pathSeparator);
36128 var curNode = this.root;
36129 if(curNode.attributes[attr] != keys[1]){ // invalid root
36131 callback(false, null);
36136 var f = function(){
36137 if(++index == keys.length){
36139 callback(true, curNode);
36143 var c = curNode.findChild(attr, keys[index]);
36146 callback(false, curNode);
36151 c.expand(false, false, f);
36153 curNode.expand(false, false, f);
36157 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36158 * @param {String} path
36159 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36160 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36161 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36163 selectPath : function(path, attr, callback){
36164 attr = attr || "id";
36165 var keys = path.split(this.pathSeparator);
36166 var v = keys.pop();
36167 if(keys.length > 0){
36168 var f = function(success, node){
36169 if(success && node){
36170 var n = node.findChild(attr, v);
36176 }else if(callback){
36177 callback(false, n);
36181 callback(false, n);
36185 this.expandPath(keys.join(this.pathSeparator), attr, f);
36187 this.root.select();
36189 callback(true, this.root);
36194 getTreeEl : function(){
36199 * Trigger rendering of this TreePanel
36201 render : function(){
36202 if (this.innerCt) {
36203 return this; // stop it rendering more than once!!
36206 this.innerCt = this.el.createChild({tag:"ul",
36207 cls:"x-tree-root-ct " +
36208 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36210 if(this.containerScroll){
36211 Roo.dd.ScrollManager.register(this.el);
36213 if((this.enableDD || this.enableDrop) && !this.dropZone){
36215 * The dropZone used by this tree if drop is enabled
36216 * @type Roo.tree.TreeDropZone
36218 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36219 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36222 if((this.enableDD || this.enableDrag) && !this.dragZone){
36224 * The dragZone used by this tree if drag is enabled
36225 * @type Roo.tree.TreeDragZone
36227 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36228 ddGroup: this.ddGroup || "TreeDD",
36229 scroll: this.ddScroll
36232 this.getSelectionModel().init(this);
36234 Roo.log("ROOT not set in tree");
36237 this.root.render();
36238 if(!this.rootVisible){
36239 this.root.renderChildren();
36245 * Ext JS Library 1.1.1
36246 * Copyright(c) 2006-2007, Ext JS, LLC.
36248 * Originally Released Under LGPL - original licence link has changed is not relivant.
36251 * <script type="text/javascript">
36256 * @class Roo.tree.DefaultSelectionModel
36257 * @extends Roo.util.Observable
36258 * The default single selection for a TreePanel.
36259 * @param {Object} cfg Configuration
36261 Roo.tree.DefaultSelectionModel = function(cfg){
36262 this.selNode = null;
36268 * @event selectionchange
36269 * Fires when the selected node changes
36270 * @param {DefaultSelectionModel} this
36271 * @param {TreeNode} node the new selection
36273 "selectionchange" : true,
36276 * @event beforeselect
36277 * Fires before the selected node changes, return false to cancel the change
36278 * @param {DefaultSelectionModel} this
36279 * @param {TreeNode} node the new selection
36280 * @param {TreeNode} node the old selection
36282 "beforeselect" : true
36285 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36288 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36289 init : function(tree){
36291 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36292 tree.on("click", this.onNodeClick, this);
36295 onNodeClick : function(node, e){
36296 if (e.ctrlKey && this.selNode == node) {
36297 this.unselect(node);
36305 * @param {TreeNode} node The node to select
36306 * @return {TreeNode} The selected node
36308 select : function(node){
36309 var last = this.selNode;
36310 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36312 last.ui.onSelectedChange(false);
36314 this.selNode = node;
36315 node.ui.onSelectedChange(true);
36316 this.fireEvent("selectionchange", this, node, last);
36323 * @param {TreeNode} node The node to unselect
36325 unselect : function(node){
36326 if(this.selNode == node){
36327 this.clearSelections();
36332 * Clear all selections
36334 clearSelections : function(){
36335 var n = this.selNode;
36337 n.ui.onSelectedChange(false);
36338 this.selNode = null;
36339 this.fireEvent("selectionchange", this, null);
36345 * Get the selected node
36346 * @return {TreeNode} The selected node
36348 getSelectedNode : function(){
36349 return this.selNode;
36353 * Returns true if the node is selected
36354 * @param {TreeNode} node The node to check
36355 * @return {Boolean}
36357 isSelected : function(node){
36358 return this.selNode == node;
36362 * Selects the node above the selected node in the tree, intelligently walking the nodes
36363 * @return TreeNode The new selection
36365 selectPrevious : function(){
36366 var s = this.selNode || this.lastSelNode;
36370 var ps = s.previousSibling;
36372 if(!ps.isExpanded() || ps.childNodes.length < 1){
36373 return this.select(ps);
36375 var lc = ps.lastChild;
36376 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36379 return this.select(lc);
36381 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36382 return this.select(s.parentNode);
36388 * Selects the node above the selected node in the tree, intelligently walking the nodes
36389 * @return TreeNode The new selection
36391 selectNext : function(){
36392 var s = this.selNode || this.lastSelNode;
36396 if(s.firstChild && s.isExpanded()){
36397 return this.select(s.firstChild);
36398 }else if(s.nextSibling){
36399 return this.select(s.nextSibling);
36400 }else if(s.parentNode){
36402 s.parentNode.bubble(function(){
36403 if(this.nextSibling){
36404 newS = this.getOwnerTree().selModel.select(this.nextSibling);
36413 onKeyDown : function(e){
36414 var s = this.selNode || this.lastSelNode;
36415 // undesirable, but required
36420 var k = e.getKey();
36428 this.selectPrevious();
36431 e.preventDefault();
36432 if(s.hasChildNodes()){
36433 if(!s.isExpanded()){
36435 }else if(s.firstChild){
36436 this.select(s.firstChild, e);
36441 e.preventDefault();
36442 if(s.hasChildNodes() && s.isExpanded()){
36444 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36445 this.select(s.parentNode, e);
36453 * @class Roo.tree.MultiSelectionModel
36454 * @extends Roo.util.Observable
36455 * Multi selection for a TreePanel.
36456 * @param {Object} cfg Configuration
36458 Roo.tree.MultiSelectionModel = function(){
36459 this.selNodes = [];
36463 * @event selectionchange
36464 * Fires when the selected nodes change
36465 * @param {MultiSelectionModel} this
36466 * @param {Array} nodes Array of the selected nodes
36468 "selectionchange" : true
36470 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36474 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36475 init : function(tree){
36477 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36478 tree.on("click", this.onNodeClick, this);
36481 onNodeClick : function(node, e){
36482 this.select(node, e, e.ctrlKey);
36487 * @param {TreeNode} node The node to select
36488 * @param {EventObject} e (optional) An event associated with the selection
36489 * @param {Boolean} keepExisting True to retain existing selections
36490 * @return {TreeNode} The selected node
36492 select : function(node, e, keepExisting){
36493 if(keepExisting !== true){
36494 this.clearSelections(true);
36496 if(this.isSelected(node)){
36497 this.lastSelNode = node;
36500 this.selNodes.push(node);
36501 this.selMap[node.id] = node;
36502 this.lastSelNode = node;
36503 node.ui.onSelectedChange(true);
36504 this.fireEvent("selectionchange", this, this.selNodes);
36510 * @param {TreeNode} node The node to unselect
36512 unselect : function(node){
36513 if(this.selMap[node.id]){
36514 node.ui.onSelectedChange(false);
36515 var sn = this.selNodes;
36518 index = sn.indexOf(node);
36520 for(var i = 0, len = sn.length; i < len; i++){
36528 this.selNodes.splice(index, 1);
36530 delete this.selMap[node.id];
36531 this.fireEvent("selectionchange", this, this.selNodes);
36536 * Clear all selections
36538 clearSelections : function(suppressEvent){
36539 var sn = this.selNodes;
36541 for(var i = 0, len = sn.length; i < len; i++){
36542 sn[i].ui.onSelectedChange(false);
36544 this.selNodes = [];
36546 if(suppressEvent !== true){
36547 this.fireEvent("selectionchange", this, this.selNodes);
36553 * Returns true if the node is selected
36554 * @param {TreeNode} node The node to check
36555 * @return {Boolean}
36557 isSelected : function(node){
36558 return this.selMap[node.id] ? true : false;
36562 * Returns an array of the selected nodes
36565 getSelectedNodes : function(){
36566 return this.selNodes;
36569 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36571 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36573 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36576 * Ext JS Library 1.1.1
36577 * Copyright(c) 2006-2007, Ext JS, LLC.
36579 * Originally Released Under LGPL - original licence link has changed is not relivant.
36582 * <script type="text/javascript">
36586 * @class Roo.tree.TreeNode
36587 * @extends Roo.data.Node
36588 * @cfg {String} text The text for this node
36589 * @cfg {Boolean} expanded true to start the node expanded
36590 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36591 * @cfg {Boolean} allowDrop false if this node cannot be drop on
36592 * @cfg {Boolean} disabled true to start the node disabled
36593 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36594 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
36595 * @cfg {String} cls A css class to be added to the node
36596 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36597 * @cfg {String} href URL of the link used for the node (defaults to #)
36598 * @cfg {String} hrefTarget target frame for the link
36599 * @cfg {String} qtip An Ext QuickTip for the node
36600 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36601 * @cfg {Boolean} singleClickExpand True for single click expand on this node
36602 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36603 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36604 * (defaults to undefined with no checkbox rendered)
36606 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36608 Roo.tree.TreeNode = function(attributes){
36609 attributes = attributes || {};
36610 if(typeof attributes == "string"){
36611 attributes = {text: attributes};
36613 this.childrenRendered = false;
36614 this.rendered = false;
36615 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36616 this.expanded = attributes.expanded === true;
36617 this.isTarget = attributes.isTarget !== false;
36618 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36619 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36622 * Read-only. The text for this node. To change it use setText().
36625 this.text = attributes.text;
36627 * True if this node is disabled.
36630 this.disabled = attributes.disabled === true;
36634 * @event textchange
36635 * Fires when the text for this node is changed
36636 * @param {Node} this This node
36637 * @param {String} text The new text
36638 * @param {String} oldText The old text
36640 "textchange" : true,
36642 * @event beforeexpand
36643 * Fires before this node is expanded, return false to cancel.
36644 * @param {Node} this This node
36645 * @param {Boolean} deep
36646 * @param {Boolean} anim
36648 "beforeexpand" : true,
36650 * @event beforecollapse
36651 * Fires before this node is collapsed, return false to cancel.
36652 * @param {Node} this This node
36653 * @param {Boolean} deep
36654 * @param {Boolean} anim
36656 "beforecollapse" : true,
36659 * Fires when this node is expanded
36660 * @param {Node} this This node
36664 * @event disabledchange
36665 * Fires when the disabled status of this node changes
36666 * @param {Node} this This node
36667 * @param {Boolean} disabled
36669 "disabledchange" : true,
36672 * Fires when this node is collapsed
36673 * @param {Node} this This node
36677 * @event beforeclick
36678 * Fires before click processing. Return false to cancel the default action.
36679 * @param {Node} this This node
36680 * @param {Roo.EventObject} e The event object
36682 "beforeclick":true,
36684 * @event checkchange
36685 * Fires when a node with a checkbox's checked property changes
36686 * @param {Node} this This node
36687 * @param {Boolean} checked
36689 "checkchange":true,
36692 * Fires when this node is clicked
36693 * @param {Node} this This node
36694 * @param {Roo.EventObject} e The event object
36699 * Fires when this node is double clicked
36700 * @param {Node} this This node
36701 * @param {Roo.EventObject} e The event object
36705 * @event contextmenu
36706 * Fires when this node is right clicked
36707 * @param {Node} this This node
36708 * @param {Roo.EventObject} e The event object
36710 "contextmenu":true,
36712 * @event beforechildrenrendered
36713 * Fires right before the child nodes for this node are rendered
36714 * @param {Node} this This node
36716 "beforechildrenrendered":true
36719 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36722 * Read-only. The UI for this node
36725 this.ui = new uiClass(this);
36727 // finally support items[]
36728 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36733 Roo.each(this.attributes.items, function(c) {
36734 this.appendChild(Roo.factory(c,Roo.Tree));
36736 delete this.attributes.items;
36741 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36742 preventHScroll: true,
36744 * Returns true if this node is expanded
36745 * @return {Boolean}
36747 isExpanded : function(){
36748 return this.expanded;
36752 * Returns the UI object for this node
36753 * @return {TreeNodeUI}
36755 getUI : function(){
36759 // private override
36760 setFirstChild : function(node){
36761 var of = this.firstChild;
36762 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36763 if(this.childrenRendered && of && node != of){
36764 of.renderIndent(true, true);
36767 this.renderIndent(true, true);
36771 // private override
36772 setLastChild : function(node){
36773 var ol = this.lastChild;
36774 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36775 if(this.childrenRendered && ol && node != ol){
36776 ol.renderIndent(true, true);
36779 this.renderIndent(true, true);
36783 // these methods are overridden to provide lazy rendering support
36784 // private override
36785 appendChild : function()
36787 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36788 if(node && this.childrenRendered){
36791 this.ui.updateExpandIcon();
36795 // private override
36796 removeChild : function(node){
36797 this.ownerTree.getSelectionModel().unselect(node);
36798 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36799 // if it's been rendered remove dom node
36800 if(this.childrenRendered){
36803 if(this.childNodes.length < 1){
36804 this.collapse(false, false);
36806 this.ui.updateExpandIcon();
36808 if(!this.firstChild) {
36809 this.childrenRendered = false;
36814 // private override
36815 insertBefore : function(node, refNode){
36816 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36817 if(newNode && refNode && this.childrenRendered){
36820 this.ui.updateExpandIcon();
36825 * Sets the text for this node
36826 * @param {String} text
36828 setText : function(text){
36829 var oldText = this.text;
36831 this.attributes.text = text;
36832 if(this.rendered){ // event without subscribing
36833 this.ui.onTextChange(this, text, oldText);
36835 this.fireEvent("textchange", this, text, oldText);
36839 * Triggers selection of this node
36841 select : function(){
36842 this.getOwnerTree().getSelectionModel().select(this);
36846 * Triggers deselection of this node
36848 unselect : function(){
36849 this.getOwnerTree().getSelectionModel().unselect(this);
36853 * Returns true if this node is selected
36854 * @return {Boolean}
36856 isSelected : function(){
36857 return this.getOwnerTree().getSelectionModel().isSelected(this);
36861 * Expand this node.
36862 * @param {Boolean} deep (optional) True to expand all children as well
36863 * @param {Boolean} anim (optional) false to cancel the default animation
36864 * @param {Function} callback (optional) A callback to be called when
36865 * expanding this node completes (does not wait for deep expand to complete).
36866 * Called with 1 parameter, this node.
36868 expand : function(deep, anim, callback){
36869 if(!this.expanded){
36870 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36873 if(!this.childrenRendered){
36874 this.renderChildren();
36876 this.expanded = true;
36878 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36879 this.ui.animExpand(function(){
36880 this.fireEvent("expand", this);
36881 if(typeof callback == "function"){
36885 this.expandChildNodes(true);
36887 }.createDelegate(this));
36891 this.fireEvent("expand", this);
36892 if(typeof callback == "function"){
36897 if(typeof callback == "function"){
36902 this.expandChildNodes(true);
36906 isHiddenRoot : function(){
36907 return this.isRoot && !this.getOwnerTree().rootVisible;
36911 * Collapse this node.
36912 * @param {Boolean} deep (optional) True to collapse all children as well
36913 * @param {Boolean} anim (optional) false to cancel the default animation
36915 collapse : function(deep, anim){
36916 if(this.expanded && !this.isHiddenRoot()){
36917 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36920 this.expanded = false;
36921 if((this.getOwnerTree().animate && anim !== false) || anim){
36922 this.ui.animCollapse(function(){
36923 this.fireEvent("collapse", this);
36925 this.collapseChildNodes(true);
36927 }.createDelegate(this));
36930 this.ui.collapse();
36931 this.fireEvent("collapse", this);
36935 var cs = this.childNodes;
36936 for(var i = 0, len = cs.length; i < len; i++) {
36937 cs[i].collapse(true, false);
36943 delayedExpand : function(delay){
36944 if(!this.expandProcId){
36945 this.expandProcId = this.expand.defer(delay, this);
36950 cancelExpand : function(){
36951 if(this.expandProcId){
36952 clearTimeout(this.expandProcId);
36954 this.expandProcId = false;
36958 * Toggles expanded/collapsed state of the node
36960 toggle : function(){
36969 * Ensures all parent nodes are expanded
36971 ensureVisible : function(callback){
36972 var tree = this.getOwnerTree();
36973 tree.expandPath(this.parentNode.getPath(), false, function(){
36974 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36975 Roo.callback(callback);
36976 }.createDelegate(this));
36980 * Expand all child nodes
36981 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36983 expandChildNodes : function(deep){
36984 var cs = this.childNodes;
36985 for(var i = 0, len = cs.length; i < len; i++) {
36986 cs[i].expand(deep);
36991 * Collapse all child nodes
36992 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36994 collapseChildNodes : function(deep){
36995 var cs = this.childNodes;
36996 for(var i = 0, len = cs.length; i < len; i++) {
36997 cs[i].collapse(deep);
37002 * Disables this node
37004 disable : function(){
37005 this.disabled = true;
37007 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37008 this.ui.onDisableChange(this, true);
37010 this.fireEvent("disabledchange", this, true);
37014 * Enables this node
37016 enable : function(){
37017 this.disabled = false;
37018 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37019 this.ui.onDisableChange(this, false);
37021 this.fireEvent("disabledchange", this, false);
37025 renderChildren : function(suppressEvent){
37026 if(suppressEvent !== false){
37027 this.fireEvent("beforechildrenrendered", this);
37029 var cs = this.childNodes;
37030 for(var i = 0, len = cs.length; i < len; i++){
37031 cs[i].render(true);
37033 this.childrenRendered = true;
37037 sort : function(fn, scope){
37038 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37039 if(this.childrenRendered){
37040 var cs = this.childNodes;
37041 for(var i = 0, len = cs.length; i < len; i++){
37042 cs[i].render(true);
37048 render : function(bulkRender){
37049 this.ui.render(bulkRender);
37050 if(!this.rendered){
37051 this.rendered = true;
37053 this.expanded = false;
37054 this.expand(false, false);
37060 renderIndent : function(deep, refresh){
37062 this.ui.childIndent = null;
37064 this.ui.renderIndent();
37065 if(deep === true && this.childrenRendered){
37066 var cs = this.childNodes;
37067 for(var i = 0, len = cs.length; i < len; i++){
37068 cs[i].renderIndent(true, refresh);
37074 * Ext JS Library 1.1.1
37075 * Copyright(c) 2006-2007, Ext JS, LLC.
37077 * Originally Released Under LGPL - original licence link has changed is not relivant.
37080 * <script type="text/javascript">
37084 * @class Roo.tree.AsyncTreeNode
37085 * @extends Roo.tree.TreeNode
37086 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37088 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
37090 Roo.tree.AsyncTreeNode = function(config){
37091 this.loaded = false;
37092 this.loading = false;
37093 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37095 * @event beforeload
37096 * Fires before this node is loaded, return false to cancel
37097 * @param {Node} this This node
37099 this.addEvents({'beforeload':true, 'load': true});
37102 * Fires when this node is loaded
37103 * @param {Node} this This node
37106 * The loader used by this node (defaults to using the tree's defined loader)
37111 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37112 expand : function(deep, anim, callback){
37113 if(this.loading){ // if an async load is already running, waiting til it's done
37115 var f = function(){
37116 if(!this.loading){ // done loading
37117 clearInterval(timer);
37118 this.expand(deep, anim, callback);
37120 }.createDelegate(this);
37121 timer = setInterval(f, 200);
37125 if(this.fireEvent("beforeload", this) === false){
37128 this.loading = true;
37129 this.ui.beforeLoad(this);
37130 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37132 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37136 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37140 * Returns true if this node is currently loading
37141 * @return {Boolean}
37143 isLoading : function(){
37144 return this.loading;
37147 loadComplete : function(deep, anim, callback){
37148 this.loading = false;
37149 this.loaded = true;
37150 this.ui.afterLoad(this);
37151 this.fireEvent("load", this);
37152 this.expand(deep, anim, callback);
37156 * Returns true if this node has been loaded
37157 * @return {Boolean}
37159 isLoaded : function(){
37160 return this.loaded;
37163 hasChildNodes : function(){
37164 if(!this.isLeaf() && !this.loaded){
37167 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37172 * Trigger a reload for this node
37173 * @param {Function} callback
37175 reload : function(callback){
37176 this.collapse(false, false);
37177 while(this.firstChild){
37178 this.removeChild(this.firstChild);
37180 this.childrenRendered = false;
37181 this.loaded = false;
37182 if(this.isHiddenRoot()){
37183 this.expanded = false;
37185 this.expand(false, false, callback);
37189 * Ext JS Library 1.1.1
37190 * Copyright(c) 2006-2007, Ext JS, LLC.
37192 * Originally Released Under LGPL - original licence link has changed is not relivant.
37195 * <script type="text/javascript">
37199 * @class Roo.tree.TreeNodeUI
37201 * @param {Object} node The node to render
37202 * The TreeNode UI implementation is separate from the
37203 * tree implementation. Unless you are customizing the tree UI,
37204 * you should never have to use this directly.
37206 Roo.tree.TreeNodeUI = function(node){
37208 this.rendered = false;
37209 this.animating = false;
37210 this.emptyIcon = Roo.BLANK_IMAGE_URL;
37213 Roo.tree.TreeNodeUI.prototype = {
37214 removeChild : function(node){
37216 this.ctNode.removeChild(node.ui.getEl());
37220 beforeLoad : function(){
37221 this.addClass("x-tree-node-loading");
37224 afterLoad : function(){
37225 this.removeClass("x-tree-node-loading");
37228 onTextChange : function(node, text, oldText){
37230 this.textNode.innerHTML = text;
37234 onDisableChange : function(node, state){
37235 this.disabled = state;
37237 this.addClass("x-tree-node-disabled");
37239 this.removeClass("x-tree-node-disabled");
37243 onSelectedChange : function(state){
37246 this.addClass("x-tree-selected");
37249 this.removeClass("x-tree-selected");
37253 onMove : function(tree, node, oldParent, newParent, index, refNode){
37254 this.childIndent = null;
37256 var targetNode = newParent.ui.getContainer();
37257 if(!targetNode){//target not rendered
37258 this.holder = document.createElement("div");
37259 this.holder.appendChild(this.wrap);
37262 var insertBefore = refNode ? refNode.ui.getEl() : null;
37264 targetNode.insertBefore(this.wrap, insertBefore);
37266 targetNode.appendChild(this.wrap);
37268 this.node.renderIndent(true);
37272 addClass : function(cls){
37274 Roo.fly(this.elNode).addClass(cls);
37278 removeClass : function(cls){
37280 Roo.fly(this.elNode).removeClass(cls);
37284 remove : function(){
37286 this.holder = document.createElement("div");
37287 this.holder.appendChild(this.wrap);
37291 fireEvent : function(){
37292 return this.node.fireEvent.apply(this.node, arguments);
37295 initEvents : function(){
37296 this.node.on("move", this.onMove, this);
37297 var E = Roo.EventManager;
37298 var a = this.anchor;
37300 var el = Roo.fly(a, '_treeui');
37302 if(Roo.isOpera){ // opera render bug ignores the CSS
37303 el.setStyle("text-decoration", "none");
37306 el.on("click", this.onClick, this);
37307 el.on("dblclick", this.onDblClick, this);
37310 Roo.EventManager.on(this.checkbox,
37311 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37314 el.on("contextmenu", this.onContextMenu, this);
37316 var icon = Roo.fly(this.iconNode);
37317 icon.on("click", this.onClick, this);
37318 icon.on("dblclick", this.onDblClick, this);
37319 icon.on("contextmenu", this.onContextMenu, this);
37320 E.on(this.ecNode, "click", this.ecClick, this, true);
37322 if(this.node.disabled){
37323 this.addClass("x-tree-node-disabled");
37325 if(this.node.hidden){
37326 this.addClass("x-tree-node-disabled");
37328 var ot = this.node.getOwnerTree();
37329 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37330 if(dd && (!this.node.isRoot || ot.rootVisible)){
37331 Roo.dd.Registry.register(this.elNode, {
37333 handles: this.getDDHandles(),
37339 getDDHandles : function(){
37340 return [this.iconNode, this.textNode];
37345 this.wrap.style.display = "none";
37351 this.wrap.style.display = "";
37355 onContextMenu : function(e){
37356 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37357 e.preventDefault();
37359 this.fireEvent("contextmenu", this.node, e);
37363 onClick : function(e){
37368 if(this.fireEvent("beforeclick", this.node, e) !== false){
37369 if(!this.disabled && this.node.attributes.href){
37370 this.fireEvent("click", this.node, e);
37373 e.preventDefault();
37378 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37379 this.node.toggle();
37382 this.fireEvent("click", this.node, e);
37388 onDblClick : function(e){
37389 e.preventDefault();
37394 this.toggleCheck();
37396 if(!this.animating && this.node.hasChildNodes()){
37397 this.node.toggle();
37399 this.fireEvent("dblclick", this.node, e);
37402 onCheckChange : function(){
37403 var checked = this.checkbox.checked;
37404 this.node.attributes.checked = checked;
37405 this.fireEvent('checkchange', this.node, checked);
37408 ecClick : function(e){
37409 if(!this.animating && this.node.hasChildNodes()){
37410 this.node.toggle();
37414 startDrop : function(){
37415 this.dropping = true;
37418 // delayed drop so the click event doesn't get fired on a drop
37419 endDrop : function(){
37420 setTimeout(function(){
37421 this.dropping = false;
37422 }.createDelegate(this), 50);
37425 expand : function(){
37426 this.updateExpandIcon();
37427 this.ctNode.style.display = "";
37430 focus : function(){
37431 if(!this.node.preventHScroll){
37432 try{this.anchor.focus();
37434 }else if(!Roo.isIE){
37436 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37437 var l = noscroll.scrollLeft;
37438 this.anchor.focus();
37439 noscroll.scrollLeft = l;
37444 toggleCheck : function(value){
37445 var cb = this.checkbox;
37447 cb.checked = (value === undefined ? !cb.checked : value);
37453 this.anchor.blur();
37457 animExpand : function(callback){
37458 var ct = Roo.get(this.ctNode);
37460 if(!this.node.hasChildNodes()){
37461 this.updateExpandIcon();
37462 this.ctNode.style.display = "";
37463 Roo.callback(callback);
37466 this.animating = true;
37467 this.updateExpandIcon();
37470 callback : function(){
37471 this.animating = false;
37472 Roo.callback(callback);
37475 duration: this.node.ownerTree.duration || .25
37479 highlight : function(){
37480 var tree = this.node.getOwnerTree();
37481 Roo.fly(this.wrap).highlight(
37482 tree.hlColor || "C3DAF9",
37483 {endColor: tree.hlBaseColor}
37487 collapse : function(){
37488 this.updateExpandIcon();
37489 this.ctNode.style.display = "none";
37492 animCollapse : function(callback){
37493 var ct = Roo.get(this.ctNode);
37494 ct.enableDisplayMode('block');
37497 this.animating = true;
37498 this.updateExpandIcon();
37501 callback : function(){
37502 this.animating = false;
37503 Roo.callback(callback);
37506 duration: this.node.ownerTree.duration || .25
37510 getContainer : function(){
37511 return this.ctNode;
37514 getEl : function(){
37518 appendDDGhost : function(ghostNode){
37519 ghostNode.appendChild(this.elNode.cloneNode(true));
37522 getDDRepairXY : function(){
37523 return Roo.lib.Dom.getXY(this.iconNode);
37526 onRender : function(){
37530 render : function(bulkRender){
37531 var n = this.node, a = n.attributes;
37532 var targetNode = n.parentNode ?
37533 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37535 if(!this.rendered){
37536 this.rendered = true;
37538 this.renderElements(n, a, targetNode, bulkRender);
37541 if(this.textNode.setAttributeNS){
37542 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37544 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37547 this.textNode.setAttribute("ext:qtip", a.qtip);
37549 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37552 }else if(a.qtipCfg){
37553 a.qtipCfg.target = Roo.id(this.textNode);
37554 Roo.QuickTips.register(a.qtipCfg);
37557 if(!this.node.expanded){
37558 this.updateExpandIcon();
37561 if(bulkRender === true) {
37562 targetNode.appendChild(this.wrap);
37567 renderElements : function(n, a, targetNode, bulkRender)
37569 // add some indent caching, this helps performance when rendering a large tree
37570 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37571 var t = n.getOwnerTree();
37572 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37573 if (typeof(n.attributes.html) != 'undefined') {
37574 txt = n.attributes.html;
37576 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37577 var cb = typeof a.checked == 'boolean';
37578 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37579 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37580 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37581 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37582 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37583 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37584 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37585 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
37586 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37587 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37590 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37591 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37592 n.nextSibling.ui.getEl(), buf.join(""));
37594 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37597 this.elNode = this.wrap.childNodes[0];
37598 this.ctNode = this.wrap.childNodes[1];
37599 var cs = this.elNode.childNodes;
37600 this.indentNode = cs[0];
37601 this.ecNode = cs[1];
37602 this.iconNode = cs[2];
37605 this.checkbox = cs[3];
37608 this.anchor = cs[index];
37609 this.textNode = cs[index].firstChild;
37612 getAnchor : function(){
37613 return this.anchor;
37616 getTextEl : function(){
37617 return this.textNode;
37620 getIconEl : function(){
37621 return this.iconNode;
37624 isChecked : function(){
37625 return this.checkbox ? this.checkbox.checked : false;
37628 updateExpandIcon : function(){
37630 var n = this.node, c1, c2;
37631 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37632 var hasChild = n.hasChildNodes();
37636 c1 = "x-tree-node-collapsed";
37637 c2 = "x-tree-node-expanded";
37640 c1 = "x-tree-node-expanded";
37641 c2 = "x-tree-node-collapsed";
37644 this.removeClass("x-tree-node-leaf");
37645 this.wasLeaf = false;
37647 if(this.c1 != c1 || this.c2 != c2){
37648 Roo.fly(this.elNode).replaceClass(c1, c2);
37649 this.c1 = c1; this.c2 = c2;
37652 // this changes non-leafs into leafs if they have no children.
37653 // it's not very rational behaviour..
37655 if(!this.wasLeaf && this.node.leaf){
37656 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37659 this.wasLeaf = true;
37662 var ecc = "x-tree-ec-icon "+cls;
37663 if(this.ecc != ecc){
37664 this.ecNode.className = ecc;
37670 getChildIndent : function(){
37671 if(!this.childIndent){
37675 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37677 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37679 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37684 this.childIndent = buf.join("");
37686 return this.childIndent;
37689 renderIndent : function(){
37692 var p = this.node.parentNode;
37694 indent = p.ui.getChildIndent();
37696 if(this.indentMarkup != indent){ // don't rerender if not required
37697 this.indentNode.innerHTML = indent;
37698 this.indentMarkup = indent;
37700 this.updateExpandIcon();
37705 Roo.tree.RootTreeNodeUI = function(){
37706 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37708 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37709 render : function(){
37710 if(!this.rendered){
37711 var targetNode = this.node.ownerTree.innerCt.dom;
37712 this.node.expanded = true;
37713 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37714 this.wrap = this.ctNode = targetNode.firstChild;
37717 collapse : function(){
37719 expand : function(){
37723 * Ext JS Library 1.1.1
37724 * Copyright(c) 2006-2007, Ext JS, LLC.
37726 * Originally Released Under LGPL - original licence link has changed is not relivant.
37729 * <script type="text/javascript">
37732 * @class Roo.tree.TreeLoader
37733 * @extends Roo.util.Observable
37734 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37735 * nodes from a specified URL. The response must be a javascript Array definition
37736 * who's elements are node definition objects. eg:
37741 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37742 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37749 * The old style respose with just an array is still supported, but not recommended.
37752 * A server request is sent, and child nodes are loaded only when a node is expanded.
37753 * The loading node's id is passed to the server under the parameter name "node" to
37754 * enable the server to produce the correct child nodes.
37756 * To pass extra parameters, an event handler may be attached to the "beforeload"
37757 * event, and the parameters specified in the TreeLoader's baseParams property:
37759 myTreeLoader.on("beforeload", function(treeLoader, node) {
37760 this.baseParams.category = node.attributes.category;
37765 * This would pass an HTTP parameter called "category" to the server containing
37766 * the value of the Node's "category" attribute.
37768 * Creates a new Treeloader.
37769 * @param {Object} config A config object containing config properties.
37771 Roo.tree.TreeLoader = function(config){
37772 this.baseParams = {};
37773 this.requestMethod = "POST";
37774 Roo.apply(this, config);
37779 * @event beforeload
37780 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37781 * @param {Object} This TreeLoader object.
37782 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37783 * @param {Object} callback The callback function specified in the {@link #load} call.
37788 * Fires when the node has been successfuly loaded.
37789 * @param {Object} This TreeLoader object.
37790 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37791 * @param {Object} response The response object containing the data from the server.
37795 * @event loadexception
37796 * Fires if the network request failed.
37797 * @param {Object} This TreeLoader object.
37798 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37799 * @param {Object} response The response object containing the data from the server.
37801 loadexception : true,
37804 * Fires before a node is created, enabling you to return custom Node types
37805 * @param {Object} This TreeLoader object.
37806 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37811 Roo.tree.TreeLoader.superclass.constructor.call(this);
37814 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37816 * @cfg {String} dataUrl The URL from which to request a Json string which
37817 * specifies an array of node definition object representing the child nodes
37821 * @cfg {String} requestMethod either GET or POST
37822 * defaults to POST (due to BC)
37826 * @cfg {Object} baseParams (optional) An object containing properties which
37827 * specify HTTP parameters to be passed to each request for child nodes.
37830 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37831 * created by this loader. If the attributes sent by the server have an attribute in this object,
37832 * they take priority.
37835 * @cfg {Object} uiProviders (optional) An object containing properties which
37837 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37838 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37839 * <i>uiProvider</i> attribute of a returned child node is a string rather
37840 * than a reference to a TreeNodeUI implementation, this that string value
37841 * is used as a property name in the uiProviders object. You can define the provider named
37842 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37847 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37848 * child nodes before loading.
37850 clearOnLoad : true,
37853 * @cfg {String} root (optional) Default to false. Use this to read data from an object
37854 * property on loading, rather than expecting an array. (eg. more compatible to a standard
37855 * Grid query { data : [ .....] }
37860 * @cfg {String} queryParam (optional)
37861 * Name of the query as it will be passed on the querystring (defaults to 'node')
37862 * eg. the request will be ?node=[id]
37869 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37870 * This is called automatically when a node is expanded, but may be used to reload
37871 * a node (or append new children if the {@link #clearOnLoad} option is false.)
37872 * @param {Roo.tree.TreeNode} node
37873 * @param {Function} callback
37875 load : function(node, callback){
37876 if(this.clearOnLoad){
37877 while(node.firstChild){
37878 node.removeChild(node.firstChild);
37881 if(node.attributes.children){ // preloaded json children
37882 var cs = node.attributes.children;
37883 for(var i = 0, len = cs.length; i < len; i++){
37884 node.appendChild(this.createNode(cs[i]));
37886 if(typeof callback == "function"){
37889 }else if(this.dataUrl){
37890 this.requestData(node, callback);
37894 getParams: function(node){
37895 var buf = [], bp = this.baseParams;
37896 for(var key in bp){
37897 if(typeof bp[key] != "function"){
37898 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37901 var n = this.queryParam === false ? 'node' : this.queryParam;
37902 buf.push(n + "=", encodeURIComponent(node.id));
37903 return buf.join("");
37906 requestData : function(node, callback){
37907 if(this.fireEvent("beforeload", this, node, callback) !== false){
37908 this.transId = Roo.Ajax.request({
37909 method:this.requestMethod,
37910 url: this.dataUrl||this.url,
37911 success: this.handleResponse,
37912 failure: this.handleFailure,
37914 argument: {callback: callback, node: node},
37915 params: this.getParams(node)
37918 // if the load is cancelled, make sure we notify
37919 // the node that we are done
37920 if(typeof callback == "function"){
37926 isLoading : function(){
37927 return this.transId ? true : false;
37930 abort : function(){
37931 if(this.isLoading()){
37932 Roo.Ajax.abort(this.transId);
37937 createNode : function(attr)
37939 // apply baseAttrs, nice idea Corey!
37940 if(this.baseAttrs){
37941 Roo.applyIf(attr, this.baseAttrs);
37943 if(this.applyLoader !== false){
37944 attr.loader = this;
37946 // uiProvider = depreciated..
37948 if(typeof(attr.uiProvider) == 'string'){
37949 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
37950 /** eval:var:attr */ eval(attr.uiProvider);
37952 if(typeof(this.uiProviders['default']) != 'undefined') {
37953 attr.uiProvider = this.uiProviders['default'];
37956 this.fireEvent('create', this, attr);
37958 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37960 new Roo.tree.TreeNode(attr) :
37961 new Roo.tree.AsyncTreeNode(attr));
37964 processResponse : function(response, node, callback)
37966 var json = response.responseText;
37969 var o = Roo.decode(json);
37971 if (this.root === false && typeof(o.success) != undefined) {
37972 this.root = 'data'; // the default behaviour for list like data..
37975 if (this.root !== false && !o.success) {
37976 // it's a failure condition.
37977 var a = response.argument;
37978 this.fireEvent("loadexception", this, a.node, response);
37979 Roo.log("Load failed - should have a handler really");
37985 if (this.root !== false) {
37989 for(var i = 0, len = o.length; i < len; i++){
37990 var n = this.createNode(o[i]);
37992 node.appendChild(n);
37995 if(typeof callback == "function"){
37996 callback(this, node);
37999 this.handleFailure(response);
38003 handleResponse : function(response){
38004 this.transId = false;
38005 var a = response.argument;
38006 this.processResponse(response, a.node, a.callback);
38007 this.fireEvent("load", this, a.node, response);
38010 handleFailure : function(response)
38012 // should handle failure better..
38013 this.transId = false;
38014 var a = response.argument;
38015 this.fireEvent("loadexception", this, a.node, response);
38016 if(typeof a.callback == "function"){
38017 a.callback(this, a.node);
38022 * Ext JS Library 1.1.1
38023 * Copyright(c) 2006-2007, Ext JS, LLC.
38025 * Originally Released Under LGPL - original licence link has changed is not relivant.
38028 * <script type="text/javascript">
38032 * @class Roo.tree.TreeFilter
38033 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38034 * @param {TreePanel} tree
38035 * @param {Object} config (optional)
38037 Roo.tree.TreeFilter = function(tree, config){
38039 this.filtered = {};
38040 Roo.apply(this, config);
38043 Roo.tree.TreeFilter.prototype = {
38050 * Filter the data by a specific attribute.
38051 * @param {String/RegExp} value Either string that the attribute value
38052 * should start with or a RegExp to test against the attribute
38053 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38054 * @param {TreeNode} startNode (optional) The node to start the filter at.
38056 filter : function(value, attr, startNode){
38057 attr = attr || "text";
38059 if(typeof value == "string"){
38060 var vlen = value.length;
38061 // auto clear empty filter
38062 if(vlen == 0 && this.clearBlank){
38066 value = value.toLowerCase();
38068 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38070 }else if(value.exec){ // regex?
38072 return value.test(n.attributes[attr]);
38075 throw 'Illegal filter type, must be string or regex';
38077 this.filterBy(f, null, startNode);
38081 * Filter by a function. The passed function will be called with each
38082 * node in the tree (or from the startNode). If the function returns true, the node is kept
38083 * otherwise it is filtered. If a node is filtered, its children are also filtered.
38084 * @param {Function} fn The filter function
38085 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38087 filterBy : function(fn, scope, startNode){
38088 startNode = startNode || this.tree.root;
38089 if(this.autoClear){
38092 var af = this.filtered, rv = this.reverse;
38093 var f = function(n){
38094 if(n == startNode){
38100 var m = fn.call(scope || n, n);
38108 startNode.cascade(f);
38111 if(typeof id != "function"){
38113 if(n && n.parentNode){
38114 n.parentNode.removeChild(n);
38122 * Clears the current filter. Note: with the "remove" option
38123 * set a filter cannot be cleared.
38125 clear : function(){
38127 var af = this.filtered;
38129 if(typeof id != "function"){
38136 this.filtered = {};
38141 * Ext JS Library 1.1.1
38142 * Copyright(c) 2006-2007, Ext JS, LLC.
38144 * Originally Released Under LGPL - original licence link has changed is not relivant.
38147 * <script type="text/javascript">
38152 * @class Roo.tree.TreeSorter
38153 * Provides sorting of nodes in a TreePanel
38155 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38156 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38157 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38158 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38159 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38160 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38162 * @param {TreePanel} tree
38163 * @param {Object} config
38165 Roo.tree.TreeSorter = function(tree, config){
38166 Roo.apply(this, config);
38167 tree.on("beforechildrenrendered", this.doSort, this);
38168 tree.on("append", this.updateSort, this);
38169 tree.on("insert", this.updateSort, this);
38171 var dsc = this.dir && this.dir.toLowerCase() == "desc";
38172 var p = this.property || "text";
38173 var sortType = this.sortType;
38174 var fs = this.folderSort;
38175 var cs = this.caseSensitive === true;
38176 var leafAttr = this.leafAttr || 'leaf';
38178 this.sortFn = function(n1, n2){
38180 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38183 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38187 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38188 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38190 return dsc ? +1 : -1;
38192 return dsc ? -1 : +1;
38199 Roo.tree.TreeSorter.prototype = {
38200 doSort : function(node){
38201 node.sort(this.sortFn);
38204 compareNodes : function(n1, n2){
38205 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38208 updateSort : function(tree, node){
38209 if(node.childrenRendered){
38210 this.doSort.defer(1, this, [node]);
38215 * Ext JS Library 1.1.1
38216 * Copyright(c) 2006-2007, Ext JS, LLC.
38218 * Originally Released Under LGPL - original licence link has changed is not relivant.
38221 * <script type="text/javascript">
38224 if(Roo.dd.DropZone){
38226 Roo.tree.TreeDropZone = function(tree, config){
38227 this.allowParentInsert = false;
38228 this.allowContainerDrop = false;
38229 this.appendOnly = false;
38230 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38232 this.lastInsertClass = "x-tree-no-status";
38233 this.dragOverData = {};
38236 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38237 ddGroup : "TreeDD",
38240 expandDelay : 1000,
38242 expandNode : function(node){
38243 if(node.hasChildNodes() && !node.isExpanded()){
38244 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38248 queueExpand : function(node){
38249 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38252 cancelExpand : function(){
38253 if(this.expandProcId){
38254 clearTimeout(this.expandProcId);
38255 this.expandProcId = false;
38259 isValidDropPoint : function(n, pt, dd, e, data){
38260 if(!n || !data){ return false; }
38261 var targetNode = n.node;
38262 var dropNode = data.node;
38263 // default drop rules
38264 if(!(targetNode && targetNode.isTarget && pt)){
38267 if(pt == "append" && targetNode.allowChildren === false){
38270 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38273 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38276 // reuse the object
38277 var overEvent = this.dragOverData;
38278 overEvent.tree = this.tree;
38279 overEvent.target = targetNode;
38280 overEvent.data = data;
38281 overEvent.point = pt;
38282 overEvent.source = dd;
38283 overEvent.rawEvent = e;
38284 overEvent.dropNode = dropNode;
38285 overEvent.cancel = false;
38286 var result = this.tree.fireEvent("nodedragover", overEvent);
38287 return overEvent.cancel === false && result !== false;
38290 getDropPoint : function(e, n, dd)
38294 return tn.allowChildren !== false ? "append" : false; // always append for root
38296 var dragEl = n.ddel;
38297 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38298 var y = Roo.lib.Event.getPageY(e);
38299 //var noAppend = tn.allowChildren === false || tn.isLeaf();
38301 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38302 var noAppend = tn.allowChildren === false;
38303 if(this.appendOnly || tn.parentNode.allowChildren === false){
38304 return noAppend ? false : "append";
38306 var noBelow = false;
38307 if(!this.allowParentInsert){
38308 noBelow = tn.hasChildNodes() && tn.isExpanded();
38310 var q = (b - t) / (noAppend ? 2 : 3);
38311 if(y >= t && y < (t + q)){
38313 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38320 onNodeEnter : function(n, dd, e, data)
38322 this.cancelExpand();
38325 onNodeOver : function(n, dd, e, data)
38328 var pt = this.getDropPoint(e, n, dd);
38331 // auto node expand check
38332 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38333 this.queueExpand(node);
38334 }else if(pt != "append"){
38335 this.cancelExpand();
38338 // set the insert point style on the target node
38339 var returnCls = this.dropNotAllowed;
38340 if(this.isValidDropPoint(n, pt, dd, e, data)){
38345 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38346 cls = "x-tree-drag-insert-above";
38347 }else if(pt == "below"){
38348 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38349 cls = "x-tree-drag-insert-below";
38351 returnCls = "x-tree-drop-ok-append";
38352 cls = "x-tree-drag-append";
38354 if(this.lastInsertClass != cls){
38355 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38356 this.lastInsertClass = cls;
38363 onNodeOut : function(n, dd, e, data){
38365 this.cancelExpand();
38366 this.removeDropIndicators(n);
38369 onNodeDrop : function(n, dd, e, data){
38370 var point = this.getDropPoint(e, n, dd);
38371 var targetNode = n.node;
38372 targetNode.ui.startDrop();
38373 if(!this.isValidDropPoint(n, point, dd, e, data)){
38374 targetNode.ui.endDrop();
38377 // first try to find the drop node
38378 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38381 target: targetNode,
38386 dropNode: dropNode,
38389 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38390 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38391 targetNode.ui.endDrop();
38394 // allow target changing
38395 targetNode = dropEvent.target;
38396 if(point == "append" && !targetNode.isExpanded()){
38397 targetNode.expand(false, null, function(){
38398 this.completeDrop(dropEvent);
38399 }.createDelegate(this));
38401 this.completeDrop(dropEvent);
38406 completeDrop : function(de){
38407 var ns = de.dropNode, p = de.point, t = de.target;
38408 if(!(ns instanceof Array)){
38412 for(var i = 0, len = ns.length; i < len; i++){
38415 t.parentNode.insertBefore(n, t);
38416 }else if(p == "below"){
38417 t.parentNode.insertBefore(n, t.nextSibling);
38423 if(this.tree.hlDrop){
38427 this.tree.fireEvent("nodedrop", de);
38430 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38431 if(this.tree.hlDrop){
38432 dropNode.ui.focus();
38433 dropNode.ui.highlight();
38435 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38438 getTree : function(){
38442 removeDropIndicators : function(n){
38445 Roo.fly(el).removeClass([
38446 "x-tree-drag-insert-above",
38447 "x-tree-drag-insert-below",
38448 "x-tree-drag-append"]);
38449 this.lastInsertClass = "_noclass";
38453 beforeDragDrop : function(target, e, id){
38454 this.cancelExpand();
38458 afterRepair : function(data){
38459 if(data && Roo.enableFx){
38460 data.node.ui.highlight();
38470 * Ext JS Library 1.1.1
38471 * Copyright(c) 2006-2007, Ext JS, LLC.
38473 * Originally Released Under LGPL - original licence link has changed is not relivant.
38476 * <script type="text/javascript">
38480 if(Roo.dd.DragZone){
38481 Roo.tree.TreeDragZone = function(tree, config){
38482 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38486 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38487 ddGroup : "TreeDD",
38489 onBeforeDrag : function(data, e){
38491 return n && n.draggable && !n.disabled;
38495 onInitDrag : function(e){
38496 var data = this.dragData;
38497 this.tree.getSelectionModel().select(data.node);
38498 this.proxy.update("");
38499 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38500 this.tree.fireEvent("startdrag", this.tree, data.node, e);
38503 getRepairXY : function(e, data){
38504 return data.node.ui.getDDRepairXY();
38507 onEndDrag : function(data, e){
38508 this.tree.fireEvent("enddrag", this.tree, data.node, e);
38513 onValidDrop : function(dd, e, id){
38514 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38518 beforeInvalidDrop : function(e, id){
38519 // this scrolls the original position back into view
38520 var sm = this.tree.getSelectionModel();
38521 sm.clearSelections();
38522 sm.select(this.dragData.node);
38527 * Ext JS Library 1.1.1
38528 * Copyright(c) 2006-2007, Ext JS, LLC.
38530 * Originally Released Under LGPL - original licence link has changed is not relivant.
38533 * <script type="text/javascript">
38536 * @class Roo.tree.TreeEditor
38537 * @extends Roo.Editor
38538 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
38539 * as the editor field.
38541 * @param {Object} config (used to be the tree panel.)
38542 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38544 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38545 * @cfg {Roo.form.TextField} field [required] The field configuration
38549 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38552 if (oldconfig) { // old style..
38553 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38556 tree = config.tree;
38557 config.field = config.field || {};
38558 config.field.xtype = 'TextField';
38559 field = Roo.factory(config.field, Roo.form);
38561 config = config || {};
38566 * @event beforenodeedit
38567 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
38568 * false from the handler of this event.
38569 * @param {Editor} this
38570 * @param {Roo.tree.Node} node
38572 "beforenodeedit" : true
38576 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38580 tree.on('beforeclick', this.beforeNodeClick, this);
38581 tree.getTreeEl().on('mousedown', this.hide, this);
38582 this.on('complete', this.updateNode, this);
38583 this.on('beforestartedit', this.fitToTree, this);
38584 this.on('startedit', this.bindScroll, this, {delay:10});
38585 this.on('specialkey', this.onSpecialKey, this);
38588 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38590 * @cfg {String} alignment
38591 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38597 * @cfg {Boolean} hideEl
38598 * True to hide the bound element while the editor is displayed (defaults to false)
38602 * @cfg {String} cls
38603 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38605 cls: "x-small-editor x-tree-editor",
38607 * @cfg {Boolean} shim
38608 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38614 * @cfg {Number} maxWidth
38615 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
38616 * the containing tree element's size, it will be automatically limited for you to the container width, taking
38617 * scroll and client offsets into account prior to each edit.
38624 fitToTree : function(ed, el){
38625 var td = this.tree.getTreeEl().dom, nd = el.dom;
38626 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
38627 td.scrollLeft = nd.offsetLeft;
38631 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38632 this.setSize(w, '');
38634 return this.fireEvent('beforenodeedit', this, this.editNode);
38639 triggerEdit : function(node){
38640 this.completeEdit();
38641 this.editNode = node;
38642 this.startEdit(node.ui.textNode, node.text);
38646 bindScroll : function(){
38647 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38651 beforeNodeClick : function(node, e){
38652 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38653 this.lastClick = new Date();
38654 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38656 this.triggerEdit(node);
38663 updateNode : function(ed, value){
38664 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38665 this.editNode.setText(value);
38669 onHide : function(){
38670 Roo.tree.TreeEditor.superclass.onHide.call(this);
38672 this.editNode.ui.focus();
38677 onSpecialKey : function(field, e){
38678 var k = e.getKey();
38682 }else if(k == e.ENTER && !e.hasModifier()){
38684 this.completeEdit();
38687 });//<Script type="text/javascript">
38690 * Ext JS Library 1.1.1
38691 * Copyright(c) 2006-2007, Ext JS, LLC.
38693 * Originally Released Under LGPL - original licence link has changed is not relivant.
38696 * <script type="text/javascript">
38700 * Not documented??? - probably should be...
38703 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38704 //focus: Roo.emptyFn, // prevent odd scrolling behavior
38706 renderElements : function(n, a, targetNode, bulkRender){
38707 //consel.log("renderElements?");
38708 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38710 var t = n.getOwnerTree();
38711 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38713 var cols = t.columns;
38714 var bw = t.borderWidth;
38716 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38717 var cb = typeof a.checked == "boolean";
38718 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38719 var colcls = 'x-t-' + tid + '-c0';
38721 '<li class="x-tree-node">',
38724 '<div class="x-tree-node-el ', a.cls,'">',
38726 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38729 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38730 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
38731 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38732 (a.icon ? ' x-tree-node-inline-icon' : ''),
38733 (a.iconCls ? ' '+a.iconCls : ''),
38734 '" unselectable="on" />',
38735 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
38736 (a.checked ? 'checked="checked" />' : ' />')) : ''),
38738 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38739 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38740 '<span unselectable="on" qtip="' + tx + '">',
38744 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38745 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38747 for(var i = 1, len = cols.length; i < len; i++){
38749 colcls = 'x-t-' + tid + '-c' +i;
38750 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38751 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38752 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38758 '<div class="x-clear"></div></div>',
38759 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38762 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38763 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38764 n.nextSibling.ui.getEl(), buf.join(""));
38766 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38768 var el = this.wrap.firstChild;
38770 this.elNode = el.firstChild;
38771 this.ranchor = el.childNodes[1];
38772 this.ctNode = this.wrap.childNodes[1];
38773 var cs = el.firstChild.childNodes;
38774 this.indentNode = cs[0];
38775 this.ecNode = cs[1];
38776 this.iconNode = cs[2];
38779 this.checkbox = cs[3];
38782 this.anchor = cs[index];
38784 this.textNode = cs[index].firstChild;
38786 //el.on("click", this.onClick, this);
38787 //el.on("dblclick", this.onDblClick, this);
38790 // console.log(this);
38792 initEvents : function(){
38793 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38796 var a = this.ranchor;
38798 var el = Roo.get(a);
38800 if(Roo.isOpera){ // opera render bug ignores the CSS
38801 el.setStyle("text-decoration", "none");
38804 el.on("click", this.onClick, this);
38805 el.on("dblclick", this.onDblClick, this);
38806 el.on("contextmenu", this.onContextMenu, this);
38810 /*onSelectedChange : function(state){
38813 this.addClass("x-tree-selected");
38816 this.removeClass("x-tree-selected");
38819 addClass : function(cls){
38821 Roo.fly(this.elRow).addClass(cls);
38827 removeClass : function(cls){
38829 Roo.fly(this.elRow).removeClass(cls);
38835 });//<Script type="text/javascript">
38839 * Ext JS Library 1.1.1
38840 * Copyright(c) 2006-2007, Ext JS, LLC.
38842 * Originally Released Under LGPL - original licence link has changed is not relivant.
38845 * <script type="text/javascript">
38850 * @class Roo.tree.ColumnTree
38851 * @extends Roo.tree.TreePanel
38852 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
38853 * @cfg {int} borderWidth compined right/left border allowance
38855 * @param {String/HTMLElement/Element} el The container element
38856 * @param {Object} config
38858 Roo.tree.ColumnTree = function(el, config)
38860 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38864 * Fire this event on a container when it resizes
38865 * @param {int} w Width
38866 * @param {int} h Height
38870 this.on('resize', this.onResize, this);
38873 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38877 borderWidth: Roo.isBorderBox ? 0 : 2,
38880 render : function(){
38881 // add the header.....
38883 Roo.tree.ColumnTree.superclass.render.apply(this);
38885 this.el.addClass('x-column-tree');
38887 this.headers = this.el.createChild(
38888 {cls:'x-tree-headers'},this.innerCt.dom);
38890 var cols = this.columns, c;
38891 var totalWidth = 0;
38893 var len = cols.length;
38894 for(var i = 0; i < len; i++){
38896 totalWidth += c.width;
38897 this.headEls.push(this.headers.createChild({
38898 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38900 cls:'x-tree-hd-text',
38903 style:'width:'+(c.width-this.borderWidth)+'px;'
38906 this.headers.createChild({cls:'x-clear'});
38907 // prevent floats from wrapping when clipped
38908 this.headers.setWidth(totalWidth);
38909 //this.innerCt.setWidth(totalWidth);
38910 this.innerCt.setStyle({ overflow: 'auto' });
38911 this.onResize(this.width, this.height);
38915 onResize : function(w,h)
38920 this.innerCt.setWidth(this.width);
38921 this.innerCt.setHeight(this.height-20);
38924 var cols = this.columns, c;
38925 var totalWidth = 0;
38927 var len = cols.length;
38928 for(var i = 0; i < len; i++){
38930 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38931 // it's the expander..
38932 expEl = this.headEls[i];
38935 totalWidth += c.width;
38939 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
38941 this.headers.setWidth(w-20);
38950 * Ext JS Library 1.1.1
38951 * Copyright(c) 2006-2007, Ext JS, LLC.
38953 * Originally Released Under LGPL - original licence link has changed is not relivant.
38956 * <script type="text/javascript">
38960 * @class Roo.menu.Menu
38961 * @extends Roo.util.Observable
38962 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38963 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
38964 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38966 * Creates a new Menu
38967 * @param {Object} config Configuration options
38969 Roo.menu.Menu = function(config){
38971 Roo.menu.Menu.superclass.constructor.call(this, config);
38973 this.id = this.id || Roo.id();
38976 * @event beforeshow
38977 * Fires before this menu is displayed
38978 * @param {Roo.menu.Menu} this
38982 * @event beforehide
38983 * Fires before this menu is hidden
38984 * @param {Roo.menu.Menu} this
38989 * Fires after this menu is displayed
38990 * @param {Roo.menu.Menu} this
38995 * Fires after this menu is hidden
38996 * @param {Roo.menu.Menu} this
39001 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
39002 * @param {Roo.menu.Menu} this
39003 * @param {Roo.menu.Item} menuItem The menu item that was clicked
39004 * @param {Roo.EventObject} e
39009 * Fires when the mouse is hovering over this menu
39010 * @param {Roo.menu.Menu} this
39011 * @param {Roo.EventObject} e
39012 * @param {Roo.menu.Item} menuItem The menu item that was clicked
39017 * Fires when the mouse exits this menu
39018 * @param {Roo.menu.Menu} this
39019 * @param {Roo.EventObject} e
39020 * @param {Roo.menu.Item} menuItem The menu item that was clicked
39025 * Fires when a menu item contained in this menu is clicked
39026 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39027 * @param {Roo.EventObject} e
39031 if (this.registerMenu) {
39032 Roo.menu.MenuMgr.register(this);
39035 var mis = this.items;
39036 this.items = new Roo.util.MixedCollection();
39038 this.add.apply(this, mis);
39042 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39044 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39048 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39049 * for bottom-right shadow (defaults to "sides")
39053 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39054 * this menu (defaults to "tl-tr?")
39056 subMenuAlign : "tl-tr?",
39058 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39059 * relative to its element of origin (defaults to "tl-bl?")
39061 defaultAlign : "tl-bl?",
39063 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39065 allowOtherMenus : false,
39067 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39069 registerMenu : true,
39074 render : function(){
39078 var el = this.el = new Roo.Layer({
39080 shadow:this.shadow,
39082 parentEl: this.parentEl || document.body,
39086 this.keyNav = new Roo.menu.MenuNav(this);
39089 el.addClass("x-menu-plain");
39092 el.addClass(this.cls);
39094 // generic focus element
39095 this.focusEl = el.createChild({
39096 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39098 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39099 //disabling touch- as it's causing issues ..
39100 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
39101 ul.on('click' , this.onClick, this);
39104 ul.on("mouseover", this.onMouseOver, this);
39105 ul.on("mouseout", this.onMouseOut, this);
39106 this.items.each(function(item){
39111 var li = document.createElement("li");
39112 li.className = "x-menu-list-item";
39113 ul.dom.appendChild(li);
39114 item.render(li, this);
39121 autoWidth : function(){
39122 var el = this.el, ul = this.ul;
39126 var w = this.width;
39129 }else if(Roo.isIE){
39130 el.setWidth(this.minWidth);
39131 var t = el.dom.offsetWidth; // force recalc
39132 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39137 delayAutoWidth : function(){
39140 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39142 this.awTask.delay(20);
39147 findTargetItem : function(e){
39148 var t = e.getTarget(".x-menu-list-item", this.ul, true);
39149 if(t && t.menuItemId){
39150 return this.items.get(t.menuItemId);
39155 onClick : function(e){
39156 Roo.log("menu.onClick");
39157 var t = this.findTargetItem(e);
39162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
39163 if(t == this.activeItem && t.shouldDeactivate(e)){
39164 this.activeItem.deactivate();
39165 delete this.activeItem;
39169 this.setActiveItem(t, true);
39177 this.fireEvent("click", this, t, e);
39181 setActiveItem : function(item, autoExpand){
39182 if(item != this.activeItem){
39183 if(this.activeItem){
39184 this.activeItem.deactivate();
39186 this.activeItem = item;
39187 item.activate(autoExpand);
39188 }else if(autoExpand){
39194 tryActivate : function(start, step){
39195 var items = this.items;
39196 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39197 var item = items.get(i);
39198 if(!item.disabled && item.canActivate){
39199 this.setActiveItem(item, false);
39207 onMouseOver : function(e){
39209 if(t = this.findTargetItem(e)){
39210 if(t.canActivate && !t.disabled){
39211 this.setActiveItem(t, true);
39214 this.fireEvent("mouseover", this, e, t);
39218 onMouseOut : function(e){
39220 if(t = this.findTargetItem(e)){
39221 if(t == this.activeItem && t.shouldDeactivate(e)){
39222 this.activeItem.deactivate();
39223 delete this.activeItem;
39226 this.fireEvent("mouseout", this, e, t);
39230 * Read-only. Returns true if the menu is currently displayed, else false.
39233 isVisible : function(){
39234 return this.el && !this.hidden;
39238 * Displays this menu relative to another element
39239 * @param {String/HTMLElement/Roo.Element} element The element to align to
39240 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39241 * the element (defaults to this.defaultAlign)
39242 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39244 show : function(el, pos, parentMenu){
39245 this.parentMenu = parentMenu;
39249 this.fireEvent("beforeshow", this);
39250 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39254 * Displays this menu at a specific xy position
39255 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39256 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39258 showAt : function(xy, parentMenu, /* private: */_e){
39259 this.parentMenu = parentMenu;
39264 this.fireEvent("beforeshow", this);
39265 xy = this.el.adjustForConstraints(xy);
39269 this.hidden = false;
39271 this.fireEvent("show", this);
39274 focus : function(){
39276 this.doFocus.defer(50, this);
39280 doFocus : function(){
39282 this.focusEl.focus();
39287 * Hides this menu and optionally all parent menus
39288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39290 hide : function(deep){
39291 if(this.el && this.isVisible()){
39292 this.fireEvent("beforehide", this);
39293 if(this.activeItem){
39294 this.activeItem.deactivate();
39295 this.activeItem = null;
39298 this.hidden = true;
39299 this.fireEvent("hide", this);
39301 if(deep === true && this.parentMenu){
39302 this.parentMenu.hide(true);
39307 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39308 * Any of the following are valid:
39310 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39311 * <li>An HTMLElement object which will be converted to a menu item</li>
39312 * <li>A menu item config object that will be created as a new menu item</li>
39313 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39314 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39319 var menu = new Roo.menu.Menu();
39321 // Create a menu item to add by reference
39322 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39324 // Add a bunch of items at once using different methods.
39325 // Only the last item added will be returned.
39326 var item = menu.add(
39327 menuItem, // add existing item by ref
39328 'Dynamic Item', // new TextItem
39329 '-', // new separator
39330 { text: 'Config Item' } // new item by config
39333 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39334 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39337 var a = arguments, l = a.length, item;
39338 for(var i = 0; i < l; i++){
39340 if ((typeof(el) == "object") && el.xtype && el.xns) {
39341 el = Roo.factory(el, Roo.menu);
39344 if(el.render){ // some kind of Item
39345 item = this.addItem(el);
39346 }else if(typeof el == "string"){ // string
39347 if(el == "separator" || el == "-"){
39348 item = this.addSeparator();
39350 item = this.addText(el);
39352 }else if(el.tagName || el.el){ // element
39353 item = this.addElement(el);
39354 }else if(typeof el == "object"){ // must be menu item config?
39355 item = this.addMenuItem(el);
39362 * Returns this menu's underlying {@link Roo.Element} object
39363 * @return {Roo.Element} The element
39365 getEl : function(){
39373 * Adds a separator bar to the menu
39374 * @return {Roo.menu.Item} The menu item that was added
39376 addSeparator : function(){
39377 return this.addItem(new Roo.menu.Separator());
39381 * Adds an {@link Roo.Element} object to the menu
39382 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39383 * @return {Roo.menu.Item} The menu item that was added
39385 addElement : function(el){
39386 return this.addItem(new Roo.menu.BaseItem(el));
39390 * Adds an existing object based on {@link Roo.menu.Item} to the menu
39391 * @param {Roo.menu.Item} item The menu item to add
39392 * @return {Roo.menu.Item} The menu item that was added
39394 addItem : function(item){
39395 this.items.add(item);
39397 var li = document.createElement("li");
39398 li.className = "x-menu-list-item";
39399 this.ul.dom.appendChild(li);
39400 item.render(li, this);
39401 this.delayAutoWidth();
39407 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39408 * @param {Object} config A MenuItem config object
39409 * @return {Roo.menu.Item} The menu item that was added
39411 addMenuItem : function(config){
39412 if(!(config instanceof Roo.menu.Item)){
39413 if(typeof config.checked == "boolean"){ // must be check menu item config?
39414 config = new Roo.menu.CheckItem(config);
39416 config = new Roo.menu.Item(config);
39419 return this.addItem(config);
39423 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39424 * @param {String} text The text to display in the menu item
39425 * @return {Roo.menu.Item} The menu item that was added
39427 addText : function(text){
39428 return this.addItem(new Roo.menu.TextItem({ text : text }));
39432 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39433 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39434 * @param {Roo.menu.Item} item The menu item to add
39435 * @return {Roo.menu.Item} The menu item that was added
39437 insert : function(index, item){
39438 this.items.insert(index, item);
39440 var li = document.createElement("li");
39441 li.className = "x-menu-list-item";
39442 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39443 item.render(li, this);
39444 this.delayAutoWidth();
39450 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39451 * @param {Roo.menu.Item} item The menu item to remove
39453 remove : function(item){
39454 this.items.removeKey(item.id);
39459 * Removes and destroys all items in the menu
39461 removeAll : function(){
39463 while(f = this.items.first()){
39469 // MenuNav is a private utility class used internally by the Menu
39470 Roo.menu.MenuNav = function(menu){
39471 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39472 this.scope = this.menu = menu;
39475 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39476 doRelay : function(e, h){
39477 var k = e.getKey();
39478 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39479 this.menu.tryActivate(0, 1);
39482 return h.call(this.scope || this, e, this.menu);
39485 up : function(e, m){
39486 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39487 m.tryActivate(m.items.length-1, -1);
39491 down : function(e, m){
39492 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39493 m.tryActivate(0, 1);
39497 right : function(e, m){
39499 m.activeItem.expandMenu(true);
39503 left : function(e, m){
39505 if(m.parentMenu && m.parentMenu.activeItem){
39506 m.parentMenu.activeItem.activate();
39510 enter : function(e, m){
39512 e.stopPropagation();
39513 m.activeItem.onClick(e);
39514 m.fireEvent("click", this, m.activeItem);
39520 * Ext JS Library 1.1.1
39521 * Copyright(c) 2006-2007, Ext JS, LLC.
39523 * Originally Released Under LGPL - original licence link has changed is not relivant.
39526 * <script type="text/javascript">
39530 * @class Roo.menu.MenuMgr
39531 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39534 Roo.menu.MenuMgr = function(){
39535 var menus, active, groups = {}, attached = false, lastShow = new Date();
39537 // private - called when first menu is created
39540 active = new Roo.util.MixedCollection();
39541 Roo.get(document).addKeyListener(27, function(){
39542 if(active.length > 0){
39549 function hideAll(){
39550 if(active && active.length > 0){
39551 var c = active.clone();
39552 c.each(function(m){
39559 function onHide(m){
39561 if(active.length < 1){
39562 Roo.get(document).un("mousedown", onMouseDown);
39568 function onShow(m){
39569 var last = active.last();
39570 lastShow = new Date();
39573 Roo.get(document).on("mousedown", onMouseDown);
39577 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39578 m.parentMenu.activeChild = m;
39579 }else if(last && last.isVisible()){
39580 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39585 function onBeforeHide(m){
39587 m.activeChild.hide();
39589 if(m.autoHideTimer){
39590 clearTimeout(m.autoHideTimer);
39591 delete m.autoHideTimer;
39596 function onBeforeShow(m){
39597 var pm = m.parentMenu;
39598 if(!pm && !m.allowOtherMenus){
39600 }else if(pm && pm.activeChild && active != m){
39601 pm.activeChild.hide();
39606 function onMouseDown(e){
39607 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39613 function onBeforeCheck(mi, state){
39615 var g = groups[mi.group];
39616 for(var i = 0, l = g.length; i < l; i++){
39618 g[i].setChecked(false);
39627 * Hides all menus that are currently visible
39629 hideAll : function(){
39634 register : function(menu){
39638 menus[menu.id] = menu;
39639 menu.on("beforehide", onBeforeHide);
39640 menu.on("hide", onHide);
39641 menu.on("beforeshow", onBeforeShow);
39642 menu.on("show", onShow);
39643 var g = menu.group;
39644 if(g && menu.events["checkchange"]){
39648 groups[g].push(menu);
39649 menu.on("checkchange", onCheck);
39654 * Returns a {@link Roo.menu.Menu} object
39655 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39656 * be used to generate and return a new Menu instance.
39658 get : function(menu){
39659 if(typeof menu == "string"){ // menu id
39660 return menus[menu];
39661 }else if(menu.events){ // menu instance
39663 }else if(typeof menu.length == 'number'){ // array of menu items?
39664 return new Roo.menu.Menu({items:menu});
39665 }else{ // otherwise, must be a config
39666 return new Roo.menu.Menu(menu);
39671 unregister : function(menu){
39672 delete menus[menu.id];
39673 menu.un("beforehide", onBeforeHide);
39674 menu.un("hide", onHide);
39675 menu.un("beforeshow", onBeforeShow);
39676 menu.un("show", onShow);
39677 var g = menu.group;
39678 if(g && menu.events["checkchange"]){
39679 groups[g].remove(menu);
39680 menu.un("checkchange", onCheck);
39685 registerCheckable : function(menuItem){
39686 var g = menuItem.group;
39691 groups[g].push(menuItem);
39692 menuItem.on("beforecheckchange", onBeforeCheck);
39697 unregisterCheckable : function(menuItem){
39698 var g = menuItem.group;
39700 groups[g].remove(menuItem);
39701 menuItem.un("beforecheckchange", onBeforeCheck);
39707 * Ext JS Library 1.1.1
39708 * Copyright(c) 2006-2007, Ext JS, LLC.
39710 * Originally Released Under LGPL - original licence link has changed is not relivant.
39713 * <script type="text/javascript">
39718 * @class Roo.menu.BaseItem
39719 * @extends Roo.Component
39721 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
39722 * management and base configuration options shared by all menu components.
39724 * Creates a new BaseItem
39725 * @param {Object} config Configuration options
39727 Roo.menu.BaseItem = function(config){
39728 Roo.menu.BaseItem.superclass.constructor.call(this, config);
39733 * Fires when this item is clicked
39734 * @param {Roo.menu.BaseItem} this
39735 * @param {Roo.EventObject} e
39740 * Fires when this item is activated
39741 * @param {Roo.menu.BaseItem} this
39745 * @event deactivate
39746 * Fires when this item is deactivated
39747 * @param {Roo.menu.BaseItem} this
39753 this.on("click", this.handler, this.scope, true);
39757 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39759 * @cfg {Function} handler
39760 * A function that will handle the click event of this menu item (defaults to undefined)
39763 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39765 canActivate : false,
39768 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39773 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39775 activeClass : "x-menu-item-active",
39777 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39779 hideOnClick : true,
39781 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39786 ctype: "Roo.menu.BaseItem",
39789 actionMode : "container",
39792 render : function(container, parentMenu){
39793 this.parentMenu = parentMenu;
39794 Roo.menu.BaseItem.superclass.render.call(this, container);
39795 this.container.menuItemId = this.id;
39799 onRender : function(container, position){
39800 this.el = Roo.get(this.el);
39801 container.dom.appendChild(this.el.dom);
39805 onClick : function(e){
39806 if(!this.disabled && this.fireEvent("click", this, e) !== false
39807 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39808 this.handleClick(e);
39815 activate : function(){
39819 var li = this.container;
39820 li.addClass(this.activeClass);
39821 this.region = li.getRegion().adjust(2, 2, -2, -2);
39822 this.fireEvent("activate", this);
39827 deactivate : function(){
39828 this.container.removeClass(this.activeClass);
39829 this.fireEvent("deactivate", this);
39833 shouldDeactivate : function(e){
39834 return !this.region || !this.region.contains(e.getPoint());
39838 handleClick : function(e){
39839 if(this.hideOnClick){
39840 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39845 expandMenu : function(autoActivate){
39850 hideMenu : function(){
39855 * Ext JS Library 1.1.1
39856 * Copyright(c) 2006-2007, Ext JS, LLC.
39858 * Originally Released Under LGPL - original licence link has changed is not relivant.
39861 * <script type="text/javascript">
39865 * @class Roo.menu.Adapter
39866 * @extends Roo.menu.BaseItem
39868 * 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.
39869 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39871 * Creates a new Adapter
39872 * @param {Object} config Configuration options
39874 Roo.menu.Adapter = function(component, config){
39875 Roo.menu.Adapter.superclass.constructor.call(this, config);
39876 this.component = component;
39878 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39880 canActivate : true,
39883 onRender : function(container, position){
39884 this.component.render(container);
39885 this.el = this.component.getEl();
39889 activate : function(){
39893 this.component.focus();
39894 this.fireEvent("activate", this);
39899 deactivate : function(){
39900 this.fireEvent("deactivate", this);
39904 disable : function(){
39905 this.component.disable();
39906 Roo.menu.Adapter.superclass.disable.call(this);
39910 enable : function(){
39911 this.component.enable();
39912 Roo.menu.Adapter.superclass.enable.call(this);
39916 * Ext JS Library 1.1.1
39917 * Copyright(c) 2006-2007, Ext JS, LLC.
39919 * Originally Released Under LGPL - original licence link has changed is not relivant.
39922 * <script type="text/javascript">
39926 * @class Roo.menu.TextItem
39927 * @extends Roo.menu.BaseItem
39928 * Adds a static text string to a menu, usually used as either a heading or group separator.
39929 * Note: old style constructor with text is still supported.
39932 * Creates a new TextItem
39933 * @param {Object} cfg Configuration
39935 Roo.menu.TextItem = function(cfg){
39936 if (typeof(cfg) == 'string') {
39939 Roo.apply(this,cfg);
39942 Roo.menu.TextItem.superclass.constructor.call(this);
39945 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39947 * @cfg {String} text Text to show on item.
39952 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39954 hideOnClick : false,
39956 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39958 itemCls : "x-menu-text",
39961 onRender : function(){
39962 var s = document.createElement("span");
39963 s.className = this.itemCls;
39964 s.innerHTML = this.text;
39966 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39970 * Ext JS Library 1.1.1
39971 * Copyright(c) 2006-2007, Ext JS, LLC.
39973 * Originally Released Under LGPL - original licence link has changed is not relivant.
39976 * <script type="text/javascript">
39980 * @class Roo.menu.Separator
39981 * @extends Roo.menu.BaseItem
39982 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39983 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39985 * @param {Object} config Configuration options
39987 Roo.menu.Separator = function(config){
39988 Roo.menu.Separator.superclass.constructor.call(this, config);
39991 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39993 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39995 itemCls : "x-menu-sep",
39997 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39999 hideOnClick : false,
40002 onRender : function(li){
40003 var s = document.createElement("span");
40004 s.className = this.itemCls;
40005 s.innerHTML = " ";
40007 li.addClass("x-menu-sep-li");
40008 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
40012 * Ext JS Library 1.1.1
40013 * Copyright(c) 2006-2007, Ext JS, LLC.
40015 * Originally Released Under LGPL - original licence link has changed is not relivant.
40018 * <script type="text/javascript">
40021 * @class Roo.menu.Item
40022 * @extends Roo.menu.BaseItem
40023 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40024 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40025 * activation and click handling.
40027 * Creates a new Item
40028 * @param {Object} config Configuration options
40030 Roo.menu.Item = function(config){
40031 Roo.menu.Item.superclass.constructor.call(this, config);
40033 this.menu = Roo.menu.MenuMgr.get(this.menu);
40036 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40038 * @cfg {Roo.menu.Menu} menu
40042 * @cfg {String} text
40043 * The text to show on the menu item.
40047 * @cfg {String} html to render in menu
40048 * The text to show on the menu item (HTML version).
40052 * @cfg {String} icon
40053 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40057 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40059 itemCls : "x-menu-item",
40061 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40063 canActivate : true,
40065 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40068 // doc'd in BaseItem
40072 ctype: "Roo.menu.Item",
40075 onRender : function(container, position){
40076 var el = document.createElement("a");
40077 el.hideFocus = true;
40078 el.unselectable = "on";
40079 el.href = this.href || "#";
40080 if(this.hrefTarget){
40081 el.target = this.hrefTarget;
40083 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
40085 var html = this.html.length ? this.html : String.format('{0}',this.text);
40087 el.innerHTML = String.format(
40088 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40089 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40091 Roo.menu.Item.superclass.onRender.call(this, container, position);
40095 * Sets the text to display in this menu item
40096 * @param {String} text The text to display
40097 * @param {Boolean} isHTML true to indicate text is pure html.
40099 setText : function(text, isHTML){
40107 var html = this.html.length ? this.html : String.format('{0}',this.text);
40109 this.el.update(String.format(
40110 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40111 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40112 this.parentMenu.autoWidth();
40117 handleClick : function(e){
40118 if(!this.href){ // if no link defined, stop the event automatically
40121 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40125 activate : function(autoExpand){
40126 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40136 shouldDeactivate : function(e){
40137 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40138 if(this.menu && this.menu.isVisible()){
40139 return !this.menu.getEl().getRegion().contains(e.getPoint());
40147 deactivate : function(){
40148 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40153 expandMenu : function(autoActivate){
40154 if(!this.disabled && this.menu){
40155 clearTimeout(this.hideTimer);
40156 delete this.hideTimer;
40157 if(!this.menu.isVisible() && !this.showTimer){
40158 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40159 }else if (this.menu.isVisible() && autoActivate){
40160 this.menu.tryActivate(0, 1);
40166 deferExpand : function(autoActivate){
40167 delete this.showTimer;
40168 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40170 this.menu.tryActivate(0, 1);
40175 hideMenu : function(){
40176 clearTimeout(this.showTimer);
40177 delete this.showTimer;
40178 if(!this.hideTimer && this.menu && this.menu.isVisible()){
40179 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40184 deferHide : function(){
40185 delete this.hideTimer;
40190 * Ext JS Library 1.1.1
40191 * Copyright(c) 2006-2007, Ext JS, LLC.
40193 * Originally Released Under LGPL - original licence link has changed is not relivant.
40196 * <script type="text/javascript">
40200 * @class Roo.menu.CheckItem
40201 * @extends Roo.menu.Item
40202 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40204 * Creates a new CheckItem
40205 * @param {Object} config Configuration options
40207 Roo.menu.CheckItem = function(config){
40208 Roo.menu.CheckItem.superclass.constructor.call(this, config);
40211 * @event beforecheckchange
40212 * Fires before the checked value is set, providing an opportunity to cancel if needed
40213 * @param {Roo.menu.CheckItem} this
40214 * @param {Boolean} checked The new checked value that will be set
40216 "beforecheckchange" : true,
40218 * @event checkchange
40219 * Fires after the checked value has been set
40220 * @param {Roo.menu.CheckItem} this
40221 * @param {Boolean} checked The checked value that was set
40223 "checkchange" : true
40225 if(this.checkHandler){
40226 this.on('checkchange', this.checkHandler, this.scope);
40229 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40231 * @cfg {String} group
40232 * All check items with the same group name will automatically be grouped into a single-select
40233 * radio button group (defaults to '')
40236 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40238 itemCls : "x-menu-item x-menu-check-item",
40240 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40242 groupClass : "x-menu-group-item",
40245 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
40246 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40247 * initialized with checked = true will be rendered as checked.
40252 ctype: "Roo.menu.CheckItem",
40255 onRender : function(c){
40256 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40258 this.el.addClass(this.groupClass);
40260 Roo.menu.MenuMgr.registerCheckable(this);
40262 this.checked = false;
40263 this.setChecked(true, true);
40268 destroy : function(){
40270 Roo.menu.MenuMgr.unregisterCheckable(this);
40272 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40276 * Set the checked state of this item
40277 * @param {Boolean} checked The new checked value
40278 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40280 setChecked : function(state, suppressEvent){
40281 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40282 if(this.container){
40283 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40285 this.checked = state;
40286 if(suppressEvent !== true){
40287 this.fireEvent("checkchange", this, state);
40293 handleClick : function(e){
40294 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40295 this.setChecked(!this.checked);
40297 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40301 * Ext JS Library 1.1.1
40302 * Copyright(c) 2006-2007, Ext JS, LLC.
40304 * Originally Released Under LGPL - original licence link has changed is not relivant.
40307 * <script type="text/javascript">
40311 * @class Roo.menu.DateItem
40312 * @extends Roo.menu.Adapter
40313 * A menu item that wraps the {@link Roo.DatPicker} component.
40315 * Creates a new DateItem
40316 * @param {Object} config Configuration options
40318 Roo.menu.DateItem = function(config){
40319 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40320 /** The Roo.DatePicker object @type Roo.DatePicker */
40321 this.picker = this.component;
40322 this.addEvents({select: true});
40324 this.picker.on("render", function(picker){
40325 picker.getEl().swallowEvent("click");
40326 picker.container.addClass("x-menu-date-item");
40329 this.picker.on("select", this.onSelect, this);
40332 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40334 onSelect : function(picker, date){
40335 this.fireEvent("select", this, date, picker);
40336 Roo.menu.DateItem.superclass.handleClick.call(this);
40340 * Ext JS Library 1.1.1
40341 * Copyright(c) 2006-2007, Ext JS, LLC.
40343 * Originally Released Under LGPL - original licence link has changed is not relivant.
40346 * <script type="text/javascript">
40350 * @class Roo.menu.ColorItem
40351 * @extends Roo.menu.Adapter
40352 * A menu item that wraps the {@link Roo.ColorPalette} component.
40354 * Creates a new ColorItem
40355 * @param {Object} config Configuration options
40357 Roo.menu.ColorItem = function(config){
40358 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40359 /** The Roo.ColorPalette object @type Roo.ColorPalette */
40360 this.palette = this.component;
40361 this.relayEvents(this.palette, ["select"]);
40362 if(this.selectHandler){
40363 this.on('select', this.selectHandler, this.scope);
40366 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40368 * Ext JS Library 1.1.1
40369 * Copyright(c) 2006-2007, Ext JS, LLC.
40371 * Originally Released Under LGPL - original licence link has changed is not relivant.
40374 * <script type="text/javascript">
40379 * @class Roo.menu.DateMenu
40380 * @extends Roo.menu.Menu
40381 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40383 * Creates a new DateMenu
40384 * @param {Object} config Configuration options
40386 Roo.menu.DateMenu = function(config){
40387 Roo.menu.DateMenu.superclass.constructor.call(this, config);
40389 var di = new Roo.menu.DateItem(config);
40392 * The {@link Roo.DatePicker} instance for this DateMenu
40395 this.picker = di.picker;
40398 * @param {DatePicker} picker
40399 * @param {Date} date
40401 this.relayEvents(di, ["select"]);
40402 this.on('beforeshow', function(){
40404 this.picker.hideMonthPicker(false);
40408 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40412 * Ext JS Library 1.1.1
40413 * Copyright(c) 2006-2007, Ext JS, LLC.
40415 * Originally Released Under LGPL - original licence link has changed is not relivant.
40418 * <script type="text/javascript">
40423 * @class Roo.menu.ColorMenu
40424 * @extends Roo.menu.Menu
40425 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40427 * Creates a new ColorMenu
40428 * @param {Object} config Configuration options
40430 Roo.menu.ColorMenu = function(config){
40431 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40433 var ci = new Roo.menu.ColorItem(config);
40436 * The {@link Roo.ColorPalette} instance for this ColorMenu
40437 * @type ColorPalette
40439 this.palette = ci.palette;
40442 * @param {ColorPalette} palette
40443 * @param {String} color
40445 this.relayEvents(ci, ["select"]);
40447 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40449 * Ext JS Library 1.1.1
40450 * Copyright(c) 2006-2007, Ext JS, LLC.
40452 * Originally Released Under LGPL - original licence link has changed is not relivant.
40455 * <script type="text/javascript">
40459 * @class Roo.form.TextItem
40460 * @extends Roo.BoxComponent
40461 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40463 * Creates a new TextItem
40464 * @param {Object} config Configuration options
40466 Roo.form.TextItem = function(config){
40467 Roo.form.TextItem.superclass.constructor.call(this, config);
40470 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
40473 * @cfg {String} tag the tag for this item (default div)
40477 * @cfg {String} html the content for this item
40481 getAutoCreate : function()
40494 onRender : function(ct, position)
40496 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40499 var cfg = this.getAutoCreate();
40501 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40503 if (!cfg.name.length) {
40506 this.el = ct.createChild(cfg, position);
40511 * @param {String} html update the Contents of the element.
40513 setHTML : function(html)
40515 this.fieldEl.dom.innerHTML = html;
40520 * Ext JS Library 1.1.1
40521 * Copyright(c) 2006-2007, Ext JS, LLC.
40523 * Originally Released Under LGPL - original licence link has changed is not relivant.
40526 * <script type="text/javascript">
40530 * @class Roo.form.Field
40531 * @extends Roo.BoxComponent
40532 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40534 * Creates a new Field
40535 * @param {Object} config Configuration options
40537 Roo.form.Field = function(config){
40538 Roo.form.Field.superclass.constructor.call(this, config);
40541 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
40543 * @cfg {String} fieldLabel Label to use when rendering a form.
40546 * @cfg {String} qtip Mouse over tip
40550 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40552 invalidClass : "x-form-invalid",
40554 * @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")
40556 invalidText : "The value in this field is invalid",
40558 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40560 focusClass : "x-form-focus",
40562 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40563 automatic validation (defaults to "keyup").
40565 validationEvent : "keyup",
40567 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40569 validateOnBlur : true,
40571 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40573 validationDelay : 250,
40575 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40576 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40578 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40580 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40582 fieldClass : "x-form-field",
40584 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
40587 ----------- ----------------------------------------------------------------------
40588 qtip Display a quick tip when the user hovers over the field
40589 title Display a default browser title attribute popup
40590 under Add a block div beneath the field containing the error text
40591 side Add an error icon to the right of the field with a popup on hover
40592 [element id] Add the error text directly to the innerHTML of the specified element
40595 msgTarget : 'qtip',
40597 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40602 * @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.
40607 * @cfg {Boolean} disabled True to disable the field (defaults to false).
40612 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40614 inputType : undefined,
40617 * @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).
40619 tabIndex : undefined,
40622 isFormField : true,
40627 * @property {Roo.Element} fieldEl
40628 * Element Containing the rendered Field (with label etc.)
40631 * @cfg {Mixed} value A value to initialize this field with.
40636 * @cfg {String} name The field's HTML name attribute.
40639 * @cfg {String} cls A CSS class to apply to the field's underlying element.
40642 loadedValue : false,
40646 initComponent : function(){
40647 Roo.form.Field.superclass.initComponent.call(this);
40651 * Fires when this field receives input focus.
40652 * @param {Roo.form.Field} this
40657 * Fires when this field loses input focus.
40658 * @param {Roo.form.Field} this
40662 * @event specialkey
40663 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
40664 * {@link Roo.EventObject#getKey} to determine which key was pressed.
40665 * @param {Roo.form.Field} this
40666 * @param {Roo.EventObject} e The event object
40671 * Fires just before the field blurs if the field value has changed.
40672 * @param {Roo.form.Field} this
40673 * @param {Mixed} newValue The new value
40674 * @param {Mixed} oldValue The original value
40679 * Fires after the field has been marked as invalid.
40680 * @param {Roo.form.Field} this
40681 * @param {String} msg The validation message
40686 * Fires after the field has been validated with no errors.
40687 * @param {Roo.form.Field} this
40692 * Fires after the key up
40693 * @param {Roo.form.Field} this
40694 * @param {Roo.EventObject} e The event Object
40701 * Returns the name attribute of the field if available
40702 * @return {String} name The field name
40704 getName: function(){
40705 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40709 onRender : function(ct, position){
40710 Roo.form.Field.superclass.onRender.call(this, ct, position);
40712 var cfg = this.getAutoCreate();
40714 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40716 if (!cfg.name.length) {
40719 if(this.inputType){
40720 cfg.type = this.inputType;
40722 this.el = ct.createChild(cfg, position);
40724 var type = this.el.dom.type;
40726 if(type == 'password'){
40729 this.el.addClass('x-form-'+type);
40732 this.el.dom.readOnly = true;
40734 if(this.tabIndex !== undefined){
40735 this.el.dom.setAttribute('tabIndex', this.tabIndex);
40738 this.el.addClass([this.fieldClass, this.cls]);
40743 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40744 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40745 * @return {Roo.form.Field} this
40747 applyTo : function(target){
40748 this.allowDomMove = false;
40749 this.el = Roo.get(target);
40750 this.render(this.el.dom.parentNode);
40755 initValue : function(){
40756 if(this.value !== undefined){
40757 this.setValue(this.value);
40758 }else if(this.el.dom.value.length > 0){
40759 this.setValue(this.el.dom.value);
40764 * Returns true if this field has been changed since it was originally loaded and is not disabled.
40765 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
40767 isDirty : function() {
40768 if(this.disabled) {
40771 return String(this.getValue()) !== String(this.originalValue);
40775 * stores the current value in loadedValue
40777 resetHasChanged : function()
40779 this.loadedValue = String(this.getValue());
40782 * checks the current value against the 'loaded' value.
40783 * Note - will return false if 'resetHasChanged' has not been called first.
40785 hasChanged : function()
40787 if(this.disabled || this.readOnly) {
40790 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40796 afterRender : function(){
40797 Roo.form.Field.superclass.afterRender.call(this);
40802 fireKey : function(e){
40803 //Roo.log('field ' + e.getKey());
40804 if(e.isNavKeyPress()){
40805 this.fireEvent("specialkey", this, e);
40810 * Resets the current field value to the originally loaded value and clears any validation messages
40812 reset : function(){
40813 this.setValue(this.resetValue);
40814 this.originalValue = this.getValue();
40815 this.clearInvalid();
40819 initEvents : function(){
40820 // safari killled keypress - so keydown is now used..
40821 this.el.on("keydown" , this.fireKey, this);
40822 this.el.on("focus", this.onFocus, this);
40823 this.el.on("blur", this.onBlur, this);
40824 this.el.relayEvent('keyup', this);
40826 // reference to original value for reset
40827 this.originalValue = this.getValue();
40828 this.resetValue = this.getValue();
40832 onFocus : function(){
40833 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40834 this.el.addClass(this.focusClass);
40836 if(!this.hasFocus){
40837 this.hasFocus = true;
40838 this.startValue = this.getValue();
40839 this.fireEvent("focus", this);
40843 beforeBlur : Roo.emptyFn,
40846 onBlur : function(){
40848 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40849 this.el.removeClass(this.focusClass);
40851 this.hasFocus = false;
40852 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40855 var v = this.getValue();
40856 if(String(v) !== String(this.startValue)){
40857 this.fireEvent('change', this, v, this.startValue);
40859 this.fireEvent("blur", this);
40863 * Returns whether or not the field value is currently valid
40864 * @param {Boolean} preventMark True to disable marking the field invalid
40865 * @return {Boolean} True if the value is valid, else false
40867 isValid : function(preventMark){
40871 var restore = this.preventMark;
40872 this.preventMark = preventMark === true;
40873 var v = this.validateValue(this.processValue(this.getRawValue()));
40874 this.preventMark = restore;
40879 * Validates the field value
40880 * @return {Boolean} True if the value is valid, else false
40882 validate : function(){
40883 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40884 this.clearInvalid();
40890 processValue : function(value){
40895 // Subclasses should provide the validation implementation by overriding this
40896 validateValue : function(value){
40901 * Mark this field as invalid
40902 * @param {String} msg The validation message
40904 markInvalid : function(msg){
40905 if(!this.rendered || this.preventMark){ // not rendered
40909 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40911 obj.el.addClass(this.invalidClass);
40912 msg = msg || this.invalidText;
40913 switch(this.msgTarget){
40915 obj.el.dom.qtip = msg;
40916 obj.el.dom.qclass = 'x-form-invalid-tip';
40917 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40918 Roo.QuickTips.enable();
40922 this.el.dom.title = msg;
40926 var elp = this.el.findParent('.x-form-element', 5, true);
40927 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40928 this.errorEl.setWidth(elp.getWidth(true)-20);
40930 this.errorEl.update(msg);
40931 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40934 if(!this.errorIcon){
40935 var elp = this.el.findParent('.x-form-element', 5, true);
40936 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40938 this.alignErrorIcon();
40939 this.errorIcon.dom.qtip = msg;
40940 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40941 this.errorIcon.show();
40942 this.on('resize', this.alignErrorIcon, this);
40945 var t = Roo.getDom(this.msgTarget);
40947 t.style.display = this.msgDisplay;
40950 this.fireEvent('invalid', this, msg);
40954 alignErrorIcon : function(){
40955 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40959 * Clear any invalid styles/messages for this field
40961 clearInvalid : function(){
40962 if(!this.rendered || this.preventMark){ // not rendered
40965 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40967 obj.el.removeClass(this.invalidClass);
40968 switch(this.msgTarget){
40970 obj.el.dom.qtip = '';
40973 this.el.dom.title = '';
40977 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40981 if(this.errorIcon){
40982 this.errorIcon.dom.qtip = '';
40983 this.errorIcon.hide();
40984 this.un('resize', this.alignErrorIcon, this);
40988 var t = Roo.getDom(this.msgTarget);
40990 t.style.display = 'none';
40993 this.fireEvent('valid', this);
40997 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
40998 * @return {Mixed} value The field value
41000 getRawValue : function(){
41001 var v = this.el.getValue();
41007 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
41008 * @return {Mixed} value The field value
41010 getValue : function(){
41011 var v = this.el.getValue();
41017 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
41018 * @param {Mixed} value The value to set
41020 setRawValue : function(v){
41021 return this.el.dom.value = (v === null || v === undefined ? '' : v);
41025 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
41026 * @param {Mixed} value The value to set
41028 setValue : function(v){
41031 this.el.dom.value = (v === null || v === undefined ? '' : v);
41036 adjustSize : function(w, h){
41037 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41038 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41042 adjustWidth : function(tag, w){
41043 tag = tag.toLowerCase();
41044 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41045 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41046 if(tag == 'input'){
41049 if(tag == 'textarea'){
41052 }else if(Roo.isOpera){
41053 if(tag == 'input'){
41056 if(tag == 'textarea'){
41066 // anything other than normal should be considered experimental
41067 Roo.form.Field.msgFx = {
41069 show: function(msgEl, f){
41070 msgEl.setDisplayed('block');
41073 hide : function(msgEl, f){
41074 msgEl.setDisplayed(false).update('');
41079 show: function(msgEl, f){
41080 msgEl.slideIn('t', {stopFx:true});
41083 hide : function(msgEl, f){
41084 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41089 show: function(msgEl, f){
41090 msgEl.fixDisplay();
41091 msgEl.alignTo(f.el, 'tl-tr');
41092 msgEl.slideIn('l', {stopFx:true});
41095 hide : function(msgEl, f){
41096 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41101 * Ext JS Library 1.1.1
41102 * Copyright(c) 2006-2007, Ext JS, LLC.
41104 * Originally Released Under LGPL - original licence link has changed is not relivant.
41107 * <script type="text/javascript">
41112 * @class Roo.form.TextField
41113 * @extends Roo.form.Field
41114 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
41115 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41117 * Creates a new TextField
41118 * @param {Object} config Configuration options
41120 Roo.form.TextField = function(config){
41121 Roo.form.TextField.superclass.constructor.call(this, config);
41125 * Fires when the autosize function is triggered. The field may or may not have actually changed size
41126 * according to the default logic, but this event provides a hook for the developer to apply additional
41127 * logic at runtime to resize the field if needed.
41128 * @param {Roo.form.Field} this This text field
41129 * @param {Number} width The new field width
41135 Roo.extend(Roo.form.TextField, Roo.form.Field, {
41137 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41141 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41145 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41149 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41153 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41157 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41159 disableKeyFilter : false,
41161 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41165 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41169 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41171 maxLength : Number.MAX_VALUE,
41173 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41175 minLengthText : "The minimum length for this field is {0}",
41177 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41179 maxLengthText : "The maximum length for this field is {0}",
41181 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41183 selectOnFocus : false,
41185 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
41187 allowLeadingSpace : false,
41189 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41191 blankText : "This field is required",
41193 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41194 * If available, this function will be called only after the basic validators all return true, and will be passed the
41195 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41199 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41200 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41201 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
41205 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41209 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41215 initEvents : function()
41217 if (this.emptyText) {
41218 this.el.attr('placeholder', this.emptyText);
41221 Roo.form.TextField.superclass.initEvents.call(this);
41222 if(this.validationEvent == 'keyup'){
41223 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41224 this.el.on('keyup', this.filterValidation, this);
41226 else if(this.validationEvent !== false){
41227 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41230 if(this.selectOnFocus){
41231 this.on("focus", this.preFocus, this);
41233 if (!this.allowLeadingSpace) {
41234 this.on('blur', this.cleanLeadingSpace, this);
41237 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41238 this.el.on("keypress", this.filterKeys, this);
41241 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
41242 this.el.on("click", this.autoSize, this);
41244 if(this.el.is('input[type=password]') && Roo.isSafari){
41245 this.el.on('keydown', this.SafariOnKeyDown, this);
41249 processValue : function(value){
41250 if(this.stripCharsRe){
41251 var newValue = value.replace(this.stripCharsRe, '');
41252 if(newValue !== value){
41253 this.setRawValue(newValue);
41260 filterValidation : function(e){
41261 if(!e.isNavKeyPress()){
41262 this.validationTask.delay(this.validationDelay);
41267 onKeyUp : function(e){
41268 if(!e.isNavKeyPress()){
41272 // private - clean the leading white space
41273 cleanLeadingSpace : function(e)
41275 if ( this.inputType == 'file') {
41279 this.setValue((this.getValue() + '').replace(/^\s+/,''));
41282 * Resets the current field value to the originally-loaded value and clears any validation messages.
41285 reset : function(){
41286 Roo.form.TextField.superclass.reset.call(this);
41290 preFocus : function(){
41292 if(this.selectOnFocus){
41293 this.el.dom.select();
41299 filterKeys : function(e){
41300 var k = e.getKey();
41301 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41304 var c = e.getCharCode(), cc = String.fromCharCode(c);
41305 if(Roo.isIE && (e.isSpecialKey() || !cc)){
41308 if(!this.maskRe.test(cc)){
41313 setValue : function(v){
41315 Roo.form.TextField.superclass.setValue.apply(this, arguments);
41321 * Validates a value according to the field's validation rules and marks the field as invalid
41322 * if the validation fails
41323 * @param {Mixed} value The value to validate
41324 * @return {Boolean} True if the value is valid, else false
41326 validateValue : function(value){
41327 if(value.length < 1) { // if it's blank
41328 if(this.allowBlank){
41329 this.clearInvalid();
41332 this.markInvalid(this.blankText);
41336 if(value.length < this.minLength){
41337 this.markInvalid(String.format(this.minLengthText, this.minLength));
41340 if(value.length > this.maxLength){
41341 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41345 var vt = Roo.form.VTypes;
41346 if(!vt[this.vtype](value, this)){
41347 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41351 if(typeof this.validator == "function"){
41352 var msg = this.validator(value);
41354 this.markInvalid(msg);
41358 if(this.regex && !this.regex.test(value)){
41359 this.markInvalid(this.regexText);
41366 * Selects text in this field
41367 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41368 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41370 selectText : function(start, end){
41371 var v = this.getRawValue();
41373 start = start === undefined ? 0 : start;
41374 end = end === undefined ? v.length : end;
41375 var d = this.el.dom;
41376 if(d.setSelectionRange){
41377 d.setSelectionRange(start, end);
41378 }else if(d.createTextRange){
41379 var range = d.createTextRange();
41380 range.moveStart("character", start);
41381 range.moveEnd("character", v.length-end);
41388 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41389 * This only takes effect if grow = true, and fires the autosize event.
41391 autoSize : function(){
41392 if(!this.grow || !this.rendered){
41396 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41399 var v = el.dom.value;
41400 var d = document.createElement('div');
41401 d.appendChild(document.createTextNode(v));
41405 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41406 this.el.setWidth(w);
41407 this.fireEvent("autosize", this, w);
41411 SafariOnKeyDown : function(event)
41413 // this is a workaround for a password hang bug on chrome/ webkit.
41415 var isSelectAll = false;
41417 if(this.el.dom.selectionEnd > 0){
41418 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41420 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41421 event.preventDefault();
41426 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41428 event.preventDefault();
41429 // this is very hacky as keydown always get's upper case.
41431 var cc = String.fromCharCode(event.getCharCode());
41434 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
41442 * Ext JS Library 1.1.1
41443 * Copyright(c) 2006-2007, Ext JS, LLC.
41445 * Originally Released Under LGPL - original licence link has changed is not relivant.
41448 * <script type="text/javascript">
41452 * @class Roo.form.Hidden
41453 * @extends Roo.form.TextField
41454 * Simple Hidden element used on forms
41456 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41459 * Creates a new Hidden form element.
41460 * @param {Object} config Configuration options
41465 // easy hidden field...
41466 Roo.form.Hidden = function(config){
41467 Roo.form.Hidden.superclass.constructor.call(this, config);
41470 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41472 inputType: 'hidden',
41475 labelSeparator: '',
41477 itemCls : 'x-form-item-display-none'
41485 * Ext JS Library 1.1.1
41486 * Copyright(c) 2006-2007, Ext JS, LLC.
41488 * Originally Released Under LGPL - original licence link has changed is not relivant.
41491 * <script type="text/javascript">
41495 * @class Roo.form.TriggerField
41496 * @extends Roo.form.TextField
41497 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41498 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41499 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41500 * for which you can provide a custom implementation. For example:
41502 var trigger = new Roo.form.TriggerField();
41503 trigger.onTriggerClick = myTriggerFn;
41504 trigger.applyTo('my-field');
41507 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41508 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41509 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
41510 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41512 * Create a new TriggerField.
41513 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41514 * to the base TextField)
41516 Roo.form.TriggerField = function(config){
41517 this.mimicing = false;
41518 Roo.form.TriggerField.superclass.constructor.call(this, config);
41521 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
41523 * @cfg {String} triggerClass A CSS class to apply to the trigger
41526 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41527 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41529 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41531 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41535 /** @cfg {Boolean} grow @hide */
41536 /** @cfg {Number} growMin @hide */
41537 /** @cfg {Number} growMax @hide */
41543 autoSize: Roo.emptyFn,
41547 deferHeight : true,
41550 actionMode : 'wrap',
41552 onResize : function(w, h){
41553 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41554 if(typeof w == 'number'){
41555 var x = w - this.trigger.getWidth();
41556 this.el.setWidth(this.adjustWidth('input', x));
41557 this.trigger.setStyle('left', x+'px');
41562 adjustSize : Roo.BoxComponent.prototype.adjustSize,
41565 getResizeEl : function(){
41570 getPositionEl : function(){
41575 alignErrorIcon : function(){
41576 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41580 onRender : function(ct, position){
41581 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41582 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41583 this.trigger = this.wrap.createChild(this.triggerConfig ||
41584 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41585 if(this.hideTrigger){
41586 this.trigger.setDisplayed(false);
41588 this.initTrigger();
41590 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41595 initTrigger : function(){
41596 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41597 this.trigger.addClassOnOver('x-form-trigger-over');
41598 this.trigger.addClassOnClick('x-form-trigger-click');
41602 onDestroy : function(){
41604 this.trigger.removeAllListeners();
41605 this.trigger.remove();
41608 this.wrap.remove();
41610 Roo.form.TriggerField.superclass.onDestroy.call(this);
41614 onFocus : function(){
41615 Roo.form.TriggerField.superclass.onFocus.call(this);
41616 if(!this.mimicing){
41617 this.wrap.addClass('x-trigger-wrap-focus');
41618 this.mimicing = true;
41619 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41620 if(this.monitorTab){
41621 this.el.on("keydown", this.checkTab, this);
41627 checkTab : function(e){
41628 if(e.getKey() == e.TAB){
41629 this.triggerBlur();
41634 onBlur : function(){
41639 mimicBlur : function(e, t){
41640 if(!this.wrap.contains(t) && this.validateBlur()){
41641 this.triggerBlur();
41646 triggerBlur : function(){
41647 this.mimicing = false;
41648 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41649 if(this.monitorTab){
41650 this.el.un("keydown", this.checkTab, this);
41652 this.wrap.removeClass('x-trigger-wrap-focus');
41653 Roo.form.TriggerField.superclass.onBlur.call(this);
41657 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41658 validateBlur : function(e, t){
41663 onDisable : function(){
41664 Roo.form.TriggerField.superclass.onDisable.call(this);
41666 this.wrap.addClass('x-item-disabled');
41671 onEnable : function(){
41672 Roo.form.TriggerField.superclass.onEnable.call(this);
41674 this.wrap.removeClass('x-item-disabled');
41679 onShow : function(){
41680 var ae = this.getActionEl();
41683 ae.dom.style.display = '';
41684 ae.dom.style.visibility = 'visible';
41690 onHide : function(){
41691 var ae = this.getActionEl();
41692 ae.dom.style.display = 'none';
41696 * The function that should handle the trigger's click event. This method does nothing by default until overridden
41697 * by an implementing function.
41699 * @param {EventObject} e
41701 onTriggerClick : Roo.emptyFn
41704 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
41705 // to be extended by an implementing class. For an example of implementing this class, see the custom
41706 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41707 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41708 initComponent : function(){
41709 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41711 this.triggerConfig = {
41712 tag:'span', cls:'x-form-twin-triggers', cn:[
41713 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41714 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41718 getTrigger : function(index){
41719 return this.triggers[index];
41722 initTrigger : function(){
41723 var ts = this.trigger.select('.x-form-trigger', true);
41724 this.wrap.setStyle('overflow', 'hidden');
41725 var triggerField = this;
41726 ts.each(function(t, all, index){
41727 t.hide = function(){
41728 var w = triggerField.wrap.getWidth();
41729 this.dom.style.display = 'none';
41730 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41732 t.show = function(){
41733 var w = triggerField.wrap.getWidth();
41734 this.dom.style.display = '';
41735 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41737 var triggerIndex = 'Trigger'+(index+1);
41739 if(this['hide'+triggerIndex]){
41740 t.dom.style.display = 'none';
41742 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41743 t.addClassOnOver('x-form-trigger-over');
41744 t.addClassOnClick('x-form-trigger-click');
41746 this.triggers = ts.elements;
41749 onTrigger1Click : Roo.emptyFn,
41750 onTrigger2Click : Roo.emptyFn
41753 * Ext JS Library 1.1.1
41754 * Copyright(c) 2006-2007, Ext JS, LLC.
41756 * Originally Released Under LGPL - original licence link has changed is not relivant.
41759 * <script type="text/javascript">
41763 * @class Roo.form.TextArea
41764 * @extends Roo.form.TextField
41765 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
41766 * support for auto-sizing.
41768 * Creates a new TextArea
41769 * @param {Object} config Configuration options
41771 Roo.form.TextArea = function(config){
41772 Roo.form.TextArea.superclass.constructor.call(this, config);
41773 // these are provided exchanges for backwards compat
41774 // minHeight/maxHeight were replaced by growMin/growMax to be
41775 // compatible with TextField growing config values
41776 if(this.minHeight !== undefined){
41777 this.growMin = this.minHeight;
41779 if(this.maxHeight !== undefined){
41780 this.growMax = this.maxHeight;
41784 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
41786 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41790 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41794 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41795 * in the field (equivalent to setting overflow: hidden, defaults to false)
41797 preventScrollbars: false,
41799 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41800 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41804 onRender : function(ct, position){
41806 this.defaultAutoCreate = {
41808 style:"width:300px;height:60px;",
41809 autocomplete: "new-password"
41812 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41814 this.textSizeEl = Roo.DomHelper.append(document.body, {
41815 tag: "pre", cls: "x-form-grow-sizer"
41817 if(this.preventScrollbars){
41818 this.el.setStyle("overflow", "hidden");
41820 this.el.setHeight(this.growMin);
41824 onDestroy : function(){
41825 if(this.textSizeEl){
41826 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41828 Roo.form.TextArea.superclass.onDestroy.call(this);
41832 onKeyUp : function(e){
41833 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41839 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41840 * This only takes effect if grow = true, and fires the autosize event if the height changes.
41842 autoSize : function(){
41843 if(!this.grow || !this.textSizeEl){
41847 var v = el.dom.value;
41848 var ts = this.textSizeEl;
41851 ts.appendChild(document.createTextNode(v));
41854 Roo.fly(ts).setWidth(this.el.getWidth());
41856 v = "  ";
41859 v = v.replace(/\n/g, '<p> </p>');
41861 v += " \n ";
41864 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41865 if(h != this.lastHeight){
41866 this.lastHeight = h;
41867 this.el.setHeight(h);
41868 this.fireEvent("autosize", this, h);
41873 * Ext JS Library 1.1.1
41874 * Copyright(c) 2006-2007, Ext JS, LLC.
41876 * Originally Released Under LGPL - original licence link has changed is not relivant.
41879 * <script type="text/javascript">
41884 * @class Roo.form.NumberField
41885 * @extends Roo.form.TextField
41886 * Numeric text field that provides automatic keystroke filtering and numeric validation.
41888 * Creates a new NumberField
41889 * @param {Object} config Configuration options
41891 Roo.form.NumberField = function(config){
41892 Roo.form.NumberField.superclass.constructor.call(this, config);
41895 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
41897 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41899 fieldClass: "x-form-field x-form-num-field",
41901 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41903 allowDecimals : true,
41905 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41907 decimalSeparator : ".",
41909 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41911 decimalPrecision : 2,
41913 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41915 allowNegative : true,
41917 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41919 minValue : Number.NEGATIVE_INFINITY,
41921 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41923 maxValue : Number.MAX_VALUE,
41925 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41927 minText : "The minimum value for this field is {0}",
41929 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41931 maxText : "The maximum value for this field is {0}",
41933 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41934 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41936 nanText : "{0} is not a valid number",
41939 initEvents : function(){
41940 Roo.form.NumberField.superclass.initEvents.call(this);
41941 var allowed = "0123456789";
41942 if(this.allowDecimals){
41943 allowed += this.decimalSeparator;
41945 if(this.allowNegative){
41948 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41949 var keyPress = function(e){
41950 var k = e.getKey();
41951 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41954 var c = e.getCharCode();
41955 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41959 this.el.on("keypress", keyPress, this);
41963 validateValue : function(value){
41964 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41967 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41970 var num = this.parseValue(value);
41972 this.markInvalid(String.format(this.nanText, value));
41975 if(num < this.minValue){
41976 this.markInvalid(String.format(this.minText, this.minValue));
41979 if(num > this.maxValue){
41980 this.markInvalid(String.format(this.maxText, this.maxValue));
41986 getValue : function(){
41987 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41991 parseValue : function(value){
41992 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41993 return isNaN(value) ? '' : value;
41997 fixPrecision : function(value){
41998 var nan = isNaN(value);
41999 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42000 return nan ? '' : value;
42002 return parseFloat(value).toFixed(this.decimalPrecision);
42005 setValue : function(v){
42006 v = this.fixPrecision(v);
42007 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
42011 decimalPrecisionFcn : function(v){
42012 return Math.floor(v);
42015 beforeBlur : function(){
42016 var v = this.parseValue(this.getRawValue());
42023 * Ext JS Library 1.1.1
42024 * Copyright(c) 2006-2007, Ext JS, LLC.
42026 * Originally Released Under LGPL - original licence link has changed is not relivant.
42029 * <script type="text/javascript">
42033 * @class Roo.form.DateField
42034 * @extends Roo.form.TriggerField
42035 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42037 * Create a new DateField
42038 * @param {Object} config
42040 Roo.form.DateField = function(config)
42042 Roo.form.DateField.superclass.constructor.call(this, config);
42048 * Fires when a date is selected
42049 * @param {Roo.form.DateField} combo This combo box
42050 * @param {Date} date The date selected
42057 if(typeof this.minValue == "string") {
42058 this.minValue = this.parseDate(this.minValue);
42060 if(typeof this.maxValue == "string") {
42061 this.maxValue = this.parseDate(this.maxValue);
42063 this.ddMatch = null;
42064 if(this.disabledDates){
42065 var dd = this.disabledDates;
42067 for(var i = 0; i < dd.length; i++){
42069 if(i != dd.length-1) {
42073 this.ddMatch = new RegExp(re + ")");
42077 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
42079 * @cfg {String} format
42080 * The default date format string which can be overriden for localization support. The format must be
42081 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42085 * @cfg {String} altFormats
42086 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42087 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42089 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42091 * @cfg {Array} disabledDays
42092 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42094 disabledDays : null,
42096 * @cfg {String} disabledDaysText
42097 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42099 disabledDaysText : "Disabled",
42101 * @cfg {Array} disabledDates
42102 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42103 * expression so they are very powerful. Some examples:
42105 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42106 * <li>["03/08", "09/16"] would disable those days for every year</li>
42107 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42108 * <li>["03/../2006"] would disable every day in March 2006</li>
42109 * <li>["^03"] would disable every day in every March</li>
42111 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42112 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42114 disabledDates : null,
42116 * @cfg {String} disabledDatesText
42117 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42119 disabledDatesText : "Disabled",
42123 * @cfg {Date/String} zeroValue
42124 * if the date is less that this number, then the field is rendered as empty
42127 zeroValue : '1800-01-01',
42131 * @cfg {Date/String} minValue
42132 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42133 * valid format (defaults to null).
42137 * @cfg {Date/String} maxValue
42138 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42139 * valid format (defaults to null).
42143 * @cfg {String} minText
42144 * The error text to display when the date in the cell is before minValue (defaults to
42145 * 'The date in this field must be after {minValue}').
42147 minText : "The date in this field must be equal to or after {0}",
42149 * @cfg {String} maxText
42150 * The error text to display when the date in the cell is after maxValue (defaults to
42151 * 'The date in this field must be before {maxValue}').
42153 maxText : "The date in this field must be equal to or before {0}",
42155 * @cfg {String} invalidText
42156 * The error text to display when the date in the field is invalid (defaults to
42157 * '{value} is not a valid date - it must be in the format {format}').
42159 invalidText : "{0} is not a valid date - it must be in the format {1}",
42161 * @cfg {String} triggerClass
42162 * An additional CSS class used to style the trigger button. The trigger will always get the
42163 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42164 * which displays a calendar icon).
42166 triggerClass : 'x-form-date-trigger',
42170 * @cfg {Boolean} useIso
42171 * if enabled, then the date field will use a hidden field to store the
42172 * real value as iso formated date. default (false)
42176 * @cfg {String/Object} autoCreate
42177 * A DomHelper element spec, or true for a default element spec (defaults to
42178 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42181 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42184 hiddenField: false,
42186 onRender : function(ct, position)
42188 Roo.form.DateField.superclass.onRender.call(this, ct, position);
42190 //this.el.dom.removeAttribute('name');
42191 Roo.log("Changing name?");
42192 this.el.dom.setAttribute('name', this.name + '____hidden___' );
42193 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42195 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42196 // prevent input submission
42197 this.hiddenName = this.name;
42204 validateValue : function(value)
42206 value = this.formatDate(value);
42207 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42208 Roo.log('super failed');
42211 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42214 var svalue = value;
42215 value = this.parseDate(value);
42217 Roo.log('parse date failed' + svalue);
42218 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42221 var time = value.getTime();
42222 if(this.minValue && time < this.minValue.getTime()){
42223 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42226 if(this.maxValue && time > this.maxValue.getTime()){
42227 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42230 if(this.disabledDays){
42231 var day = value.getDay();
42232 for(var i = 0; i < this.disabledDays.length; i++) {
42233 if(day === this.disabledDays[i]){
42234 this.markInvalid(this.disabledDaysText);
42239 var fvalue = this.formatDate(value);
42240 if(this.ddMatch && this.ddMatch.test(fvalue)){
42241 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42248 // Provides logic to override the default TriggerField.validateBlur which just returns true
42249 validateBlur : function(){
42250 return !this.menu || !this.menu.isVisible();
42253 getName: function()
42255 // returns hidden if it's set..
42256 if (!this.rendered) {return ''};
42257 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42262 * Returns the current date value of the date field.
42263 * @return {Date} The date value
42265 getValue : function(){
42267 return this.hiddenField ?
42268 this.hiddenField.value :
42269 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42273 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42274 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42275 * (the default format used is "m/d/y").
42278 //All of these calls set the same date value (May 4, 2006)
42280 //Pass a date object:
42281 var dt = new Date('5/4/06');
42282 dateField.setValue(dt);
42284 //Pass a date string (default format):
42285 dateField.setValue('5/4/06');
42287 //Pass a date string (custom format):
42288 dateField.format = 'Y-m-d';
42289 dateField.setValue('2006-5-4');
42291 * @param {String/Date} date The date or valid date string
42293 setValue : function(date){
42294 if (this.hiddenField) {
42295 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42297 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42298 // make sure the value field is always stored as a date..
42299 this.value = this.parseDate(date);
42305 parseDate : function(value){
42307 if (value instanceof Date) {
42308 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42315 if(!value || value instanceof Date){
42318 var v = Date.parseDate(value, this.format);
42319 if (!v && this.useIso) {
42320 v = Date.parseDate(value, 'Y-m-d');
42322 if(!v && this.altFormats){
42323 if(!this.altFormatsArray){
42324 this.altFormatsArray = this.altFormats.split("|");
42326 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42327 v = Date.parseDate(value, this.altFormatsArray[i]);
42330 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42337 formatDate : function(date, fmt){
42338 return (!date || !(date instanceof Date)) ?
42339 date : date.dateFormat(fmt || this.format);
42344 select: function(m, d){
42347 this.fireEvent('select', this, d);
42349 show : function(){ // retain focus styling
42353 this.focus.defer(10, this);
42354 var ml = this.menuListeners;
42355 this.menu.un("select", ml.select, this);
42356 this.menu.un("show", ml.show, this);
42357 this.menu.un("hide", ml.hide, this);
42362 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42363 onTriggerClick : function(){
42367 if(this.menu == null){
42368 this.menu = new Roo.menu.DateMenu();
42370 Roo.apply(this.menu.picker, {
42371 showClear: this.allowBlank,
42372 minDate : this.minValue,
42373 maxDate : this.maxValue,
42374 disabledDatesRE : this.ddMatch,
42375 disabledDatesText : this.disabledDatesText,
42376 disabledDays : this.disabledDays,
42377 disabledDaysText : this.disabledDaysText,
42378 format : this.useIso ? 'Y-m-d' : this.format,
42379 minText : String.format(this.minText, this.formatDate(this.minValue)),
42380 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42382 this.menu.on(Roo.apply({}, this.menuListeners, {
42385 this.menu.picker.setValue(this.getValue() || new Date());
42386 this.menu.show(this.el, "tl-bl?");
42389 beforeBlur : function(){
42390 var v = this.parseDate(this.getRawValue());
42400 isDirty : function() {
42401 if(this.disabled) {
42405 if(typeof(this.startValue) === 'undefined'){
42409 return String(this.getValue()) !== String(this.startValue);
42413 cleanLeadingSpace : function(e)
42420 * Ext JS Library 1.1.1
42421 * Copyright(c) 2006-2007, Ext JS, LLC.
42423 * Originally Released Under LGPL - original licence link has changed is not relivant.
42426 * <script type="text/javascript">
42430 * @class Roo.form.MonthField
42431 * @extends Roo.form.TriggerField
42432 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42434 * Create a new MonthField
42435 * @param {Object} config
42437 Roo.form.MonthField = function(config){
42439 Roo.form.MonthField.superclass.constructor.call(this, config);
42445 * Fires when a date is selected
42446 * @param {Roo.form.MonthFieeld} combo This combo box
42447 * @param {Date} date The date selected
42454 if(typeof this.minValue == "string") {
42455 this.minValue = this.parseDate(this.minValue);
42457 if(typeof this.maxValue == "string") {
42458 this.maxValue = this.parseDate(this.maxValue);
42460 this.ddMatch = null;
42461 if(this.disabledDates){
42462 var dd = this.disabledDates;
42464 for(var i = 0; i < dd.length; i++){
42466 if(i != dd.length-1) {
42470 this.ddMatch = new RegExp(re + ")");
42474 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
42476 * @cfg {String} format
42477 * The default date format string which can be overriden for localization support. The format must be
42478 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42482 * @cfg {String} altFormats
42483 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42484 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42486 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42488 * @cfg {Array} disabledDays
42489 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42491 disabledDays : [0,1,2,3,4,5,6],
42493 * @cfg {String} disabledDaysText
42494 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42496 disabledDaysText : "Disabled",
42498 * @cfg {Array} disabledDates
42499 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42500 * expression so they are very powerful. Some examples:
42502 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42503 * <li>["03/08", "09/16"] would disable those days for every year</li>
42504 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42505 * <li>["03/../2006"] would disable every day in March 2006</li>
42506 * <li>["^03"] would disable every day in every March</li>
42508 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42509 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42511 disabledDates : null,
42513 * @cfg {String} disabledDatesText
42514 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42516 disabledDatesText : "Disabled",
42518 * @cfg {Date/String} minValue
42519 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42520 * valid format (defaults to null).
42524 * @cfg {Date/String} maxValue
42525 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42526 * valid format (defaults to null).
42530 * @cfg {String} minText
42531 * The error text to display when the date in the cell is before minValue (defaults to
42532 * 'The date in this field must be after {minValue}').
42534 minText : "The date in this field must be equal to or after {0}",
42536 * @cfg {String} maxTextf
42537 * The error text to display when the date in the cell is after maxValue (defaults to
42538 * 'The date in this field must be before {maxValue}').
42540 maxText : "The date in this field must be equal to or before {0}",
42542 * @cfg {String} invalidText
42543 * The error text to display when the date in the field is invalid (defaults to
42544 * '{value} is not a valid date - it must be in the format {format}').
42546 invalidText : "{0} is not a valid date - it must be in the format {1}",
42548 * @cfg {String} triggerClass
42549 * An additional CSS class used to style the trigger button. The trigger will always get the
42550 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42551 * which displays a calendar icon).
42553 triggerClass : 'x-form-date-trigger',
42557 * @cfg {Boolean} useIso
42558 * if enabled, then the date field will use a hidden field to store the
42559 * real value as iso formated date. default (true)
42563 * @cfg {String/Object} autoCreate
42564 * A DomHelper element spec, or true for a default element spec (defaults to
42565 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42568 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42571 hiddenField: false,
42573 hideMonthPicker : false,
42575 onRender : function(ct, position)
42577 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42579 this.el.dom.removeAttribute('name');
42580 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42582 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42583 // prevent input submission
42584 this.hiddenName = this.name;
42591 validateValue : function(value)
42593 value = this.formatDate(value);
42594 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42597 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42600 var svalue = value;
42601 value = this.parseDate(value);
42603 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42606 var time = value.getTime();
42607 if(this.minValue && time < this.minValue.getTime()){
42608 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42611 if(this.maxValue && time > this.maxValue.getTime()){
42612 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42615 /*if(this.disabledDays){
42616 var day = value.getDay();
42617 for(var i = 0; i < this.disabledDays.length; i++) {
42618 if(day === this.disabledDays[i]){
42619 this.markInvalid(this.disabledDaysText);
42625 var fvalue = this.formatDate(value);
42626 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42627 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42635 // Provides logic to override the default TriggerField.validateBlur which just returns true
42636 validateBlur : function(){
42637 return !this.menu || !this.menu.isVisible();
42641 * Returns the current date value of the date field.
42642 * @return {Date} The date value
42644 getValue : function(){
42648 return this.hiddenField ?
42649 this.hiddenField.value :
42650 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42654 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42655 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42656 * (the default format used is "m/d/y").
42659 //All of these calls set the same date value (May 4, 2006)
42661 //Pass a date object:
42662 var dt = new Date('5/4/06');
42663 monthField.setValue(dt);
42665 //Pass a date string (default format):
42666 monthField.setValue('5/4/06');
42668 //Pass a date string (custom format):
42669 monthField.format = 'Y-m-d';
42670 monthField.setValue('2006-5-4');
42672 * @param {String/Date} date The date or valid date string
42674 setValue : function(date){
42675 Roo.log('month setValue' + date);
42676 // can only be first of month..
42678 var val = this.parseDate(date);
42680 if (this.hiddenField) {
42681 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42683 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42684 this.value = this.parseDate(date);
42688 parseDate : function(value){
42689 if(!value || value instanceof Date){
42690 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42693 var v = Date.parseDate(value, this.format);
42694 if (!v && this.useIso) {
42695 v = Date.parseDate(value, 'Y-m-d');
42699 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42703 if(!v && this.altFormats){
42704 if(!this.altFormatsArray){
42705 this.altFormatsArray = this.altFormats.split("|");
42707 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42708 v = Date.parseDate(value, this.altFormatsArray[i]);
42715 formatDate : function(date, fmt){
42716 return (!date || !(date instanceof Date)) ?
42717 date : date.dateFormat(fmt || this.format);
42722 select: function(m, d){
42724 this.fireEvent('select', this, d);
42726 show : function(){ // retain focus styling
42730 this.focus.defer(10, this);
42731 var ml = this.menuListeners;
42732 this.menu.un("select", ml.select, this);
42733 this.menu.un("show", ml.show, this);
42734 this.menu.un("hide", ml.hide, this);
42738 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42739 onTriggerClick : function(){
42743 if(this.menu == null){
42744 this.menu = new Roo.menu.DateMenu();
42748 Roo.apply(this.menu.picker, {
42750 showClear: this.allowBlank,
42751 minDate : this.minValue,
42752 maxDate : this.maxValue,
42753 disabledDatesRE : this.ddMatch,
42754 disabledDatesText : this.disabledDatesText,
42756 format : this.useIso ? 'Y-m-d' : this.format,
42757 minText : String.format(this.minText, this.formatDate(this.minValue)),
42758 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42761 this.menu.on(Roo.apply({}, this.menuListeners, {
42769 // hide month picker get's called when we called by 'before hide';
42771 var ignorehide = true;
42772 p.hideMonthPicker = function(disableAnim){
42776 if(this.monthPicker){
42777 Roo.log("hideMonthPicker called");
42778 if(disableAnim === true){
42779 this.monthPicker.hide();
42781 this.monthPicker.slideOut('t', {duration:.2});
42782 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42783 p.fireEvent("select", this, this.value);
42789 Roo.log('picker set value');
42790 Roo.log(this.getValue());
42791 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42792 m.show(this.el, 'tl-bl?');
42793 ignorehide = false;
42794 // this will trigger hideMonthPicker..
42797 // hidden the day picker
42798 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42804 p.showMonthPicker.defer(100, p);
42810 beforeBlur : function(){
42811 var v = this.parseDate(this.getRawValue());
42817 /** @cfg {Boolean} grow @hide */
42818 /** @cfg {Number} growMin @hide */
42819 /** @cfg {Number} growMax @hide */
42826 * Ext JS Library 1.1.1
42827 * Copyright(c) 2006-2007, Ext JS, LLC.
42829 * Originally Released Under LGPL - original licence link has changed is not relivant.
42832 * <script type="text/javascript">
42837 * @class Roo.form.ComboBox
42838 * @extends Roo.form.TriggerField
42839 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42841 * Create a new ComboBox.
42842 * @param {Object} config Configuration options
42844 Roo.form.ComboBox = function(config){
42845 Roo.form.ComboBox.superclass.constructor.call(this, config);
42849 * Fires when the dropdown list is expanded
42850 * @param {Roo.form.ComboBox} combo This combo box
42855 * Fires when the dropdown list is collapsed
42856 * @param {Roo.form.ComboBox} combo This combo box
42860 * @event beforeselect
42861 * Fires before a list item is selected. Return false to cancel the selection.
42862 * @param {Roo.form.ComboBox} combo This combo box
42863 * @param {Roo.data.Record} record The data record returned from the underlying store
42864 * @param {Number} index The index of the selected item in the dropdown list
42866 'beforeselect' : true,
42869 * Fires when a list item is selected
42870 * @param {Roo.form.ComboBox} combo This combo box
42871 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42872 * @param {Number} index The index of the selected item in the dropdown list
42876 * @event beforequery
42877 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42878 * The event object passed has these properties:
42879 * @param {Roo.form.ComboBox} combo This combo box
42880 * @param {String} query The query
42881 * @param {Boolean} forceAll true to force "all" query
42882 * @param {Boolean} cancel true to cancel the query
42883 * @param {Object} e The query event object
42885 'beforequery': true,
42888 * Fires when the 'add' icon is pressed (add a listener to enable add button)
42889 * @param {Roo.form.ComboBox} combo This combo box
42894 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42895 * @param {Roo.form.ComboBox} combo This combo box
42896 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42902 if(this.transform){
42903 this.allowDomMove = false;
42904 var s = Roo.getDom(this.transform);
42905 if(!this.hiddenName){
42906 this.hiddenName = s.name;
42909 this.mode = 'local';
42910 var d = [], opts = s.options;
42911 for(var i = 0, len = opts.length;i < len; i++){
42913 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42915 this.value = value;
42917 d.push([value, o.text]);
42919 this.store = new Roo.data.SimpleStore({
42921 fields: ['value', 'text'],
42924 this.valueField = 'value';
42925 this.displayField = 'text';
42927 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42928 if(!this.lazyRender){
42929 this.target = true;
42930 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42931 s.parentNode.removeChild(s); // remove it
42932 this.render(this.el.parentNode);
42934 s.parentNode.removeChild(s); // remove it
42939 this.store = Roo.factory(this.store, Roo.data);
42942 this.selectedIndex = -1;
42943 if(this.mode == 'local'){
42944 if(config.queryDelay === undefined){
42945 this.queryDelay = 10;
42947 if(config.minChars === undefined){
42953 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42955 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42958 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42959 * rendering into an Roo.Editor, defaults to false)
42962 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42963 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42966 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42969 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42970 * the dropdown list (defaults to undefined, with no header element)
42974 * @cfg {String/Roo.Template} tpl The template to use to render the output
42978 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42980 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42982 listWidth: undefined,
42984 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42985 * mode = 'remote' or 'text' if mode = 'local')
42987 displayField: undefined,
42989 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42990 * mode = 'remote' or 'value' if mode = 'local').
42991 * Note: use of a valueField requires the user make a selection
42992 * in order for a value to be mapped.
42994 valueField: undefined,
42998 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42999 * field's data value (defaults to the underlying DOM element's name)
43001 hiddenName: undefined,
43003 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
43007 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
43009 selectedClass: 'x-combo-selected',
43011 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
43012 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
43013 * which displays a downward arrow icon).
43015 triggerClass : 'x-form-arrow-trigger',
43017 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
43021 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
43022 * anchor positions (defaults to 'tl-bl')
43024 listAlign: 'tl-bl?',
43026 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43030 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
43031 * query specified by the allQuery config option (defaults to 'query')
43033 triggerAction: 'query',
43035 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43036 * (defaults to 4, does not apply if editable = false)
43040 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43041 * delay (typeAheadDelay) if it matches a known value (defaults to false)
43045 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43046 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43050 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43051 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
43055 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
43056 * when editable = true (defaults to false)
43058 selectOnFocus:false,
43060 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43062 queryParam: 'query',
43064 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
43065 * when mode = 'remote' (defaults to 'Loading...')
43067 loadingText: 'Loading...',
43069 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43073 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43077 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43078 * traditional select (defaults to true)
43082 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43086 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43090 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43091 * listWidth has a higher value)
43095 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43096 * allow the user to set arbitrary text into the field (defaults to false)
43098 forceSelection:false,
43100 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43101 * if typeAhead = true (defaults to 250)
43103 typeAheadDelay : 250,
43105 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43106 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43108 valueNotFoundText : undefined,
43110 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43112 blockFocus : false,
43115 * @cfg {Boolean} disableClear Disable showing of clear button.
43117 disableClear : false,
43119 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
43121 alwaysQuery : false,
43127 // element that contains real text value.. (when hidden is used..)
43130 onRender : function(ct, position)
43132 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43134 if(this.hiddenName){
43135 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
43137 this.hiddenField.value =
43138 this.hiddenValue !== undefined ? this.hiddenValue :
43139 this.value !== undefined ? this.value : '';
43141 // prevent input submission
43142 this.el.dom.removeAttribute('name');
43148 this.el.dom.setAttribute('autocomplete', 'off');
43151 var cls = 'x-combo-list';
43153 this.list = new Roo.Layer({
43154 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43157 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43158 this.list.setWidth(lw);
43159 this.list.swallowEvent('mousewheel');
43160 this.assetHeight = 0;
43163 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43164 this.assetHeight += this.header.getHeight();
43167 this.innerList = this.list.createChild({cls:cls+'-inner'});
43168 this.innerList.on('mouseover', this.onViewOver, this);
43169 this.innerList.on('mousemove', this.onViewMove, this);
43170 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43172 if(this.allowBlank && !this.pageSize && !this.disableClear){
43173 this.footer = this.list.createChild({cls:cls+'-ft'});
43174 this.pageTb = new Roo.Toolbar(this.footer);
43178 this.footer = this.list.createChild({cls:cls+'-ft'});
43179 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43180 {pageSize: this.pageSize});
43184 if (this.pageTb && this.allowBlank && !this.disableClear) {
43186 this.pageTb.add(new Roo.Toolbar.Fill(), {
43187 cls: 'x-btn-icon x-btn-clear',
43189 handler: function()
43192 _this.clearValue();
43193 _this.onSelect(false, -1);
43198 this.assetHeight += this.footer.getHeight();
43203 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43206 this.view = new Roo.View(this.innerList, this.tpl, {
43209 selectedClass: this.selectedClass
43212 this.view.on('click', this.onViewClick, this);
43214 this.store.on('beforeload', this.onBeforeLoad, this);
43215 this.store.on('load', this.onLoad, this);
43216 this.store.on('loadexception', this.onLoadException, this);
43218 if(this.resizable){
43219 this.resizer = new Roo.Resizable(this.list, {
43220 pinned:true, handles:'se'
43222 this.resizer.on('resize', function(r, w, h){
43223 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43224 this.listWidth = w;
43225 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43226 this.restrictHeight();
43228 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43230 if(!this.editable){
43231 this.editable = true;
43232 this.setEditable(false);
43236 if (typeof(this.events.add.listeners) != 'undefined') {
43238 this.addicon = this.wrap.createChild(
43239 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
43241 this.addicon.on('click', function(e) {
43242 this.fireEvent('add', this);
43245 if (typeof(this.events.edit.listeners) != 'undefined') {
43247 this.editicon = this.wrap.createChild(
43248 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
43249 if (this.addicon) {
43250 this.editicon.setStyle('margin-left', '40px');
43252 this.editicon.on('click', function(e) {
43254 // we fire even if inothing is selected..
43255 this.fireEvent('edit', this, this.lastData );
43265 initEvents : function(){
43266 Roo.form.ComboBox.superclass.initEvents.call(this);
43268 this.keyNav = new Roo.KeyNav(this.el, {
43269 "up" : function(e){
43270 this.inKeyMode = true;
43274 "down" : function(e){
43275 if(!this.isExpanded()){
43276 this.onTriggerClick();
43278 this.inKeyMode = true;
43283 "enter" : function(e){
43284 this.onViewClick();
43288 "esc" : function(e){
43292 "tab" : function(e){
43293 this.onViewClick(false);
43294 this.fireEvent("specialkey", this, e);
43300 doRelay : function(foo, bar, hname){
43301 if(hname == 'down' || this.scope.isExpanded()){
43302 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43309 this.queryDelay = Math.max(this.queryDelay || 10,
43310 this.mode == 'local' ? 10 : 250);
43311 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43312 if(this.typeAhead){
43313 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43315 if(this.editable !== false){
43316 this.el.on("keyup", this.onKeyUp, this);
43318 if(this.forceSelection){
43319 this.on('blur', this.doForce, this);
43323 onDestroy : function(){
43325 this.view.setStore(null);
43326 this.view.el.removeAllListeners();
43327 this.view.el.remove();
43328 this.view.purgeListeners();
43331 this.list.destroy();
43334 this.store.un('beforeload', this.onBeforeLoad, this);
43335 this.store.un('load', this.onLoad, this);
43336 this.store.un('loadexception', this.onLoadException, this);
43338 Roo.form.ComboBox.superclass.onDestroy.call(this);
43342 fireKey : function(e){
43343 if(e.isNavKeyPress() && !this.list.isVisible()){
43344 this.fireEvent("specialkey", this, e);
43349 onResize: function(w, h){
43350 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43352 if(typeof w != 'number'){
43353 // we do not handle it!?!?
43356 var tw = this.trigger.getWidth();
43357 tw += this.addicon ? this.addicon.getWidth() : 0;
43358 tw += this.editicon ? this.editicon.getWidth() : 0;
43360 this.el.setWidth( this.adjustWidth('input', x));
43362 this.trigger.setStyle('left', x+'px');
43364 if(this.list && this.listWidth === undefined){
43365 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43366 this.list.setWidth(lw);
43367 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43375 * Allow or prevent the user from directly editing the field text. If false is passed,
43376 * the user will only be able to select from the items defined in the dropdown list. This method
43377 * is the runtime equivalent of setting the 'editable' config option at config time.
43378 * @param {Boolean} value True to allow the user to directly edit the field text
43380 setEditable : function(value){
43381 if(value == this.editable){
43384 this.editable = value;
43386 this.el.dom.setAttribute('readOnly', true);
43387 this.el.on('mousedown', this.onTriggerClick, this);
43388 this.el.addClass('x-combo-noedit');
43390 this.el.dom.setAttribute('readOnly', false);
43391 this.el.un('mousedown', this.onTriggerClick, this);
43392 this.el.removeClass('x-combo-noedit');
43397 onBeforeLoad : function(){
43398 if(!this.hasFocus){
43401 this.innerList.update(this.loadingText ?
43402 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43403 this.restrictHeight();
43404 this.selectedIndex = -1;
43408 onLoad : function(){
43409 if(!this.hasFocus){
43412 if(this.store.getCount() > 0){
43414 this.restrictHeight();
43415 if(this.lastQuery == this.allQuery){
43417 this.el.dom.select();
43419 if(!this.selectByValue(this.value, true)){
43420 this.select(0, true);
43424 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43425 this.taTask.delay(this.typeAheadDelay);
43429 this.onEmptyResults();
43434 onLoadException : function()
43437 Roo.log(this.store.reader.jsonData);
43438 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43439 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43445 onTypeAhead : function(){
43446 if(this.store.getCount() > 0){
43447 var r = this.store.getAt(0);
43448 var newValue = r.data[this.displayField];
43449 var len = newValue.length;
43450 var selStart = this.getRawValue().length;
43451 if(selStart != len){
43452 this.setRawValue(newValue);
43453 this.selectText(selStart, newValue.length);
43459 onSelect : function(record, index){
43460 if(this.fireEvent('beforeselect', this, record, index) !== false){
43461 this.setFromData(index > -1 ? record.data : false);
43463 this.fireEvent('select', this, record, index);
43468 * Returns the currently selected field value or empty string if no value is set.
43469 * @return {String} value The selected value
43471 getValue : function(){
43472 if(this.valueField){
43473 return typeof this.value != 'undefined' ? this.value : '';
43475 return Roo.form.ComboBox.superclass.getValue.call(this);
43479 * Clears any text/value currently set in the field
43481 clearValue : function(){
43482 if(this.hiddenField){
43483 this.hiddenField.value = '';
43486 this.setRawValue('');
43487 this.lastSelectionText = '';
43492 * Sets the specified value into the field. If the value finds a match, the corresponding record text
43493 * will be displayed in the field. If the value does not match the data value of an existing item,
43494 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43495 * Otherwise the field will be blank (although the value will still be set).
43496 * @param {String} value The value to match
43498 setValue : function(v){
43500 if(this.valueField){
43501 var r = this.findRecord(this.valueField, v);
43503 text = r.data[this.displayField];
43504 }else if(this.valueNotFoundText !== undefined){
43505 text = this.valueNotFoundText;
43508 this.lastSelectionText = text;
43509 if(this.hiddenField){
43510 this.hiddenField.value = v;
43512 Roo.form.ComboBox.superclass.setValue.call(this, text);
43516 * @property {Object} the last set data for the element
43521 * Sets the value of the field based on a object which is related to the record format for the store.
43522 * @param {Object} value the value to set as. or false on reset?
43524 setFromData : function(o){
43525 var dv = ''; // display value
43526 var vv = ''; // value value..
43528 if (this.displayField) {
43529 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43531 // this is an error condition!!!
43532 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
43535 if(this.valueField){
43536 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43538 if(this.hiddenField){
43539 this.hiddenField.value = vv;
43541 this.lastSelectionText = dv;
43542 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43546 // no hidden field.. - we store the value in 'value', but still display
43547 // display field!!!!
43548 this.lastSelectionText = dv;
43549 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43555 reset : function(){
43556 // overridden so that last data is reset..
43557 this.setValue(this.resetValue);
43558 this.originalValue = this.getValue();
43559 this.clearInvalid();
43560 this.lastData = false;
43562 this.view.clearSelections();
43566 findRecord : function(prop, value){
43568 if(this.store.getCount() > 0){
43569 this.store.each(function(r){
43570 if(r.data[prop] == value){
43580 getName: function()
43582 // returns hidden if it's set..
43583 if (!this.rendered) {return ''};
43584 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
43588 onViewMove : function(e, t){
43589 this.inKeyMode = false;
43593 onViewOver : function(e, t){
43594 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43597 var item = this.view.findItemFromChild(t);
43599 var index = this.view.indexOf(item);
43600 this.select(index, false);
43605 onViewClick : function(doFocus)
43607 var index = this.view.getSelectedIndexes()[0];
43608 var r = this.store.getAt(index);
43610 this.onSelect(r, index);
43612 if(doFocus !== false && !this.blockFocus){
43618 restrictHeight : function(){
43619 this.innerList.dom.style.height = '';
43620 var inner = this.innerList.dom;
43621 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43622 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43623 this.list.beginUpdate();
43624 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43625 this.list.alignTo(this.el, this.listAlign);
43626 this.list.endUpdate();
43630 onEmptyResults : function(){
43635 * Returns true if the dropdown list is expanded, else false.
43637 isExpanded : function(){
43638 return this.list.isVisible();
43642 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43643 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43644 * @param {String} value The data value of the item to select
43645 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43646 * selected item if it is not currently in view (defaults to true)
43647 * @return {Boolean} True if the value matched an item in the list, else false
43649 selectByValue : function(v, scrollIntoView){
43650 if(v !== undefined && v !== null){
43651 var r = this.findRecord(this.valueField || this.displayField, v);
43653 this.select(this.store.indexOf(r), scrollIntoView);
43661 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43662 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43663 * @param {Number} index The zero-based index of the list item to select
43664 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43665 * selected item if it is not currently in view (defaults to true)
43667 select : function(index, scrollIntoView){
43668 this.selectedIndex = index;
43669 this.view.select(index);
43670 if(scrollIntoView !== false){
43671 var el = this.view.getNode(index);
43673 this.innerList.scrollChildIntoView(el, false);
43679 selectNext : function(){
43680 var ct = this.store.getCount();
43682 if(this.selectedIndex == -1){
43684 }else if(this.selectedIndex < ct-1){
43685 this.select(this.selectedIndex+1);
43691 selectPrev : function(){
43692 var ct = this.store.getCount();
43694 if(this.selectedIndex == -1){
43696 }else if(this.selectedIndex != 0){
43697 this.select(this.selectedIndex-1);
43703 onKeyUp : function(e){
43704 if(this.editable !== false && !e.isSpecialKey()){
43705 this.lastKey = e.getKey();
43706 this.dqTask.delay(this.queryDelay);
43711 validateBlur : function(){
43712 return !this.list || !this.list.isVisible();
43716 initQuery : function(){
43717 this.doQuery(this.getRawValue());
43721 doForce : function(){
43722 if(this.el.dom.value.length > 0){
43723 this.el.dom.value =
43724 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43730 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
43731 * query allowing the query action to be canceled if needed.
43732 * @param {String} query The SQL query to execute
43733 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43734 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
43735 * saved in the current store (defaults to false)
43737 doQuery : function(q, forceAll){
43738 if(q === undefined || q === null){
43743 forceAll: forceAll,
43747 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43751 forceAll = qe.forceAll;
43752 if(forceAll === true || (q.length >= this.minChars)){
43753 if(this.lastQuery != q || this.alwaysQuery){
43754 this.lastQuery = q;
43755 if(this.mode == 'local'){
43756 this.selectedIndex = -1;
43758 this.store.clearFilter();
43760 this.store.filter(this.displayField, q);
43764 this.store.baseParams[this.queryParam] = q;
43766 params: this.getParams(q)
43771 this.selectedIndex = -1;
43778 getParams : function(q){
43780 //p[this.queryParam] = q;
43783 p.limit = this.pageSize;
43789 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43791 collapse : function(){
43792 if(!this.isExpanded()){
43796 Roo.get(document).un('mousedown', this.collapseIf, this);
43797 Roo.get(document).un('mousewheel', this.collapseIf, this);
43798 if (!this.editable) {
43799 Roo.get(document).un('keydown', this.listKeyPress, this);
43801 this.fireEvent('collapse', this);
43805 collapseIf : function(e){
43806 if(!e.within(this.wrap) && !e.within(this.list)){
43812 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43814 expand : function(){
43815 if(this.isExpanded() || !this.hasFocus){
43818 this.list.alignTo(this.el, this.listAlign);
43820 Roo.get(document).on('mousedown', this.collapseIf, this);
43821 Roo.get(document).on('mousewheel', this.collapseIf, this);
43822 if (!this.editable) {
43823 Roo.get(document).on('keydown', this.listKeyPress, this);
43826 this.fireEvent('expand', this);
43830 // Implements the default empty TriggerField.onTriggerClick function
43831 onTriggerClick : function(){
43835 if(this.isExpanded()){
43837 if (!this.blockFocus) {
43842 this.hasFocus = true;
43843 if(this.triggerAction == 'all') {
43844 this.doQuery(this.allQuery, true);
43846 this.doQuery(this.getRawValue());
43848 if (!this.blockFocus) {
43853 listKeyPress : function(e)
43855 //Roo.log('listkeypress');
43856 // scroll to first matching element based on key pres..
43857 if (e.isSpecialKey()) {
43860 var k = String.fromCharCode(e.getKey()).toUpperCase();
43863 var csel = this.view.getSelectedNodes();
43864 var cselitem = false;
43866 var ix = this.view.indexOf(csel[0]);
43867 cselitem = this.store.getAt(ix);
43868 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43874 this.store.each(function(v) {
43876 // start at existing selection.
43877 if (cselitem.id == v.id) {
43883 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43884 match = this.store.indexOf(v);
43889 if (match === false) {
43890 return true; // no more action?
43893 this.view.select(match);
43894 var sn = Roo.get(this.view.getSelectedNodes()[0]);
43895 sn.scrollIntoView(sn.dom.parentNode, false);
43899 * @cfg {Boolean} grow
43903 * @cfg {Number} growMin
43907 * @cfg {Number} growMax
43915 * Copyright(c) 2010-2012, Roo J Solutions Limited
43922 * @class Roo.form.ComboBoxArray
43923 * @extends Roo.form.TextField
43924 * A facebook style adder... for lists of email / people / countries etc...
43925 * pick multiple items from a combo box, and shows each one.
43927 * Fred [x] Brian [x] [Pick another |v]
43930 * For this to work: it needs various extra information
43931 * - normal combo problay has
43933 * + displayField, valueField
43935 * For our purpose...
43938 * If we change from 'extends' to wrapping...
43945 * Create a new ComboBoxArray.
43946 * @param {Object} config Configuration options
43950 Roo.form.ComboBoxArray = function(config)
43954 * @event beforeremove
43955 * Fires before remove the value from the list
43956 * @param {Roo.form.ComboBoxArray} _self This combo box array
43957 * @param {Roo.form.ComboBoxArray.Item} item removed item
43959 'beforeremove' : true,
43962 * Fires when remove the value from the list
43963 * @param {Roo.form.ComboBoxArray} _self This combo box array
43964 * @param {Roo.form.ComboBoxArray.Item} item removed item
43971 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43973 this.items = new Roo.util.MixedCollection(false);
43975 // construct the child combo...
43985 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43988 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43993 // behavies liek a hiddne field
43994 inputType: 'hidden',
43996 * @cfg {Number} width The width of the box that displays the selected element
44003 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
44007 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
44009 hiddenName : false,
44011 * @cfg {String} seperator The value seperator normally ','
44015 // private the array of items that are displayed..
44017 // private - the hidden field el.
44019 // private - the filed el..
44022 //validateValue : function() { return true; }, // all values are ok!
44023 //onAddClick: function() { },
44025 onRender : function(ct, position)
44028 // create the standard hidden element
44029 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44032 // give fake names to child combo;
44033 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44034 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44036 this.combo = Roo.factory(this.combo, Roo.form);
44037 this.combo.onRender(ct, position);
44038 if (typeof(this.combo.width) != 'undefined') {
44039 this.combo.onResize(this.combo.width,0);
44042 this.combo.initEvents();
44044 // assigned so form know we need to do this..
44045 this.store = this.combo.store;
44046 this.valueField = this.combo.valueField;
44047 this.displayField = this.combo.displayField ;
44050 this.combo.wrap.addClass('x-cbarray-grp');
44052 var cbwrap = this.combo.wrap.createChild(
44053 {tag: 'div', cls: 'x-cbarray-cb'},
44058 this.hiddenEl = this.combo.wrap.createChild({
44059 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
44061 this.el = this.combo.wrap.createChild({
44062 tag: 'input', type:'hidden' , name: this.name, value : ''
44064 // this.el.dom.removeAttribute("name");
44067 this.outerWrap = this.combo.wrap;
44068 this.wrap = cbwrap;
44070 this.outerWrap.setWidth(this.width);
44071 this.outerWrap.dom.removeChild(this.el.dom);
44073 this.wrap.dom.appendChild(this.el.dom);
44074 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44075 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44077 this.combo.trigger.setStyle('position','relative');
44078 this.combo.trigger.setStyle('left', '0px');
44079 this.combo.trigger.setStyle('top', '2px');
44081 this.combo.el.setStyle('vertical-align', 'text-bottom');
44083 //this.trigger.setStyle('vertical-align', 'top');
44085 // this should use the code from combo really... on('add' ....)
44089 this.adder = this.outerWrap.createChild(
44090 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
44092 this.adder.on('click', function(e) {
44093 _t.fireEvent('adderclick', this, e);
44097 //this.adder.on('click', this.onAddClick, _t);
44100 this.combo.on('select', function(cb, rec, ix) {
44101 this.addItem(rec.data);
44104 cb.el.dom.value = '';
44105 //cb.lastData = rec.data;
44114 getName: function()
44116 // returns hidden if it's set..
44117 if (!this.rendered) {return ''};
44118 return this.hiddenName ? this.hiddenName : this.name;
44123 onResize: function(w, h){
44126 // not sure if this is needed..
44127 //this.combo.onResize(w,h);
44129 if(typeof w != 'number'){
44130 // we do not handle it!?!?
44133 var tw = this.combo.trigger.getWidth();
44134 tw += this.addicon ? this.addicon.getWidth() : 0;
44135 tw += this.editicon ? this.editicon.getWidth() : 0;
44137 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44139 this.combo.trigger.setStyle('left', '0px');
44141 if(this.list && this.listWidth === undefined){
44142 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44143 this.list.setWidth(lw);
44144 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44151 addItem: function(rec)
44153 var valueField = this.combo.valueField;
44154 var displayField = this.combo.displayField;
44156 if (this.items.indexOfKey(rec[valueField]) > -1) {
44157 //console.log("GOT " + rec.data.id);
44161 var x = new Roo.form.ComboBoxArray.Item({
44162 //id : rec[this.idField],
44164 displayField : displayField ,
44165 tipField : displayField ,
44169 this.items.add(rec[valueField],x);
44170 // add it before the element..
44171 this.updateHiddenEl();
44172 x.render(this.outerWrap, this.wrap.dom);
44173 // add the image handler..
44176 updateHiddenEl : function()
44179 if (!this.hiddenEl) {
44183 var idField = this.combo.valueField;
44185 this.items.each(function(f) {
44186 ar.push(f.data[idField]);
44188 this.hiddenEl.dom.value = ar.join(this.seperator);
44194 this.items.clear();
44196 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44200 this.el.dom.value = '';
44201 if (this.hiddenEl) {
44202 this.hiddenEl.dom.value = '';
44206 getValue: function()
44208 return this.hiddenEl ? this.hiddenEl.dom.value : '';
44210 setValue: function(v) // not a valid action - must use addItems..
44215 if (this.store.isLocal && (typeof(v) == 'string')) {
44216 // then we can use the store to find the values..
44217 // comma seperated at present.. this needs to allow JSON based encoding..
44218 this.hiddenEl.value = v;
44220 Roo.each(v.split(this.seperator), function(k) {
44221 Roo.log("CHECK " + this.valueField + ',' + k);
44222 var li = this.store.query(this.valueField, k);
44227 add[this.valueField] = k;
44228 add[this.displayField] = li.item(0).data[this.displayField];
44234 if (typeof(v) == 'object' ) {
44235 // then let's assume it's an array of objects..
44236 Roo.each(v, function(l) {
44238 if (typeof(l) == 'string') {
44240 add[this.valueField] = l;
44241 add[this.displayField] = l
44250 setFromData: function(v)
44252 // this recieves an object, if setValues is called.
44254 this.el.dom.value = v[this.displayField];
44255 this.hiddenEl.dom.value = v[this.valueField];
44256 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44259 var kv = v[this.valueField];
44260 var dv = v[this.displayField];
44261 kv = typeof(kv) != 'string' ? '' : kv;
44262 dv = typeof(dv) != 'string' ? '' : dv;
44265 var keys = kv.split(this.seperator);
44266 var display = dv.split(this.seperator);
44267 for (var i = 0 ; i < keys.length; i++) {
44269 add[this.valueField] = keys[i];
44270 add[this.displayField] = display[i];
44278 * Validates the combox array value
44279 * @return {Boolean} True if the value is valid, else false
44281 validate : function(){
44282 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44283 this.clearInvalid();
44289 validateValue : function(value){
44290 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44298 isDirty : function() {
44299 if(this.disabled) {
44304 var d = Roo.decode(String(this.originalValue));
44306 return String(this.getValue()) !== String(this.originalValue);
44309 var originalValue = [];
44311 for (var i = 0; i < d.length; i++){
44312 originalValue.push(d[i][this.valueField]);
44315 return String(this.getValue()) !== String(originalValue.join(this.seperator));
44324 * @class Roo.form.ComboBoxArray.Item
44325 * @extends Roo.BoxComponent
44326 * A selected item in the list
44327 * Fred [x] Brian [x] [Pick another |v]
44330 * Create a new item.
44331 * @param {Object} config Configuration options
44334 Roo.form.ComboBoxArray.Item = function(config) {
44335 config.id = Roo.id();
44336 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44339 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44342 displayField : false,
44346 defaultAutoCreate : {
44348 cls: 'x-cbarray-item',
44355 src : Roo.BLANK_IMAGE_URL ,
44363 onRender : function(ct, position)
44365 Roo.form.Field.superclass.onRender.call(this, ct, position);
44368 var cfg = this.getAutoCreate();
44369 this.el = ct.createChild(cfg, position);
44372 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44374 this.el.child('div').dom.innerHTML = this.cb.renderer ?
44375 this.cb.renderer(this.data) :
44376 String.format('{0}',this.data[this.displayField]);
44379 this.el.child('div').dom.setAttribute('qtip',
44380 String.format('{0}',this.data[this.tipField])
44383 this.el.child('img').on('click', this.remove, this);
44387 remove : function()
44389 if(this.cb.disabled){
44393 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44394 this.cb.items.remove(this);
44395 this.el.child('img').un('click', this.remove, this);
44397 this.cb.updateHiddenEl();
44399 this.cb.fireEvent('remove', this.cb, this);
44404 * RooJS Library 1.1.1
44405 * Copyright(c) 2008-2011 Alan Knowles
44412 * @class Roo.form.ComboNested
44413 * @extends Roo.form.ComboBox
44414 * A combobox for that allows selection of nested items in a list,
44429 * Create a new ComboNested
44430 * @param {Object} config Configuration options
44432 Roo.form.ComboNested = function(config){
44433 Roo.form.ComboCheck.superclass.constructor.call(this, config);
44434 // should verify some data...
44436 // hiddenName = required..
44437 // displayField = required
44438 // valudField == required
44439 var req= [ 'hiddenName', 'displayField', 'valueField' ];
44441 Roo.each(req, function(e) {
44442 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44443 throw "Roo.form.ComboNested : missing value for: " + e;
44450 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44453 * @config {Number} max Number of columns to show
44458 list : null, // the outermost div..
44459 innerLists : null, // the
44463 loadingChildren : false,
44465 onRender : function(ct, position)
44467 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44469 if(this.hiddenName){
44470 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
44472 this.hiddenField.value =
44473 this.hiddenValue !== undefined ? this.hiddenValue :
44474 this.value !== undefined ? this.value : '';
44476 // prevent input submission
44477 this.el.dom.removeAttribute('name');
44483 this.el.dom.setAttribute('autocomplete', 'off');
44486 var cls = 'x-combo-list';
44488 this.list = new Roo.Layer({
44489 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44492 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44493 this.list.setWidth(lw);
44494 this.list.swallowEvent('mousewheel');
44495 this.assetHeight = 0;
44498 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44499 this.assetHeight += this.header.getHeight();
44501 this.innerLists = [];
44504 for (var i =0 ; i < this.maxColumns; i++) {
44505 this.onRenderList( cls, i);
44508 // always needs footer, as we are going to have an 'OK' button.
44509 this.footer = this.list.createChild({cls:cls+'-ft'});
44510 this.pageTb = new Roo.Toolbar(this.footer);
44515 handler: function()
44521 if ( this.allowBlank && !this.disableClear) {
44523 this.pageTb.add(new Roo.Toolbar.Fill(), {
44524 cls: 'x-btn-icon x-btn-clear',
44526 handler: function()
44529 _this.clearValue();
44530 _this.onSelect(false, -1);
44535 this.assetHeight += this.footer.getHeight();
44539 onRenderList : function ( cls, i)
44542 var lw = Math.floor(
44543 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44546 this.list.setWidth(lw); // default to '1'
44548 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44549 //il.on('mouseover', this.onViewOver, this, { list: i });
44550 //il.on('mousemove', this.onViewMove, this, { list: i });
44552 il.setStyle({ 'overflow-x' : 'hidden'});
44555 this.tpl = new Roo.Template({
44556 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44557 isEmpty: function (value, allValues) {
44559 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44560 return dl ? 'has-children' : 'no-children'
44565 var store = this.store;
44567 store = new Roo.data.SimpleStore({
44568 //fields : this.store.reader.meta.fields,
44569 reader : this.store.reader,
44573 this.stores[i] = store;
44575 var view = this.views[i] = new Roo.View(
44581 selectedClass: this.selectedClass
44584 view.getEl().setWidth(lw);
44585 view.getEl().setStyle({
44586 position: i < 1 ? 'relative' : 'absolute',
44588 left: (i * lw ) + 'px',
44589 display : i > 0 ? 'none' : 'block'
44591 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44592 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44593 //view.on('click', this.onViewClick, this, { list : i });
44595 store.on('beforeload', this.onBeforeLoad, this);
44596 store.on('load', this.onLoad, this, { list : i});
44597 store.on('loadexception', this.onLoadException, this);
44599 // hide the other vies..
44605 restrictHeight : function()
44608 Roo.each(this.innerLists, function(il,i) {
44609 var el = this.views[i].getEl();
44610 el.dom.style.height = '';
44611 var inner = el.dom;
44612 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44613 // only adjust heights on other ones..
44614 mh = Math.max(h, mh);
44617 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44618 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44625 this.list.beginUpdate();
44626 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44627 this.list.alignTo(this.el, this.listAlign);
44628 this.list.endUpdate();
44633 // -- store handlers..
44635 onBeforeLoad : function()
44637 if(!this.hasFocus){
44640 this.innerLists[0].update(this.loadingText ?
44641 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44642 this.restrictHeight();
44643 this.selectedIndex = -1;
44646 onLoad : function(a,b,c,d)
44648 if (!this.loadingChildren) {
44649 // then we are loading the top level. - hide the children
44650 for (var i = 1;i < this.views.length; i++) {
44651 this.views[i].getEl().setStyle({ display : 'none' });
44653 var lw = Math.floor(
44654 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44657 this.list.setWidth(lw); // default to '1'
44661 if(!this.hasFocus){
44665 if(this.store.getCount() > 0) {
44667 this.restrictHeight();
44669 this.onEmptyResults();
44672 if (!this.loadingChildren) {
44673 this.selectActive();
44676 this.stores[1].loadData([]);
44677 this.stores[2].loadData([]);
44686 onLoadException : function()
44689 Roo.log(this.store.reader.jsonData);
44690 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44691 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44696 // no cleaning of leading spaces on blur here.
44697 cleanLeadingSpace : function(e) { },
44700 onSelectChange : function (view, sels, opts )
44702 var ix = view.getSelectedIndexes();
44704 if (opts.list > this.maxColumns - 2) {
44705 if (view.store.getCount()< 1) {
44706 this.views[opts.list ].getEl().setStyle({ display : 'none' });
44710 // used to clear ?? but if we are loading unselected
44711 this.setFromData(view.store.getAt(ix[0]).data);
44720 // this get's fired when trigger opens..
44721 // this.setFromData({});
44722 var str = this.stores[opts.list+1];
44723 str.data.clear(); // removeall wihtout the fire events..
44727 var rec = view.store.getAt(ix[0]);
44729 this.setFromData(rec.data);
44730 this.fireEvent('select', this, rec, ix[0]);
44732 var lw = Math.floor(
44734 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44735 ) / this.maxColumns
44737 this.loadingChildren = true;
44738 this.stores[opts.list+1].loadDataFromChildren( rec );
44739 this.loadingChildren = false;
44740 var dl = this.stores[opts.list+1]. getTotalCount();
44742 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44744 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44745 for (var i = opts.list+2; i < this.views.length;i++) {
44746 this.views[i].getEl().setStyle({ display : 'none' });
44749 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44750 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44752 if (this.isLoading) {
44753 // this.selectActive(opts.list);
44761 onDoubleClick : function()
44763 this.collapse(); //??
44771 recordToStack : function(store, prop, value, stack)
44773 var cstore = new Roo.data.SimpleStore({
44774 //fields : this.store.reader.meta.fields, // we need array reader.. for
44775 reader : this.store.reader,
44779 var record = false;
44781 if(store.getCount() < 1){
44784 store.each(function(r){
44785 if(r.data[prop] == value){
44790 if (r.data.cn && r.data.cn.length) {
44791 cstore.loadDataFromChildren( r);
44792 var cret = _this.recordToStack(cstore, prop, value, stack);
44793 if (cret !== false) {
44802 if (record == false) {
44805 stack.unshift(srec);
44810 * find the stack of stores that match our value.
44815 selectActive : function ()
44817 // if store is not loaded, then we will need to wait for that to happen first.
44819 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44820 for (var i = 0; i < stack.length; i++ ) {
44821 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44833 * Ext JS Library 1.1.1
44834 * Copyright(c) 2006-2007, Ext JS, LLC.
44836 * Originally Released Under LGPL - original licence link has changed is not relivant.
44839 * <script type="text/javascript">
44842 * @class Roo.form.Checkbox
44843 * @extends Roo.form.Field
44844 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
44846 * Creates a new Checkbox
44847 * @param {Object} config Configuration options
44849 Roo.form.Checkbox = function(config){
44850 Roo.form.Checkbox.superclass.constructor.call(this, config);
44854 * Fires when the checkbox is checked or unchecked.
44855 * @param {Roo.form.Checkbox} this This checkbox
44856 * @param {Boolean} checked The new checked value
44862 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
44864 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44866 focusClass : undefined,
44868 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44870 fieldClass: "x-form-field",
44872 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44876 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44877 * {tag: "input", type: "checkbox", autocomplete: "off"})
44879 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44881 * @cfg {String} boxLabel The text that appears beside the checkbox
44885 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44889 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44891 valueOff: '0', // value when not checked..
44893 actionMode : 'viewEl',
44896 itemCls : 'x-menu-check-item x-form-item',
44897 groupClass : 'x-menu-group-item',
44898 inputType : 'hidden',
44901 inSetChecked: false, // check that we are not calling self...
44903 inputElement: false, // real input element?
44904 basedOn: false, // ????
44906 isFormField: true, // not sure where this is needed!!!!
44908 onResize : function(){
44909 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44910 if(!this.boxLabel){
44911 this.el.alignTo(this.wrap, 'c-c');
44915 initEvents : function(){
44916 Roo.form.Checkbox.superclass.initEvents.call(this);
44917 this.el.on("click", this.onClick, this);
44918 this.el.on("change", this.onClick, this);
44922 getResizeEl : function(){
44926 getPositionEl : function(){
44931 onRender : function(ct, position){
44932 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44934 if(this.inputValue !== undefined){
44935 this.el.dom.value = this.inputValue;
44938 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44939 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44940 var viewEl = this.wrap.createChild({
44941 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44942 this.viewEl = viewEl;
44943 this.wrap.on('click', this.onClick, this);
44945 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
44946 this.el.on('propertychange', this.setFromHidden, this); //ie
44951 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44952 // viewEl.on('click', this.onClick, this);
44954 //if(this.checked){
44955 this.setChecked(this.checked);
44957 //this.checked = this.el.dom;
44963 initValue : Roo.emptyFn,
44966 * Returns the checked state of the checkbox.
44967 * @return {Boolean} True if checked, else false
44969 getValue : function(){
44971 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44973 return this.valueOff;
44978 onClick : function(){
44979 if (this.disabled) {
44982 this.setChecked(!this.checked);
44984 //if(this.el.dom.checked != this.checked){
44985 // this.setValue(this.el.dom.checked);
44990 * Sets the checked state of the checkbox.
44991 * On is always based on a string comparison between inputValue and the param.
44992 * @param {Boolean/String} value - the value to set
44993 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44995 setValue : function(v,suppressEvent){
44998 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44999 //if(this.el && this.el.dom){
45000 // this.el.dom.checked = this.checked;
45001 // this.el.dom.defaultChecked = this.checked;
45003 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45004 //this.fireEvent("check", this, this.checked);
45007 setChecked : function(state,suppressEvent)
45009 if (this.inSetChecked) {
45010 this.checked = state;
45016 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45018 this.checked = state;
45019 if(suppressEvent !== true){
45020 this.fireEvent('check', this, state);
45022 this.inSetChecked = true;
45023 this.el.dom.value = state ? this.inputValue : this.valueOff;
45024 this.inSetChecked = false;
45027 // handle setting of hidden value by some other method!!?!?
45028 setFromHidden: function()
45033 //console.log("SET FROM HIDDEN");
45034 //alert('setFrom hidden');
45035 this.setValue(this.el.dom.value);
45038 onDestroy : function()
45041 Roo.get(this.viewEl).remove();
45044 Roo.form.Checkbox.superclass.onDestroy.call(this);
45047 setBoxLabel : function(str)
45049 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45054 * Ext JS Library 1.1.1
45055 * Copyright(c) 2006-2007, Ext JS, LLC.
45057 * Originally Released Under LGPL - original licence link has changed is not relivant.
45060 * <script type="text/javascript">
45064 * @class Roo.form.Radio
45065 * @extends Roo.form.Checkbox
45066 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
45067 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45069 * Creates a new Radio
45070 * @param {Object} config Configuration options
45072 Roo.form.Radio = function(){
45073 Roo.form.Radio.superclass.constructor.apply(this, arguments);
45075 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45076 inputType: 'radio',
45079 * If this radio is part of a group, it will return the selected value
45082 getGroupValue : function(){
45083 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45087 onRender : function(ct, position){
45088 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45090 if(this.inputValue !== undefined){
45091 this.el.dom.value = this.inputValue;
45094 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45095 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45096 //var viewEl = this.wrap.createChild({
45097 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45098 //this.viewEl = viewEl;
45099 //this.wrap.on('click', this.onClick, this);
45101 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
45102 //this.el.on('propertychange', this.setFromHidden, this); //ie
45107 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45108 // viewEl.on('click', this.onClick, this);
45111 this.el.dom.checked = 'checked' ;
45117 });Roo.rtf = {}; // namespace
45118 Roo.rtf.Hex = function(hex)
45122 Roo.rtf.Paragraph = function(opts)
45124 this.content = []; ///??? is that used?
45125 };Roo.rtf.Span = function(opts)
45127 this.value = opts.value;
45130 Roo.rtf.Group = function(parent)
45132 // we dont want to acutally store parent - it will make debug a nightmare..
45140 Roo.rtf.Group.prototype = {
45144 addContent : function(node) {
45145 // could set styles...
45146 this.content.push(node);
45148 addChild : function(cn)
45152 // only for images really...
45153 toDataURL : function()
45155 var mimetype = false;
45157 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
45158 mimetype = "image/png";
45160 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45161 mimetype = "image/jpeg";
45164 return 'about:blank'; // ?? error?
45168 var hexstring = this.content[this.content.length-1].value;
45170 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45171 return String.fromCharCode(parseInt(a, 16));
45176 // this looks like it's normally the {rtf{ .... }}
45177 Roo.rtf.Document = function()
45179 // we dont want to acutally store parent - it will make debug a nightmare..
45185 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
45186 addChild : function(cn)
45190 case 'rtlch': // most content seems to be inside this??
45193 this.rtlch.push(cn);
45196 this[cn.type] = cn;
45201 getElementsByType : function(type)
45204 this._getElementsByType(type, ret, this.cn, 'rtf');
45207 _getElementsByType : function (type, ret, search_array, path)
45209 search_array.forEach(function(n,i) {
45210 if (n.type == type) {
45211 n.path = path + '/' + n.type + ':' + i;
45214 if (n.cn.length > 0) {
45215 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45222 Roo.rtf.Ctrl = function(opts)
45224 this.value = opts.value;
45225 this.param = opts.param;
45230 * based on this https://github.com/iarna/rtf-parser
45231 * it's really only designed to extract pict from pasted RTF
45235 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45244 Roo.rtf.Parser = function(text) {
45245 //super({objectMode: true})
45247 this.parserState = this.parseText;
45249 // these are for interpeter...
45251 ///this.parserState = this.parseTop
45252 this.groupStack = [];
45253 this.hexStore = [];
45256 this.groups = []; // where we put the return.
45258 for (var ii = 0; ii < text.length; ++ii) {
45261 if (text[ii] === '\n') {
45267 this.parserState(text[ii]);
45273 Roo.rtf.Parser.prototype = {
45274 text : '', // string being parsed..
45276 controlWordParam : '',
45280 groupStack : false,
45285 row : 1, // reportin?
45289 push : function (el)
45291 var m = 'cmd'+ el.type;
45292 if (typeof(this[m]) == 'undefined') {
45293 Roo.log('invalid cmd:' + el.type);
45299 flushHexStore : function()
45301 if (this.hexStore.length < 1) {
45304 var hexstr = this.hexStore.map(
45309 this.group.addContent( new Roo.rtf.Hex( hexstr ));
45312 this.hexStore.splice(0)
45316 cmdgroupstart : function()
45318 this.flushHexStore();
45320 this.groupStack.push(this.group);
45323 if (this.doc === false) {
45324 this.group = this.doc = new Roo.rtf.Document();
45328 this.group = new Roo.rtf.Group(this.group);
45330 cmdignorable : function()
45332 this.flushHexStore();
45333 this.group.ignorable = true;
45335 cmdendparagraph : function()
45337 this.flushHexStore();
45338 this.group.addContent(new Roo.rtf.Paragraph());
45340 cmdgroupend : function ()
45342 this.flushHexStore();
45343 var endingGroup = this.group;
45346 this.group = this.groupStack.pop();
45348 this.group.addChild(endingGroup);
45353 var doc = this.group || this.doc;
45354 //if (endingGroup instanceof FontTable) {
45355 // doc.fonts = endingGroup.table
45356 //} else if (endingGroup instanceof ColorTable) {
45357 // doc.colors = endingGroup.table
45358 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45359 if (endingGroup.ignorable === false) {
45361 this.groups.push(endingGroup);
45362 // Roo.log( endingGroup );
45364 //Roo.each(endingGroup.content, function(item)) {
45365 // doc.addContent(item);
45367 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45370 cmdtext : function (cmd)
45372 this.flushHexStore();
45373 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45374 //this.group = this.doc
45375 return; // we really don't care about stray text...
45377 this.group.addContent(new Roo.rtf.Span(cmd));
45379 cmdcontrolword : function (cmd)
45381 this.flushHexStore();
45382 if (!this.group.type) {
45383 this.group.type = cmd.value;
45386 this.group.addContent(new Roo.rtf.Ctrl(cmd));
45387 // we actually don't care about ctrl words...
45390 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45391 if (this[method]) {
45392 this[method](cmd.param)
45394 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45398 cmdhexchar : function(cmd) {
45399 this.hexStore.push(cmd);
45401 cmderror : function(cmd) {
45407 if (this.text !== '\u0000') this.emitText()
45413 parseText : function(c)
45416 this.parserState = this.parseEscapes;
45417 } else if (c === '{') {
45418 this.emitStartGroup();
45419 } else if (c === '}') {
45420 this.emitEndGroup();
45421 } else if (c === '\x0A' || c === '\x0D') {
45422 // cr/lf are noise chars
45428 parseEscapes: function (c)
45430 if (c === '\\' || c === '{' || c === '}') {
45432 this.parserState = this.parseText;
45434 this.parserState = this.parseControlSymbol;
45435 this.parseControlSymbol(c);
45438 parseControlSymbol: function(c)
45441 this.text += '\u00a0'; // nbsp
45442 this.parserState = this.parseText
45443 } else if (c === '-') {
45444 this.text += '\u00ad'; // soft hyphen
45445 } else if (c === '_') {
45446 this.text += '\u2011'; // non-breaking hyphen
45447 } else if (c === '*') {
45448 this.emitIgnorable();
45449 this.parserState = this.parseText;
45450 } else if (c === "'") {
45451 this.parserState = this.parseHexChar;
45452 } else if (c === '|') { // formula cacter
45453 this.emitFormula();
45454 this.parserState = this.parseText;
45455 } else if (c === ':') { // subentry in an index entry
45456 this.emitIndexSubEntry();
45457 this.parserState = this.parseText;
45458 } else if (c === '\x0a') {
45459 this.emitEndParagraph();
45460 this.parserState = this.parseText;
45461 } else if (c === '\x0d') {
45462 this.emitEndParagraph();
45463 this.parserState = this.parseText;
45465 this.parserState = this.parseControlWord;
45466 this.parseControlWord(c);
45469 parseHexChar: function (c)
45471 if (/^[A-Fa-f0-9]$/.test(c)) {
45473 if (this.hexChar.length >= 2) {
45474 this.emitHexChar();
45475 this.parserState = this.parseText;
45479 this.emitError("Invalid character \"" + c + "\" in hex literal.");
45480 this.parserState = this.parseText;
45483 parseControlWord : function(c)
45486 this.emitControlWord();
45487 this.parserState = this.parseText;
45488 } else if (/^[-\d]$/.test(c)) {
45489 this.parserState = this.parseControlWordParam;
45490 this.controlWordParam += c;
45491 } else if (/^[A-Za-z]$/.test(c)) {
45492 this.controlWord += c;
45494 this.emitControlWord();
45495 this.parserState = this.parseText;
45499 parseControlWordParam : function (c) {
45500 if (/^\d$/.test(c)) {
45501 this.controlWordParam += c;
45502 } else if (c === ' ') {
45503 this.emitControlWord();
45504 this.parserState = this.parseText;
45506 this.emitControlWord();
45507 this.parserState = this.parseText;
45515 emitText : function () {
45516 if (this.text === '') {
45528 emitControlWord : function ()
45531 if (this.controlWord === '') {
45532 // do we want to track this - it seems just to cause problems.
45533 //this.emitError('empty control word');
45536 type: 'controlword',
45537 value: this.controlWord,
45538 param: this.controlWordParam !== '' && Number(this.controlWordParam),
45544 this.controlWord = '';
45545 this.controlWordParam = '';
45547 emitStartGroup : function ()
45551 type: 'groupstart',
45557 emitEndGroup : function ()
45567 emitIgnorable : function ()
45577 emitHexChar : function ()
45582 value: this.hexChar,
45589 emitError : function (message)
45597 char: this.cpos //,
45598 //stack: new Error().stack
45601 emitEndParagraph : function () {
45604 type: 'endparagraph',
45612 Roo.htmleditor = {};
45615 * @class Roo.htmleditor.Filter
45616 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45617 * @cfg {DomElement} node The node to iterate and filter
45618 * @cfg {boolean|String|Array} tag Tags to replace
45620 * Create a new Filter.
45621 * @param {Object} config Configuration options
45626 Roo.htmleditor.Filter = function(cfg) {
45627 Roo.apply(this.cfg);
45628 // this does not actually call walk as it's really just a abstract class
45632 Roo.htmleditor.Filter.prototype = {
45638 // overrride to do replace comments.
45639 replaceComment : false,
45641 // overrride to do replace or do stuff with tags..
45642 replaceTag : false,
45644 walk : function(dom)
45646 Roo.each( Array.from(dom.childNodes), function( e ) {
45649 case e.nodeType == 8 && this.replaceComment !== false: // comment
45650 this.replaceComment(e);
45653 case e.nodeType != 1: //not a node.
45656 case this.tag === true: // everything
45657 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45658 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45659 if (this.replaceTag && false === this.replaceTag(e)) {
45662 if (e.hasChildNodes()) {
45667 default: // tags .. that do not match.
45668 if (e.hasChildNodes()) {
45679 * @class Roo.htmleditor.FilterAttributes
45680 * clean attributes and styles including http:// etc.. in attribute
45682 * Run a new Attribute Filter
45683 * @param {Object} config Configuration options
45685 Roo.htmleditor.FilterAttributes = function(cfg)
45687 Roo.apply(this, cfg);
45688 this.attrib_black = this.attrib_black || [];
45689 this.attrib_white = this.attrib_white || [];
45691 this.attrib_clean = this.attrib_clean || [];
45692 this.style_white = this.style_white || [];
45693 this.style_black = this.style_black || [];
45694 this.walk(cfg.node);
45697 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45699 tag: true, // all tags
45701 attrib_black : false, // array
45702 attrib_clean : false,
45703 attrib_white : false,
45705 style_white : false,
45706 style_black : false,
45709 replaceTag : function(node)
45711 if (!node.attributes || !node.attributes.length) {
45715 for (var i = node.attributes.length-1; i > -1 ; i--) {
45716 var a = node.attributes[i];
45718 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45719 node.removeAttribute(a.name);
45725 if (a.name.toLowerCase().substr(0,2)=='on') {
45726 node.removeAttribute(a.name);
45731 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45732 node.removeAttribute(a.name);
45735 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45736 this.cleanAttr(node,a.name,a.value); // fixme..
45739 if (a.name == 'style') {
45740 this.cleanStyle(node,a.name,a.value);
45743 /// clean up MS crap..
45744 // tecnically this should be a list of valid class'es..
45747 if (a.name == 'class') {
45748 if (a.value.match(/^Mso/)) {
45749 node.removeAttribute('class');
45752 if (a.value.match(/^body$/)) {
45753 node.removeAttribute('class');
45763 return true; // clean children
45766 cleanAttr: function(node, n,v)
45769 if (v.match(/^\./) || v.match(/^\//)) {
45772 if (v.match(/^(http|https):\/\//)
45773 || v.match(/^mailto:/)
45774 || v.match(/^ftp:/)
45775 || v.match(/^data:/)
45779 if (v.match(/^#/)) {
45782 if (v.match(/^\{/)) { // allow template editing.
45785 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45786 node.removeAttribute(n);
45789 cleanStyle : function(node, n,v)
45791 if (v.match(/expression/)) { //XSS?? should we even bother..
45792 node.removeAttribute(n);
45796 var parts = v.split(/;/);
45799 Roo.each(parts, function(p) {
45800 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45804 var l = p.split(':').shift().replace(/\s+/g,'');
45805 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45807 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45811 // only allow 'c whitelisted system attributes'
45812 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45820 if (clean.length) {
45821 node.setAttribute(n, clean.join(';'));
45823 node.removeAttribute(n);
45832 * @class Roo.htmleditor.FilterBlack
45833 * remove blacklisted elements.
45835 * Run a new Blacklisted Filter
45836 * @param {Object} config Configuration options
45839 Roo.htmleditor.FilterBlack = function(cfg)
45841 Roo.apply(this, cfg);
45842 this.walk(cfg.node);
45845 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45847 tag : true, // all elements.
45849 replaceTag : function(n)
45851 n.parentNode.removeChild(n);
45855 * @class Roo.htmleditor.FilterComment
45858 * Run a new Comments Filter
45859 * @param {Object} config Configuration options
45861 Roo.htmleditor.FilterComment = function(cfg)
45863 this.walk(cfg.node);
45866 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45869 replaceComment : function(n)
45871 n.parentNode.removeChild(n);
45874 * @class Roo.htmleditor.FilterKeepChildren
45875 * remove tags but keep children
45877 * Run a new Keep Children Filter
45878 * @param {Object} config Configuration options
45881 Roo.htmleditor.FilterKeepChildren = function(cfg)
45883 Roo.apply(this, cfg);
45884 if (this.tag === false) {
45885 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45887 this.walk(cfg.node);
45890 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45894 replaceTag : function(node)
45896 // walk children...
45898 var ar = Array.from(node.childNodes);
45900 for (var i = 0; i < ar.length; i++) {
45901 if (ar[i].nodeType == 1) {
45903 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45904 || // array and it matches
45905 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45907 this.replaceTag(ar[i]); // child is blacklisted as well...
45912 ar = Array.from(node.childNodes);
45913 for (var i = 0; i < ar.length; i++) {
45915 node.removeChild(ar[i]);
45916 // what if we need to walk these???
45917 node.parentNode.insertBefore(ar[i], node);
45918 if (this.tag !== false) {
45923 node.parentNode.removeChild(node);
45924 return false; // don't walk children
45929 * @class Roo.htmleditor.FilterParagraph
45930 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45931 * like on 'push' to remove the <p> tags and replace them with line breaks.
45933 * Run a new Paragraph Filter
45934 * @param {Object} config Configuration options
45937 Roo.htmleditor.FilterParagraph = function(cfg)
45939 // no need to apply config.
45940 this.walk(cfg.node);
45943 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45950 replaceTag : function(node)
45953 if (node.childNodes.length == 1 &&
45954 node.childNodes[0].nodeType == 3 &&
45955 node.childNodes[0].textContent.trim().length < 1
45957 // remove and replace with '<BR>';
45958 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45959 return false; // no need to walk..
45961 var ar = Array.from(node.childNodes);
45962 for (var i = 0; i < ar.length; i++) {
45963 node.removeChild(ar[i]);
45964 // what if we need to walk these???
45965 node.parentNode.insertBefore(ar[i], node);
45967 // now what about this?
45971 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45972 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45973 node.parentNode.removeChild(node);
45980 * @class Roo.htmleditor.FilterSpan
45981 * filter span's with no attributes out..
45983 * Run a new Span Filter
45984 * @param {Object} config Configuration options
45987 Roo.htmleditor.FilterSpan = function(cfg)
45989 // no need to apply config.
45990 this.walk(cfg.node);
45993 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45999 replaceTag : function(node)
46001 if (node.attributes && node.attributes.length > 0) {
46002 return true; // walk if there are any.
46004 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
46010 * @class Roo.htmleditor.FilterTableWidth
46011 try and remove table width data - as that frequently messes up other stuff.
46013 * was cleanTableWidths.
46015 * Quite often pasting from word etc.. results in tables with column and widths.
46016 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
46019 * Run a new Table Filter
46020 * @param {Object} config Configuration options
46023 Roo.htmleditor.FilterTableWidth = function(cfg)
46025 // no need to apply config.
46026 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46027 this.walk(cfg.node);
46030 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46035 replaceTag: function(node) {
46039 if (node.hasAttribute('width')) {
46040 node.removeAttribute('width');
46044 if (node.hasAttribute("style")) {
46047 var styles = node.getAttribute("style").split(";");
46049 Roo.each(styles, function(s) {
46050 if (!s.match(/:/)) {
46053 var kv = s.split(":");
46054 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46057 // what ever is left... we allow.
46060 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46061 if (!nstyle.length) {
46062 node.removeAttribute('style');
46066 return true; // continue doing children..
46069 * @class Roo.htmleditor.FilterWord
46070 * try and clean up all the mess that Word generates.
46072 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
46075 * Run a new Span Filter
46076 * @param {Object} config Configuration options
46079 Roo.htmleditor.FilterWord = function(cfg)
46081 // no need to apply config.
46082 this.replaceDocBullets(cfg.node);
46084 // this.walk(cfg.node);
46089 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46095 * Clean up MS wordisms...
46097 replaceTag : function(node)
46100 // no idea what this does - span with text, replaceds with just text.
46102 node.nodeName == 'SPAN' &&
46103 !node.hasAttributes() &&
46104 node.childNodes.length == 1 &&
46105 node.firstChild.nodeName == "#text"
46107 var textNode = node.firstChild;
46108 node.removeChild(textNode);
46109 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46110 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46112 node.parentNode.insertBefore(textNode, node);
46113 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46114 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46117 node.parentNode.removeChild(node);
46118 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46123 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46124 node.parentNode.removeChild(node);
46125 return false; // dont do chidlren
46127 //Roo.log(node.tagName);
46128 // remove - but keep children..
46129 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46130 //Roo.log('-- removed');
46131 while (node.childNodes.length) {
46132 var cn = node.childNodes[0];
46133 node.removeChild(cn);
46134 node.parentNode.insertBefore(cn, node);
46135 // move node to parent - and clean it..
46136 if (cn.nodeType == 1) {
46137 this.replaceTag(cn);
46141 node.parentNode.removeChild(node);
46142 /// no need to iterate chidlren = it's got none..
46143 //this.iterateChildren(node, this.cleanWord);
46144 return false; // no need to iterate children.
46147 if (node.className.length) {
46149 var cn = node.className.split(/\W+/);
46151 Roo.each(cn, function(cls) {
46152 if (cls.match(/Mso[a-zA-Z]+/)) {
46157 node.className = cna.length ? cna.join(' ') : '';
46159 node.removeAttribute("class");
46163 if (node.hasAttribute("lang")) {
46164 node.removeAttribute("lang");
46167 if (node.hasAttribute("style")) {
46169 var styles = node.getAttribute("style").split(";");
46171 Roo.each(styles, function(s) {
46172 if (!s.match(/:/)) {
46175 var kv = s.split(":");
46176 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46179 // what ever is left... we allow.
46182 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46183 if (!nstyle.length) {
46184 node.removeAttribute('style');
46187 return true; // do children
46193 styleToObject: function(node)
46195 var styles = (node.getAttribute("style") || '').split(";");
46197 Roo.each(styles, function(s) {
46198 if (!s.match(/:/)) {
46201 var kv = s.split(":");
46203 // what ever is left... we allow.
46204 ret[kv[0].trim()] = kv[1];
46210 replaceDocBullets : function(doc)
46212 // this is a bit odd - but it appears some indents use ql-indent-1
46214 var listpara = doc.getElementsByClassName('ql-indent-1');
46215 while(listpara.length) {
46216 this.replaceDocBullet(listpara.item(0));
46219 var listpara = doc.getElementsByClassName('MsoListParagraph');
46220 while(listpara.length) {
46221 this.replaceDocBullet(listpara.item(0));
46225 replaceDocBullet : function(p)
46227 // gather all the siblings.
46229 parent = p.parentNode,
46230 doc = parent.ownerDocument,
46235 if (ns.nodeType != 1) {
46236 ns = ns.nextSibling;
46239 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
46243 ns = ns.nextSibling;
46247 var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
46248 parent.insertBefore(ul, p);
46250 var stack = [ ul ];
46251 var last_li = false;
46253 items.forEach(function(n, ipos) {
46254 //Roo.log("got innertHMLT=" + n.innerHTML);
46256 var spans = n.getElementsByTagName('span');
46257 if (!spans.length) {
46258 //Roo.log("No spans found");
46260 parent.removeChild(n);
46261 return; // skip it...
46267 for(var i = 0; i < spans.length; i++) {
46269 style = this.styleToObject(spans[i]);
46270 if (typeof(style['mso-list']) == 'undefined') {
46274 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
46277 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
46278 style = this.styleToObject(n); // mo-list is from the parent node.
46279 if (typeof(style['mso-list']) == 'undefined') {
46280 //Roo.log("parent is missing level");
46281 parent.removeChild(n);
46285 var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1 ;
46291 var nul = doc.createElement('ul'); // what about number lists...
46292 last_li.appendChild(nul);
46298 var nli = stack[nlvl].appendChild(doc.createElement('li'));
46300 nli.innerHTML = n.innerHTML;
46301 //Roo.log("innerHTML = " + n.innerHTML);
46302 parent.removeChild(n);
46304 // copy children of p into nli
46305 /*while(n.firstChild) {
46306 var fc = n.firstChild;
46308 nli.appendChild(fc);
46323 * @class Roo.htmleditor.FilterStyleToTag
46324 * part of the word stuff... - certain 'styles' should be converted to tags.
46326 * font-weight: bold -> bold
46327 * ?? super / subscrit etc..
46330 * Run a new style to tag filter.
46331 * @param {Object} config Configuration options
46333 Roo.htmleditor.FilterStyleToTag = function(cfg)
46337 B : [ 'fontWeight' , 'bold'],
46338 I : [ 'fontStyle' , 'italic'],
46339 //pre : [ 'font-style' , 'italic'],
46340 // h1.. h6 ?? font-size?
46341 SUP : [ 'verticalAlign' , 'super' ],
46342 SUB : [ 'verticalAlign' , 'sub' ]
46347 Roo.apply(this, cfg);
46350 this.walk(cfg.node);
46357 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46359 tag: true, // all tags
46364 replaceTag : function(node)
46368 if (node.getAttribute("style") === null) {
46372 for (var k in this.tags) {
46373 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46375 node.style.removeProperty(this.tags[k][0]);
46378 if (!inject.length) {
46381 var cn = Array.from(node.childNodes);
46383 Roo.each(inject, function(t) {
46384 var nc = node.ownerDocument.createElement(t);
46385 nn.appendChild(nc);
46388 for(var i = 0;i < cn.length;cn++) {
46389 node.removeChild(cn[i]);
46390 nn.appendChild(cn[i]);
46392 return true /// iterate thru
46396 * @class Roo.htmleditor.FilterLongBr
46397 * BR/BR/BR - keep a maximum of 2...
46399 * Run a new Long BR Filter
46400 * @param {Object} config Configuration options
46403 Roo.htmleditor.FilterLongBr = function(cfg)
46405 // no need to apply config.
46406 this.walk(cfg.node);
46409 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46416 replaceTag : function(node)
46419 var ps = node.nextSibling;
46420 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46421 ps = ps.nextSibling;
46424 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
46425 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46429 if (!ps || ps.nodeType != 1) {
46433 if (!ps || ps.tagName != 'BR') {
46442 if (!node.previousSibling) {
46445 var ps = node.previousSibling;
46447 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46448 ps = ps.previousSibling;
46450 if (!ps || ps.nodeType != 1) {
46453 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46454 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46458 node.parentNode.removeChild(node); // remove me...
46460 return false; // no need to do children
46467 * @class Roo.htmleditor.FilterBlock
46468 * removes id / data-block and contenteditable that are associated with blocks
46469 * usage should be done on a cloned copy of the dom
46471 * Run a new Attribute Filter { node : xxxx }}
46472 * @param {Object} config Configuration options
46474 Roo.htmleditor.FilterBlock = function(cfg)
46476 Roo.apply(this, cfg);
46477 var qa = cfg.node.querySelectorAll;
46478 this.removeAttributes('data-block');
46479 this.removeAttributes('contenteditable');
46480 this.removeAttributes('id');
46484 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46486 node: true, // all tags
46489 removeAttributes : function(attr)
46491 var ar = this.node.querySelectorAll('*[' + attr + ']');
46492 for (var i =0;i<ar.length;i++) {
46493 ar[i].removeAttribute(attr);
46502 * This is based loosely on tinymce
46503 * @class Roo.htmleditor.TidySerializer
46504 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46506 * @method Serializer
46507 * @param {Object} settings Name/value settings object.
46511 Roo.htmleditor.TidySerializer = function(settings)
46513 Roo.apply(this, settings);
46515 this.writer = new Roo.htmleditor.TidyWriter(settings);
46520 Roo.htmleditor.TidySerializer.prototype = {
46523 * @param {boolean} inner do the inner of the node.
46530 * Serializes the specified node into a string.
46533 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46534 * @method serialize
46535 * @param {DomElement} node Node instance to serialize.
46536 * @return {String} String with HTML based on DOM tree.
46538 serialize : function(node) {
46540 // = settings.validate;
46541 var writer = this.writer;
46545 3: function(node) {
46547 writer.text(node.nodeValue, node);
46550 8: function(node) {
46551 writer.comment(node.nodeValue);
46553 // Processing instruction
46554 7: function(node) {
46555 writer.pi(node.name, node.nodeValue);
46558 10: function(node) {
46559 writer.doctype(node.nodeValue);
46562 4: function(node) {
46563 writer.cdata(node.nodeValue);
46565 // Document fragment
46566 11: function(node) {
46567 node = node.firstChild;
46573 node = node.nextSibling
46578 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46579 return writer.getContent();
46582 walk: function(node)
46584 var attrName, attrValue, sortedAttrs, i, l, elementRule,
46585 handler = this.handlers[node.nodeType];
46592 var name = node.nodeName;
46593 var isEmpty = node.childNodes.length < 1;
46595 var writer = this.writer;
46596 var attrs = node.attributes;
46599 writer.start(node.nodeName, attrs, isEmpty, node);
46603 node = node.firstChild;
46610 node = node.nextSibling;
46616 // Serialize element and treat all non elements as fragments
46621 * This is based loosely on tinymce
46622 * @class Roo.htmleditor.TidyWriter
46623 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46626 * - not tested much with 'PRE' formated elements.
46632 Roo.htmleditor.TidyWriter = function(settings)
46635 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46636 Roo.apply(this, settings);
46640 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46643 Roo.htmleditor.TidyWriter.prototype = {
46650 // part of state...
46654 last_inline : false,
46659 * Writes the a start element such as <p id="a">.
46662 * @param {String} name Name of the element.
46663 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46664 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46666 start: function(name, attrs, empty, node)
46668 var i, l, attr, value;
46670 // there are some situations where adding line break && indentation will not work. will not work.
46671 // <span / b / i ... formating?
46673 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46674 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46676 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46678 var add_lb = name == 'BR' ? false : in_inline;
46680 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46684 var indentstr = this.indentstr;
46686 // e_inline = elements that can be inline, but still allow \n before and after?
46687 // only 'BR' ??? any others?
46689 // ADD LINE BEFORE tage
46690 if (!this.in_pre) {
46693 if (name == 'BR') {
46695 } else if (this.lastElementEndsWS()) {
46698 // otherwise - no new line. (and dont indent.)
46709 this.html.push(indentstr + '<', name.toLowerCase());
46712 for (i = 0, l = attrs.length; i < l; i++) {
46714 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46720 this.html[this.html.length] = '/>';
46722 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46724 var e_inline = name == 'BR' ? false : this.in_inline;
46726 if (!e_inline && !this.in_pre) {
46733 this.html[this.html.length] = '>';
46735 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46737 if (!in_inline && !in_pre) {
46738 var cn = node.firstChild;
46740 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46744 cn = cn.nextSibling;
46752 indentstr : in_pre ? '' : (this.indentstr + this.indent),
46754 in_inline : in_inline
46756 // add a line after if we are not in a
46758 if (!in_inline && !in_pre) {
46767 lastElementEndsWS : function()
46769 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46770 if (value === false) {
46773 return value.match(/\s+$/);
46778 * Writes the a end element such as </p>.
46781 * @param {String} name Name of the element.
46783 end: function(name) {
46786 var indentstr = '';
46787 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46789 if (!this.in_pre && !in_inline) {
46791 indentstr = this.indentstr;
46793 this.html.push(indentstr + '</', name.toLowerCase(), '>');
46794 this.last_inline = in_inline;
46796 // pop the indent state..
46799 * Writes a text node.
46801 * In pre - we should not mess with the contents.
46805 * @param {String} text String to write out.
46806 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46808 text: function(in_text, node)
46810 // if not in whitespace critical
46811 if (in_text.length < 1) {
46814 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
46817 this.html[this.html.length] = text;
46821 if (this.in_inline) {
46822 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
46824 text = text.replace(/\s+/,' '); // all white space to single white space
46827 // if next tag is '<BR>', then we can trim right..
46828 if (node.nextSibling &&
46829 node.nextSibling.nodeType == 1 &&
46830 node.nextSibling.nodeName == 'BR' )
46832 text = text.replace(/\s+$/g,'');
46834 // if previous tag was a BR, we can also trim..
46835 if (node.previousSibling &&
46836 node.previousSibling.nodeType == 1 &&
46837 node.previousSibling.nodeName == 'BR' )
46839 text = this.indentstr + text.replace(/^\s+/g,'');
46841 if (text.match(/\n/)) {
46842 text = text.replace(
46843 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46845 // remoeve the last whitespace / line break.
46846 text = text.replace(/\n\s+$/,'');
46848 // repace long lines
46852 this.html[this.html.length] = text;
46855 // see if previous element was a inline element.
46856 var indentstr = this.indentstr;
46858 text = text.replace(/\s+/g," "); // all whitespace into single white space.
46860 // should trim left?
46861 if (node.previousSibling &&
46862 node.previousSibling.nodeType == 1 &&
46863 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
46869 text = text.replace(/^\s+/,''); // trim left
46872 // should trim right?
46873 if (node.nextSibling &&
46874 node.nextSibling.nodeType == 1 &&
46875 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
46880 text = text.replace(/\s+$/,''); // trim right
46887 if (text.length < 1) {
46890 if (!text.match(/\n/)) {
46891 this.html.push(indentstr + text);
46895 text = this.indentstr + text.replace(
46896 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46898 // remoeve the last whitespace / line break.
46899 text = text.replace(/\s+$/,'');
46901 this.html.push(text);
46903 // split and indent..
46908 * Writes a cdata node such as <![CDATA[data]]>.
46911 * @param {String} text String to write out inside the cdata.
46913 cdata: function(text) {
46914 this.html.push('<![CDATA[', text, ']]>');
46917 * Writes a comment node such as <!-- Comment -->.
46920 * @param {String} text String to write out inside the comment.
46922 comment: function(text) {
46923 this.html.push('<!--', text, '-->');
46926 * Writes a PI node such as <?xml attr="value" ?>.
46929 * @param {String} name Name of the pi.
46930 * @param {String} text String to write out inside the pi.
46932 pi: function(name, text) {
46933 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
46934 this.indent != '' && this.html.push('\n');
46937 * Writes a doctype node such as <!DOCTYPE data>.
46940 * @param {String} text String to write out inside the doctype.
46942 doctype: function(text) {
46943 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
46946 * Resets the internal buffer if one wants to reuse the writer.
46950 reset: function() {
46951 this.html.length = 0;
46960 * Returns the contents that got serialized.
46962 * @method getContent
46963 * @return {String} HTML contents that got written down.
46965 getContent: function() {
46966 return this.html.join('').replace(/\n$/, '');
46969 pushState : function(cfg)
46971 this.state.push(cfg);
46972 Roo.apply(this, cfg);
46975 popState : function()
46977 if (this.state.length < 1) {
46978 return; // nothing to push
46985 if (this.state.length > 0) {
46986 cfg = this.state[this.state.length-1];
46988 Roo.apply(this, cfg);
46991 addLine: function()
46993 if (this.html.length < 1) {
46998 var value = this.html[this.html.length - 1];
46999 if (value.length > 0 && '\n' !== value) {
47000 this.html.push('\n');
47005 //'pre script noscript style textarea video audio iframe object code'
47006 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
47010 Roo.htmleditor.TidyWriter.inline_elements = [
47011 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
47012 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
47014 Roo.htmleditor.TidyWriter.shortend_elements = [
47015 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
47016 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
47019 Roo.htmleditor.TidyWriter.whitespace_elements = [
47020 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
47022 * This is based loosely on tinymce
47023 * @class Roo.htmleditor.TidyEntities
47025 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47027 * Not 100% sure this is actually used or needed.
47030 Roo.htmleditor.TidyEntities = {
47033 * initialize data..
47035 init : function (){
47037 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
47042 buildEntitiesLookup: function(items, radix) {
47043 var i, chr, entity, lookup = {};
47047 items = typeof(items) == 'string' ? items.split(',') : items;
47048 radix = radix || 10;
47049 // Build entities lookup table
47050 for (i = 0; i < items.length; i += 2) {
47051 chr = String.fromCharCode(parseInt(items[i], radix));
47052 // Only add non base entities
47053 if (!this.baseEntities[chr]) {
47054 entity = '&' + items[i + 1] + ';';
47055 lookup[chr] = entity;
47056 lookup[entity] = chr;
47095 // Needs to be escaped since the YUI compressor would otherwise break the code
47102 // Reverse lookup table for raw entities
47103 reverseEntities : {
47111 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47112 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47113 rawCharsRegExp : /[<>&\"\']/g,
47114 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
47115 namedEntities : false,
47116 namedEntitiesData : [
47617 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47619 * @method encodeRaw
47620 * @param {String} text Text to encode.
47621 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47622 * @return {String} Entity encoded text.
47624 encodeRaw: function(text, attr)
47627 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47628 return t.baseEntities[chr] || chr;
47632 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47633 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47634 * and is exposed as the DOMUtils.encode function.
47636 * @method encodeAllRaw
47637 * @param {String} text Text to encode.
47638 * @return {String} Entity encoded text.
47640 encodeAllRaw: function(text) {
47642 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47643 return t.baseEntities[chr] || chr;
47647 * Encodes the specified string using numeric entities. The core entities will be
47648 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47650 * @method encodeNumeric
47651 * @param {String} text Text to encode.
47652 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47653 * @return {String} Entity encoded text.
47655 encodeNumeric: function(text, attr) {
47657 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47658 // Multi byte sequence convert it to a single entity
47659 if (chr.length > 1) {
47660 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47662 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47666 * Encodes the specified string using named entities. The core entities will be encoded
47667 * as named ones but all non lower ascii characters will be encoded into named entities.
47669 * @method encodeNamed
47670 * @param {String} text Text to encode.
47671 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47672 * @param {Object} entities Optional parameter with entities to use.
47673 * @return {String} Entity encoded text.
47675 encodeNamed: function(text, attr, entities) {
47677 entities = entities || this.namedEntities;
47678 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47679 return t.baseEntities[chr] || entities[chr] || chr;
47683 * Returns an encode function based on the name(s) and it's optional entities.
47685 * @method getEncodeFunc
47686 * @param {String} name Comma separated list of encoders for example named,numeric.
47687 * @param {String} entities Optional parameter with entities to use instead of the built in set.
47688 * @return {function} Encode function to be used.
47690 getEncodeFunc: function(name, entities) {
47691 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47693 function encodeNamedAndNumeric(text, attr) {
47694 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47695 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47699 function encodeCustomNamed(text, attr) {
47700 return t.encodeNamed(text, attr, entities);
47702 // Replace + with , to be compatible with previous TinyMCE versions
47703 name = this.makeMap(name.replace(/\+/g, ','));
47704 // Named and numeric encoder
47705 if (name.named && name.numeric) {
47706 return this.encodeNamedAndNumeric;
47712 return encodeCustomNamed;
47714 return this.encodeNamed;
47717 if (name.numeric) {
47718 return this.encodeNumeric;
47721 return this.encodeRaw;
47724 * Decodes the specified string, this will replace entities with raw UTF characters.
47727 * @param {String} text Text to entity decode.
47728 * @return {String} Entity decoded string.
47730 decode: function(text)
47733 return text.replace(this.entityRegExp, function(all, numeric) {
47735 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47736 // Support upper UTF
47737 if (numeric > 65535) {
47739 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47741 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47743 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47746 nativeDecode : function (text) {
47749 makeMap : function (items, delim, map) {
47751 items = items || [];
47752 delim = delim || ',';
47753 if (typeof items == "string") {
47754 items = items.split(delim);
47759 map[items[i]] = {};
47767 Roo.htmleditor.TidyEntities.init();
47769 * @class Roo.htmleditor.KeyEnter
47770 * Handle Enter press..
47771 * @cfg {Roo.HtmlEditorCore} core the editor.
47773 * Create a new Filter.
47774 * @param {Object} config Configuration options
47781 Roo.htmleditor.KeyEnter = function(cfg) {
47782 Roo.apply(this, cfg);
47783 // this does not actually call walk as it's really just a abstract class
47785 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47788 //Roo.htmleditor.KeyEnter.i = 0;
47791 Roo.htmleditor.KeyEnter.prototype = {
47795 keypress : function(e)
47797 if (e.charCode != 13 && e.charCode != 10) {
47798 Roo.log([e.charCode,e]);
47801 e.preventDefault();
47802 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47803 var doc = this.core.doc;
47807 var sel = this.core.getSelection();
47808 var range = sel.getRangeAt(0);
47809 var n = range.commonAncestorContainer;
47810 var pc = range.closest([ 'ol', 'ul']);
47811 var pli = range.closest('li');
47812 if (!pc || e.ctrlKey) {
47813 sel.insertNode('br', 'after');
47815 this.core.undoManager.addEvent();
47816 this.core.fireEditorEvent(e);
47820 // deal with <li> insetion
47821 if (pli.innerText.trim() == '' &&
47822 pli.previousSibling &&
47823 pli.previousSibling.nodeName == 'LI' &&
47824 pli.previousSibling.innerText.trim() == '') {
47825 pli.parentNode.removeChild(pli.previousSibling);
47826 sel.cursorAfter(pc);
47827 this.core.undoManager.addEvent();
47828 this.core.fireEditorEvent(e);
47832 var li = doc.createElement('LI');
47833 li.innerHTML = ' ';
47834 if (!pli || !pli.firstSibling) {
47835 pc.appendChild(li);
47837 pli.parentNode.insertBefore(li, pli.firstSibling);
47839 sel.cursorText (li.firstChild);
47841 this.core.undoManager.addEvent();
47842 this.core.fireEditorEvent(e);
47854 * @class Roo.htmleditor.Block
47855 * Base class for html editor blocks - do not use it directly .. extend it..
47856 * @cfg {DomElement} node The node to apply stuff to.
47857 * @cfg {String} friendly_name the name that appears in the context bar about this block
47858 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
47861 * Create a new Filter.
47862 * @param {Object} config Configuration options
47865 Roo.htmleditor.Block = function(cfg)
47867 // do nothing .. should not be called really.
47870 * factory method to get the block from an element (using cache if necessary)
47872 * @param {HtmlElement} the dom element
47874 Roo.htmleditor.Block.factory = function(node)
47876 var cc = Roo.htmleditor.Block.cache;
47877 var id = Roo.get(node).id;
47878 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
47879 Roo.htmleditor.Block.cache[id].readElement(node);
47880 return Roo.htmleditor.Block.cache[id];
47882 var db = node.getAttribute('data-block');
47884 db = node.nodeName.toLowerCase().toUpperCaseFirst();
47886 var cls = Roo.htmleditor['Block' + db];
47887 if (typeof(cls) == 'undefined') {
47888 //Roo.log(node.getAttribute('data-block'));
47889 Roo.log("OOps missing block : " + 'Block' + db);
47892 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
47893 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
47897 * initalize all Elements from content that are 'blockable'
47899 * @param the body element
47901 Roo.htmleditor.Block.initAll = function(body, type)
47903 if (typeof(type) == 'undefined') {
47904 var ia = Roo.htmleditor.Block.initAll;
47910 Roo.each(Roo.get(body).query(type), function(e) {
47911 Roo.htmleditor.Block.factory(e);
47914 // question goes here... do we need to clear out this cache sometimes?
47915 // or show we make it relivant to the htmleditor.
47916 Roo.htmleditor.Block.cache = {};
47918 Roo.htmleditor.Block.prototype = {
47922 // used by context menu
47923 friendly_name : 'Based Block',
47925 // text for button to delete this element
47926 deleteTitle : false,
47930 * Update a node with values from this object
47931 * @param {DomElement} node
47933 updateElement : function(node)
47935 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
47938 * convert to plain HTML for calling insertAtCursor..
47940 toHTML : function()
47942 return Roo.DomHelper.markup(this.toObject());
47945 * used by readEleemnt to extract data from a node
47946 * may need improving as it's pretty basic
47948 * @param {DomElement} node
47949 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
47950 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
47951 * @param {String} style the style property - eg. text-align
47953 getVal : function(node, tag, attr, style)
47956 if (tag !== true && n.tagName != tag.toUpperCase()) {
47957 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
47958 // but kiss for now.
47959 n = node.getElementsByTagName(tag).item(0);
47964 if (attr === false) {
47967 if (attr == 'html') {
47968 return n.innerHTML;
47970 if (attr == 'style') {
47971 return n.style[style];
47974 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
47978 * create a DomHelper friendly object - for use with
47979 * Roo.DomHelper.markup / overwrite / etc..
47982 toObject : function()
47987 * Read a node that has a 'data-block' property - and extract the values from it.
47988 * @param {DomElement} node - the node
47990 readElement : function(node)
48001 * @class Roo.htmleditor.BlockFigure
48002 * Block that has an image and a figcaption
48003 * @cfg {String} image_src the url for the image
48004 * @cfg {String} align (left|right) alignment for the block default left
48005 * @cfg {String} caption the text to appear below (and in the alt tag)
48006 * @cfg {String} caption_display (block|none) display or not the caption
48007 * @cfg {String|number} image_width the width of the image number or %?
48008 * @cfg {String|number} image_height the height of the image number or %?
48011 * Create a new Filter.
48012 * @param {Object} config Configuration options
48015 Roo.htmleditor.BlockFigure = function(cfg)
48018 this.readElement(cfg.node);
48019 this.updateElement(cfg.node);
48021 Roo.apply(this, cfg);
48023 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
48030 caption_display : 'block',
48036 // margin: '2%', not used
48038 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
48041 // used by context menu
48042 friendly_name : 'Image with caption',
48043 deleteTitle : "Delete Image and Caption",
48045 contextMenu : function(toolbar)
48048 var block = function() {
48049 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48053 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48055 var syncValue = toolbar.editorcore.syncValue;
48061 xtype : 'TextItem',
48063 xns : rooui.Toolbar //Boostrap?
48067 text: 'Change Image URL',
48070 click: function (btn, state)
48074 Roo.MessageBox.show({
48075 title : "Image Source URL",
48076 msg : "Enter the url for the image",
48077 buttons: Roo.MessageBox.OKCANCEL,
48078 fn: function(btn, val){
48085 toolbar.editorcore.onEditorEvent();
48089 //multiline: multiline,
48091 value : b.image_src
48095 xns : rooui.Toolbar
48100 text: 'Change Link URL',
48103 click: function (btn, state)
48107 Roo.MessageBox.show({
48108 title : "Link URL",
48109 msg : "Enter the url for the link - leave blank to have no link",
48110 buttons: Roo.MessageBox.OKCANCEL,
48111 fn: function(btn, val){
48118 toolbar.editorcore.onEditorEvent();
48122 //multiline: multiline,
48128 xns : rooui.Toolbar
48132 text: 'Show Video URL',
48135 click: function (btn, state)
48137 Roo.MessageBox.alert("Video URL",
48138 block().video_url == '' ? 'This image is not linked ot a video' :
48139 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
48142 xns : rooui.Toolbar
48147 xtype : 'TextItem',
48149 xns : rooui.Toolbar //Boostrap?
48152 xtype : 'ComboBox',
48153 allowBlank : false,
48154 displayField : 'val',
48157 triggerAction : 'all',
48159 valueField : 'val',
48163 select : function (combo, r, index)
48165 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48167 b.width = r.get('val');
48170 toolbar.editorcore.onEditorEvent();
48175 xtype : 'SimpleStore',
48186 xtype : 'TextItem',
48188 xns : rooui.Toolbar //Boostrap?
48191 xtype : 'ComboBox',
48192 allowBlank : false,
48193 displayField : 'val',
48196 triggerAction : 'all',
48198 valueField : 'val',
48202 select : function (combo, r, index)
48204 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48206 b.align = r.get('val');
48209 toolbar.editorcore.onEditorEvent();
48214 xtype : 'SimpleStore',
48228 text: 'Hide Caption',
48229 name : 'caption_display',
48231 enableToggle : true,
48232 setValue : function(v) {
48233 // this trigger toggle.
48235 this.setText(v ? "Hide Caption" : "Show Caption");
48236 this.setPressed(v != 'block');
48239 toggle: function (btn, state)
48242 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48243 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48246 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48247 toolbar.editorcore.onEditorEvent();
48250 xns : rooui.Toolbar
48256 * create a DomHelper friendly object - for use with
48257 * Roo.DomHelper.markup / overwrite / etc..
48259 toObject : function()
48261 var d = document.createElement('div');
48262 d.innerHTML = this.caption;
48264 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
48266 var iw = this.align == 'center' ? this.width : '100%';
48269 contenteditable : 'false',
48270 src : this.image_src,
48271 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48274 maxWidth : iw + ' !important', // this is not getting rendered?
48280 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48282 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
48287 if (this.href.length > 0) {
48291 contenteditable : 'true',
48299 if (this.video_url.length > 0) {
48304 allowfullscreen : true,
48305 width : 420, // these are for video tricks - that we replace the outer
48307 src : this.video_url,
48313 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48314 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48319 'data-block' : 'Figure',
48320 'data-width' : this.width,
48321 contenteditable : 'false',
48325 float : this.align ,
48326 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48327 width : this.align == 'center' ? '100%' : this.width,
48329 padding: this.align == 'center' ? '0' : '0 10px' ,
48330 textAlign : this.align // seems to work for email..
48335 align : this.align,
48341 'data-display' : this.caption_display,
48343 textAlign : 'left',
48345 lineHeight : '24px',
48346 display : this.caption_display,
48347 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
48349 width: this.align == 'center' ? this.width : '100%'
48353 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
48358 marginTop : '16px',
48364 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
48366 contenteditable : true,
48382 readElement : function(node)
48384 // this should not really come from the link...
48385 this.video_url = this.getVal(node, 'div', 'src');
48386 this.cls = this.getVal(node, 'div', 'class');
48387 this.href = this.getVal(node, 'a', 'href');
48390 this.image_src = this.getVal(node, 'img', 'src');
48392 this.align = this.getVal(node, 'figure', 'align');
48393 var figcaption = this.getVal(node, 'figcaption', false);
48394 if (figcaption !== '') {
48395 this.caption = this.getVal(figcaption, 'i', 'html');
48399 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48400 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48401 this.width = this.getVal(node, true, 'data-width');
48402 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48405 removeNode : function()
48422 * @class Roo.htmleditor.BlockTable
48423 * Block that manages a table
48426 * Create a new Filter.
48427 * @param {Object} config Configuration options
48430 Roo.htmleditor.BlockTable = function(cfg)
48433 this.readElement(cfg.node);
48434 this.updateElement(cfg.node);
48436 Roo.apply(this, cfg);
48439 for(var r = 0; r < this.no_row; r++) {
48441 for(var c = 0; c < this.no_col; c++) {
48442 this.rows[r][c] = this.emptyCell();
48449 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48458 // used by context menu
48459 friendly_name : 'Table',
48460 deleteTitle : 'Delete Table',
48461 // context menu is drawn once..
48463 contextMenu : function(toolbar)
48466 var block = function() {
48467 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48471 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48473 var syncValue = toolbar.editorcore.syncValue;
48479 xtype : 'TextItem',
48481 xns : rooui.Toolbar //Boostrap?
48484 xtype : 'ComboBox',
48485 allowBlank : false,
48486 displayField : 'val',
48489 triggerAction : 'all',
48491 valueField : 'val',
48495 select : function (combo, r, index)
48497 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48499 b.width = r.get('val');
48502 toolbar.editorcore.onEditorEvent();
48507 xtype : 'SimpleStore',
48519 xtype : 'TextItem',
48520 text : "Columns: ",
48521 xns : rooui.Toolbar //Boostrap?
48528 click : function (_self, e)
48530 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48531 block().removeColumn();
48533 toolbar.editorcore.onEditorEvent();
48536 xns : rooui.Toolbar
48542 click : function (_self, e)
48544 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48545 block().addColumn();
48547 toolbar.editorcore.onEditorEvent();
48550 xns : rooui.Toolbar
48554 xtype : 'TextItem',
48556 xns : rooui.Toolbar //Boostrap?
48563 click : function (_self, e)
48565 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48566 block().removeRow();
48568 toolbar.editorcore.onEditorEvent();
48571 xns : rooui.Toolbar
48577 click : function (_self, e)
48581 toolbar.editorcore.onEditorEvent();
48584 xns : rooui.Toolbar
48589 text: 'Reset Column Widths',
48592 click : function (_self, e)
48594 block().resetWidths();
48596 toolbar.editorcore.onEditorEvent();
48599 xns : rooui.Toolbar
48610 * create a DomHelper friendly object - for use with
48611 * Roo.DomHelper.markup / overwrite / etc..
48612 * ?? should it be called with option to hide all editing features?
48614 toObject : function()
48619 contenteditable : 'false', // this stops cell selection from picking the table.
48620 'data-block' : 'Table',
48623 border : 'solid 1px #000', // ??? hard coded?
48624 'border-collapse' : 'collapse'
48627 { tag : 'tbody' , cn : [] }
48631 // do we have a head = not really
48633 Roo.each(this.rows, function( row ) {
48638 border : 'solid 1px #000',
48644 ret.cn[0].cn.push(tr);
48645 // does the row have any properties? ?? height?
48647 Roo.each(row, function( cell ) {
48651 contenteditable : 'true',
48652 'data-block' : 'Td',
48656 if (cell.colspan > 1) {
48657 td.colspan = cell.colspan ;
48658 nc += cell.colspan;
48662 if (cell.rowspan > 1) {
48663 td.rowspan = cell.rowspan ;
48672 ncols = Math.max(nc, ncols);
48676 // add the header row..
48685 readElement : function(node)
48687 node = node ? node : this.node ;
48688 this.width = this.getVal(node, true, 'style', 'width') || '100%';
48692 var trs = Array.from(node.rows);
48693 trs.forEach(function(tr) {
48695 this.rows.push(row);
48699 Array.from(tr.cells).forEach(function(td) {
48702 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48703 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48704 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48705 html : td.innerHTML
48707 no_column += add.colspan;
48714 this.no_col = Math.max(this.no_col, no_column);
48721 normalizeRows: function()
48725 this.rows.forEach(function(row) {
48728 row = this.normalizeRow(row);
48730 row.forEach(function(c) {
48731 while (typeof(ret[rid][cid]) != 'undefined') {
48734 if (typeof(ret[rid]) == 'undefined') {
48740 if (c.rowspan < 2) {
48744 for(var i = 1 ;i < c.rowspan; i++) {
48745 if (typeof(ret[rid+i]) == 'undefined') {
48748 ret[rid+i][cid] = c;
48756 normalizeRow: function(row)
48759 row.forEach(function(c) {
48760 if (c.colspan < 2) {
48764 for(var i =0 ;i < c.colspan; i++) {
48772 deleteColumn : function(sel)
48774 if (!sel || sel.type != 'col') {
48777 if (this.no_col < 2) {
48781 this.rows.forEach(function(row) {
48782 var cols = this.normalizeRow(row);
48783 var col = cols[sel.col];
48784 if (col.colspan > 1) {
48794 removeColumn : function()
48796 this.deleteColumn({
48798 col : this.no_col-1
48800 this.updateElement();
48804 addColumn : function()
48807 this.rows.forEach(function(row) {
48808 row.push(this.emptyCell());
48811 this.updateElement();
48814 deleteRow : function(sel)
48816 if (!sel || sel.type != 'row') {
48820 if (this.no_row < 2) {
48824 var rows = this.normalizeRows();
48827 rows[sel.row].forEach(function(col) {
48828 if (col.rowspan > 1) {
48831 col.remove = 1; // flage it as removed.
48836 this.rows.forEach(function(row) {
48838 row.forEach(function(c) {
48839 if (typeof(c.remove) == 'undefined') {
48844 if (newrow.length > 0) {
48848 this.rows = newrows;
48853 this.updateElement();
48856 removeRow : function()
48860 row : this.no_row-1
48866 addRow : function()
48870 for (var i = 0; i < this.no_col; i++ ) {
48872 row.push(this.emptyCell());
48875 this.rows.push(row);
48876 this.updateElement();
48880 // the default cell object... at present...
48881 emptyCell : function() {
48882 return (new Roo.htmleditor.BlockTd({})).toObject();
48887 removeNode : function()
48894 resetWidths : function()
48896 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
48897 var nn = Roo.htmleditor.Block.factory(n);
48899 nn.updateElement(n);
48912 * since selections really work on the table cell, then editing really should work from there
48914 * The original plan was to support merging etc... - but that may not be needed yet..
48916 * So this simple version will support:
48918 * adjust the width +/-
48919 * reset the width...
48928 * @class Roo.htmleditor.BlockTable
48929 * Block that manages a table
48932 * Create a new Filter.
48933 * @param {Object} config Configuration options
48936 Roo.htmleditor.BlockTd = function(cfg)
48939 this.readElement(cfg.node);
48940 this.updateElement(cfg.node);
48942 Roo.apply(this, cfg);
48947 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
48952 textAlign : 'left',
48959 // used by context menu
48960 friendly_name : 'Table Cell',
48961 deleteTitle : false, // use our customer delete
48963 // context menu is drawn once..
48965 contextMenu : function(toolbar)
48968 var cell = function() {
48969 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48972 var table = function() {
48973 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
48977 var saveSel = function()
48979 lr = toolbar.editorcore.getSelection().getRangeAt(0);
48981 var restoreSel = function()
48985 toolbar.editorcore.focus();
48986 var cr = toolbar.editorcore.getSelection();
48987 cr.removeAllRanges();
48989 toolbar.editorcore.onEditorEvent();
48990 }).defer(10, this);
48996 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48998 var syncValue = toolbar.editorcore.syncValue;
49005 text : 'Edit Table',
49007 click : function() {
49008 var t = toolbar.tb.selectedNode.closest('table');
49009 toolbar.editorcore.selectNode(t);
49010 toolbar.editorcore.onEditorEvent();
49019 xtype : 'TextItem',
49020 text : "Column Width: ",
49021 xns : rooui.Toolbar
49028 click : function (_self, e)
49030 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49031 cell().shrinkColumn();
49033 toolbar.editorcore.onEditorEvent();
49036 xns : rooui.Toolbar
49042 click : function (_self, e)
49044 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49045 cell().growColumn();
49047 toolbar.editorcore.onEditorEvent();
49050 xns : rooui.Toolbar
49054 xtype : 'TextItem',
49055 text : "Vertical Align: ",
49056 xns : rooui.Toolbar //Boostrap?
49059 xtype : 'ComboBox',
49060 allowBlank : false,
49061 displayField : 'val',
49064 triggerAction : 'all',
49066 valueField : 'val',
49070 select : function (combo, r, index)
49072 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49074 b.valign = r.get('val');
49077 toolbar.editorcore.onEditorEvent();
49082 xtype : 'SimpleStore',
49086 ['bottom'] // there are afew more...
49094 xtype : 'TextItem',
49095 text : "Merge Cells: ",
49096 xns : rooui.Toolbar
49105 click : function (_self, e)
49107 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49108 cell().mergeRight();
49109 //block().growColumn();
49111 toolbar.editorcore.onEditorEvent();
49114 xns : rooui.Toolbar
49121 click : function (_self, e)
49123 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49124 cell().mergeBelow();
49125 //block().growColumn();
49127 toolbar.editorcore.onEditorEvent();
49130 xns : rooui.Toolbar
49133 xtype : 'TextItem',
49135 xns : rooui.Toolbar
49143 click : function (_self, e)
49145 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49148 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49149 toolbar.editorcore.onEditorEvent();
49153 xns : rooui.Toolbar
49157 xns : rooui.Toolbar
49166 xns : rooui.Toolbar,
49175 click : function (_self, e)
49179 cell().deleteColumn();
49181 toolbar.editorcore.selectNode(t.node);
49182 toolbar.editorcore.onEditorEvent();
49191 click : function (_self, e)
49194 cell().deleteRow();
49197 toolbar.editorcore.selectNode(t.node);
49198 toolbar.editorcore.onEditorEvent();
49205 xtype : 'Separator',
49212 click : function (_self, e)
49215 var nn = t.node.nextSibling || t.node.previousSibling;
49216 t.node.parentNode.removeChild(t.node);
49218 toolbar.editorcore.selectNode(nn, true);
49220 toolbar.editorcore.onEditorEvent();
49230 // align... << fixme
49238 * create a DomHelper friendly object - for use with
49239 * Roo.DomHelper.markup / overwrite / etc..
49240 * ?? should it be called with option to hide all editing features?
49243 * create a DomHelper friendly object - for use with
49244 * Roo.DomHelper.markup / overwrite / etc..
49245 * ?? should it be called with option to hide all editing features?
49247 toObject : function()
49252 contenteditable : 'true', // this stops cell selection from picking the table.
49253 'data-block' : 'Td',
49254 valign : this.valign,
49256 'text-align' : this.textAlign,
49257 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49258 'border-collapse' : 'collapse',
49259 padding : '6px', // 8 for desktop / 4 for mobile
49260 'vertical-align': this.valign
49264 if (this.width != '') {
49265 ret.width = this.width;
49266 ret.style.width = this.width;
49270 if (this.colspan > 1) {
49271 ret.colspan = this.colspan ;
49273 if (this.rowspan > 1) {
49274 ret.rowspan = this.rowspan ;
49283 readElement : function(node)
49285 node = node ? node : this.node ;
49286 this.width = node.style.width;
49287 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49288 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49289 this.html = node.innerHTML;
49294 // the default cell object... at present...
49295 emptyCell : function() {
49299 textAlign : 'left',
49300 html : " " // is this going to be editable now?
49305 removeNode : function()
49307 return this.node.closest('table');
49315 toTableArray : function()
49318 var tab = this.node.closest('tr').closest('table');
49319 Array.from(tab.rows).forEach(function(r, ri){
49323 this.colWidths = [];
49324 var all_auto = true;
49325 Array.from(tab.rows).forEach(function(r, ri){
49328 Array.from(r.cells).forEach(function(ce, ci){
49333 colspan : ce.colSpan,
49334 rowspan : ce.rowSpan
49336 if (ce.isEqualNode(this.node)) {
49339 // if we have been filled up by a row?
49340 if (typeof(ret[rn][cn]) != 'undefined') {
49341 while(typeof(ret[rn][cn]) != 'undefined') {
49347 if (typeof(this.colWidths[cn]) == 'undefined') {
49348 this.colWidths[cn] = ce.style.width;
49349 if (this.colWidths[cn] != '') {
49355 if (c.colspan < 2 && c.rowspan < 2 ) {
49360 for(var j = 0; j < c.rowspan; j++) {
49361 if (typeof(ret[rn+j]) == 'undefined') {
49362 continue; // we have a problem..
49365 for(var i = 0; i < c.colspan; i++) {
49366 ret[rn+j][cn+i] = c;
49375 // initalize widths.?
49376 // either all widths or no widths..
49378 this.colWidths[0] = false; // no widths flag.
49389 mergeRight: function()
49392 // get the contents of the next cell along..
49393 var tr = this.node.closest('tr');
49394 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49395 if (i >= tr.childNodes.length - 1) {
49396 return; // no cells on right to merge with.
49398 var table = this.toTableArray();
49400 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49401 return; // nothing right?
49403 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49404 // right cell - must be same rowspan and on the same row.
49405 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49406 return; // right hand side is not same rowspan.
49411 this.node.innerHTML += ' ' + rc.cell.innerHTML;
49412 tr.removeChild(rc.cell);
49413 this.colspan += rc.colspan;
49414 this.node.setAttribute('colspan', this.colspan);
49419 mergeBelow : function()
49421 var table = this.toTableArray();
49422 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49423 return; // no row below
49425 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49426 return; // nothing right?
49428 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49430 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49431 return; // right hand side is not same rowspan.
49433 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
49434 rc.cell.parentNode.removeChild(rc.cell);
49435 this.rowspan += rc.rowspan;
49436 this.node.setAttribute('rowspan', this.rowspan);
49441 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49444 var table = this.toTableArray();
49445 var cd = this.cellData;
49449 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49453 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49454 if (r == cd.row && c == cd.col) {
49455 this.node.removeAttribute('rowspan');
49456 this.node.removeAttribute('colspan');
49460 var ntd = this.node.cloneNode(); // which col/row should be 0..
49461 ntd.removeAttribute('id'); //
49462 //ntd.style.width = '';
49463 ntd.innerHTML = '';
49464 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
49468 this.redrawAllCells(table);
49476 redrawAllCells: function(table)
49480 var tab = this.node.closest('tr').closest('table');
49481 var ctr = tab.rows[0].parentNode;
49482 Array.from(tab.rows).forEach(function(r, ri){
49484 Array.from(r.cells).forEach(function(ce, ci){
49485 ce.parentNode.removeChild(ce);
49487 r.parentNode.removeChild(r);
49489 for(var r = 0 ; r < table.length; r++) {
49490 var re = tab.rows[r];
49492 var re = tab.ownerDocument.createElement('tr');
49493 ctr.appendChild(re);
49494 for(var c = 0 ; c < table[r].length; c++) {
49495 if (table[r][c].cell === false) {
49499 re.appendChild(table[r][c].cell);
49501 table[r][c].cell = false;
49506 updateWidths : function(table)
49508 for(var r = 0 ; r < table.length; r++) {
49510 for(var c = 0 ; c < table[r].length; c++) {
49511 if (table[r][c].cell === false) {
49515 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49516 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49517 el.width = Math.floor(this.colWidths[c]) +'%';
49518 el.updateElement(el.node);
49520 table[r][c].cell = false; // done
49524 normalizeWidths : function(table)
49527 if (this.colWidths[0] === false) {
49528 var nw = 100.0 / this.colWidths.length;
49529 this.colWidths.forEach(function(w,i) {
49530 this.colWidths[i] = nw;
49535 var t = 0, missing = [];
49537 this.colWidths.forEach(function(w,i) {
49539 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49540 var add = this.colWidths[i];
49549 var nc = this.colWidths.length;
49550 if (missing.length) {
49551 var mult = (nc - missing.length) / (1.0 * nc);
49553 var ew = (100 -t) / (1.0 * missing.length);
49554 this.colWidths.forEach(function(w,i) {
49556 this.colWidths[i] = w * mult;
49560 this.colWidths[i] = ew;
49562 // have to make up numbers..
49565 // now we should have all the widths..
49570 shrinkColumn : function()
49572 var table = this.toTableArray();
49573 this.normalizeWidths(table);
49574 var col = this.cellData.col;
49575 var nw = this.colWidths[col] * 0.8;
49579 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49580 this.colWidths.forEach(function(w,i) {
49582 this.colWidths[i] = nw;
49585 this.colWidths[i] += otherAdd
49587 this.updateWidths(table);
49590 growColumn : function()
49592 var table = this.toTableArray();
49593 this.normalizeWidths(table);
49594 var col = this.cellData.col;
49595 var nw = this.colWidths[col] * 1.2;
49599 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49600 this.colWidths.forEach(function(w,i) {
49602 this.colWidths[i] = nw;
49605 this.colWidths[i] -= otherSub
49607 this.updateWidths(table);
49610 deleteRow : function()
49612 // delete this rows 'tr'
49613 // if any of the cells in this row have a rowspan > 1 && row!= this row..
49614 // then reduce the rowspan.
49615 var table = this.toTableArray();
49616 // this.cellData.row;
49617 for (var i =0;i< table[this.cellData.row].length ; i++) {
49618 var c = table[this.cellData.row][i];
49619 if (c.row != this.cellData.row) {
49622 c.cell.setAttribute('rowspan', c.rowspan);
49625 if (c.rowspan > 1) {
49627 c.cell.setAttribute('rowspan', c.rowspan);
49630 table.splice(this.cellData.row,1);
49631 this.redrawAllCells(table);
49634 deleteColumn : function()
49636 var table = this.toTableArray();
49638 for (var i =0;i< table.length ; i++) {
49639 var c = table[i][this.cellData.col];
49640 if (c.col != this.cellData.col) {
49641 table[i][this.cellData.col].colspan--;
49642 } else if (c.colspan > 1) {
49644 c.cell.setAttribute('colspan', c.colspan);
49646 table[i].splice(this.cellData.col,1);
49649 this.redrawAllCells(table);
49657 //<script type="text/javascript">
49660 * Based Ext JS Library 1.1.1
49661 * Copyright(c) 2006-2007, Ext JS, LLC.
49667 * @class Roo.HtmlEditorCore
49668 * @extends Roo.Component
49669 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49671 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49674 Roo.HtmlEditorCore = function(config){
49677 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49682 * @event initialize
49683 * Fires when the editor is fully initialized (including the iframe)
49684 * @param {Roo.HtmlEditorCore} this
49689 * Fires when the editor is first receives the focus. Any insertion must wait
49690 * until after this event.
49691 * @param {Roo.HtmlEditorCore} this
49695 * @event beforesync
49696 * Fires before the textarea is updated with content from the editor iframe. Return false
49697 * to cancel the sync.
49698 * @param {Roo.HtmlEditorCore} this
49699 * @param {String} html
49703 * @event beforepush
49704 * Fires before the iframe editor is updated with content from the textarea. Return false
49705 * to cancel the push.
49706 * @param {Roo.HtmlEditorCore} this
49707 * @param {String} html
49712 * Fires when the textarea is updated with content from the editor iframe.
49713 * @param {Roo.HtmlEditorCore} this
49714 * @param {String} html
49719 * Fires when the iframe editor is updated with content from the textarea.
49720 * @param {Roo.HtmlEditorCore} this
49721 * @param {String} html
49726 * @event editorevent
49727 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49728 * @param {Roo.HtmlEditorCore} this
49735 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49737 // defaults : white / black...
49738 this.applyBlacklists();
49745 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
49749 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
49755 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
49760 * @cfg {Number} height (in pixels)
49764 * @cfg {Number} width (in pixels)
49768 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49769 * if you are doing an email editor, this probably needs disabling, it's designed
49774 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49776 enableBlocks : true,
49778 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49781 stylesheets: false,
49783 * @cfg {String} language default en - language of text (usefull for rtl languages)
49789 * @cfg {boolean} allowComments - default false - allow comments in HTML source
49790 * - by default they are stripped - if you are editing email you may need this.
49792 allowComments: false,
49796 // private properties
49797 validationEvent : false,
49799 initialized : false,
49801 sourceEditMode : false,
49802 onFocus : Roo.emptyFn,
49804 hideMode:'offsets',
49808 // blacklist + whitelisted elements..
49815 undoManager : false,
49817 * Protected method that will not generally be called directly. It
49818 * is called when the editor initializes the iframe with HTML contents. Override this method if you
49819 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
49821 getDocMarkup : function(){
49825 // inherit styels from page...??
49826 if (this.stylesheets === false) {
49828 Roo.get(document.head).select('style').each(function(node) {
49829 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49832 Roo.get(document.head).select('link').each(function(node) {
49833 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49836 } else if (!this.stylesheets.length) {
49838 st = '<style type="text/css">' +
49839 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49842 for (var i in this.stylesheets) {
49843 if (typeof(this.stylesheets[i]) != 'string') {
49846 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
49851 st += '<style type="text/css">' +
49852 'IMG { cursor: pointer } ' +
49855 st += '<meta name="google" content="notranslate">';
49857 var cls = 'notranslate roo-htmleditor-body';
49859 if(this.bodyCls.length){
49860 cls += ' ' + this.bodyCls;
49863 return '<html class="notranslate" translate="no"><head>' + st +
49864 //<style type="text/css">' +
49865 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49867 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
49871 onRender : function(ct, position)
49874 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
49875 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
49878 this.el.dom.style.border = '0 none';
49879 this.el.dom.setAttribute('tabIndex', -1);
49880 this.el.addClass('x-hidden hide');
49884 if(Roo.isIE){ // fix IE 1px bogus margin
49885 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
49889 this.frameId = Roo.id();
49893 var iframe = this.owner.wrap.createChild({
49895 cls: 'form-control', // bootstrap..
49897 name: this.frameId,
49898 frameBorder : 'no',
49899 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
49904 this.iframe = iframe.dom;
49906 this.assignDocWin();
49908 this.doc.designMode = 'on';
49911 this.doc.write(this.getDocMarkup());
49915 var task = { // must defer to wait for browser to be ready
49917 //console.log("run task?" + this.doc.readyState);
49918 this.assignDocWin();
49919 if(this.doc.body || this.doc.readyState == 'complete'){
49921 this.doc.designMode="on";
49926 Roo.TaskMgr.stop(task);
49927 this.initEditor.defer(10, this);
49934 Roo.TaskMgr.start(task);
49939 onResize : function(w, h)
49941 Roo.log('resize: ' +w + ',' + h );
49942 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
49946 if(typeof w == 'number'){
49948 this.iframe.style.width = w + 'px';
49950 if(typeof h == 'number'){
49952 this.iframe.style.height = h + 'px';
49954 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
49961 * Toggles the editor between standard and source edit mode.
49962 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49964 toggleSourceEdit : function(sourceEditMode){
49966 this.sourceEditMode = sourceEditMode === true;
49968 if(this.sourceEditMode){
49970 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
49973 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
49974 //this.iframe.className = '';
49977 //this.setSize(this.owner.wrap.getSize());
49978 //this.fireEvent('editmodechange', this, this.sourceEditMode);
49985 * Protected method that will not generally be called directly. If you need/want
49986 * custom HTML cleanup, this is the method you should override.
49987 * @param {String} html The HTML to be cleaned
49988 * return {String} The cleaned HTML
49990 cleanHtml : function(html)
49992 html = String(html);
49993 if(html.length > 5){
49994 if(Roo.isSafari){ // strip safari nonsense
49995 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
49998 if(html == ' '){
50005 * HTML Editor -> Textarea
50006 * Protected method that will not generally be called directly. Syncs the contents
50007 * of the editor iframe with the textarea.
50009 syncValue : function()
50011 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50012 if(this.initialized){
50014 if (this.undoManager) {
50015 this.undoManager.addEvent();
50019 var bd = (this.doc.body || this.doc.documentElement);
50022 var sel = this.win.getSelection();
50024 var div = document.createElement('div');
50025 div.innerHTML = bd.innerHTML;
50026 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50027 if (gtx.length > 0) {
50028 var rm = gtx.item(0).parentNode;
50029 rm.parentNode.removeChild(rm);
50033 if (this.enableBlocks) {
50034 new Roo.htmleditor.FilterBlock({ node : div });
50037 var tidy = new Roo.htmleditor.TidySerializer({
50040 var html = tidy.serialize(div);
50044 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50045 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50047 html = '<div style="'+m[0]+'">' + html + '</div>';
50050 html = this.cleanHtml(html);
50051 // fix up the special chars.. normaly like back quotes in word...
50052 // however we do not want to do this with chinese..
50053 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50055 var cc = match.charCodeAt();
50057 // Get the character value, handling surrogate pairs
50058 if (match.length == 2) {
50059 // It's a surrogate pair, calculate the Unicode code point
50060 var high = match.charCodeAt(0) - 0xD800;
50061 var low = match.charCodeAt(1) - 0xDC00;
50062 cc = (high * 0x400) + low + 0x10000;
50064 (cc >= 0x4E00 && cc < 0xA000 ) ||
50065 (cc >= 0x3400 && cc < 0x4E00 ) ||
50066 (cc >= 0xf900 && cc < 0xfb00 )
50071 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50072 return "&#" + cc + ";";
50079 if(this.owner.fireEvent('beforesync', this, html) !== false){
50080 this.el.dom.value = html;
50081 this.owner.fireEvent('sync', this, html);
50087 * TEXTAREA -> EDITABLE
50088 * Protected method that will not generally be called directly. Pushes the value of the textarea
50089 * into the iframe editor.
50091 pushValue : function()
50093 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50094 if(this.initialized){
50095 var v = this.el.dom.value.trim();
50098 if(this.owner.fireEvent('beforepush', this, v) !== false){
50099 var d = (this.doc.body || this.doc.documentElement);
50102 this.el.dom.value = d.innerHTML;
50103 this.owner.fireEvent('push', this, v);
50105 if (this.autoClean) {
50106 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50107 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50109 if (this.enableBlocks) {
50110 Roo.htmleditor.Block.initAll(this.doc.body);
50113 this.updateLanguage();
50115 var lc = this.doc.body.lastChild;
50116 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50117 // add an extra line at the end.
50118 this.doc.body.appendChild(this.doc.createElement('br'));
50126 deferFocus : function(){
50127 this.focus.defer(10, this);
50131 focus : function(){
50132 if(this.win && !this.sourceEditMode){
50139 assignDocWin: function()
50141 var iframe = this.iframe;
50144 this.doc = iframe.contentWindow.document;
50145 this.win = iframe.contentWindow;
50147 // if (!Roo.get(this.frameId)) {
50150 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50151 // this.win = Roo.get(this.frameId).dom.contentWindow;
50153 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50157 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50158 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50163 initEditor : function(){
50164 //console.log("INIT EDITOR");
50165 this.assignDocWin();
50169 this.doc.designMode="on";
50171 this.doc.write(this.getDocMarkup());
50174 var dbody = (this.doc.body || this.doc.documentElement);
50175 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50176 // this copies styles from the containing element into thsi one..
50177 // not sure why we need all of this..
50178 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50180 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50181 //ss['background-attachment'] = 'fixed'; // w3c
50182 dbody.bgProperties = 'fixed'; // ie
50183 dbody.setAttribute("translate", "no");
50185 //Roo.DomHelper.applyStyles(dbody, ss);
50186 Roo.EventManager.on(this.doc, {
50188 'mouseup': this.onEditorEvent,
50189 'dblclick': this.onEditorEvent,
50190 'click': this.onEditorEvent,
50191 'keyup': this.onEditorEvent,
50196 Roo.EventManager.on(this.doc, {
50197 'paste': this.onPasteEvent,
50201 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50204 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50205 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50207 this.initialized = true;
50210 // initialize special key events - enter
50211 new Roo.htmleditor.KeyEnter({core : this});
50215 this.owner.fireEvent('initialize', this);
50218 // this is to prevent a href clicks resulting in a redirect?
50220 onPasteEvent : function(e,v)
50222 // I think we better assume paste is going to be a dirty load of rubish from word..
50224 // even pasting into a 'email version' of this widget will have to clean up that mess.
50225 var cd = (e.browserEvent.clipboardData || window.clipboardData);
50227 // check what type of paste - if it's an image, then handle it differently.
50228 if (cd.files && cd.files.length > 0) {
50230 var urlAPI = (window.createObjectURL && window) ||
50231 (window.URL && URL.revokeObjectURL && URL) ||
50232 (window.webkitURL && webkitURL);
50234 var url = urlAPI.createObjectURL( cd.files[0]);
50235 this.insertAtCursor('<img src=" + url + ">');
50238 if (cd.types.indexOf('text/html') < 0 ) {
50242 var html = cd.getData('text/html'); // clipboard event
50243 if (cd.types.indexOf('text/rtf') > -1) {
50244 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50245 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50250 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50251 .map(function(g) { return g.toDataURL(); })
50252 .filter(function(g) { return g != 'about:blank'; });
50255 html = this.cleanWordChars(html);
50257 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50260 var sn = this.getParentElement();
50261 // check if d contains a table, and prevent nesting??
50262 //Roo.log(d.getElementsByTagName('table'));
50264 //Roo.log(sn.closest('table'));
50265 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50266 e.preventDefault();
50267 this.insertAtCursor("You can not nest tables");
50268 //Roo.log("prevent?"); // fixme -
50272 if (images.length > 0) {
50273 Roo.each(d.getElementsByTagName('img'), function(img, i) {
50274 img.setAttribute('src', images[i]);
50277 if (this.autoClean) {
50278 new Roo.htmleditor.FilterWord({ node : d });
50280 new Roo.htmleditor.FilterStyleToTag({ node : d });
50281 new Roo.htmleditor.FilterAttributes({
50283 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
50284 attrib_clean : ['href', 'src' ]
50286 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50287 // should be fonts..
50288 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
50289 new Roo.htmleditor.FilterParagraph({ node : d });
50290 new Roo.htmleditor.FilterSpan({ node : d });
50291 new Roo.htmleditor.FilterLongBr({ node : d });
50292 new Roo.htmleditor.FilterComment({ node : d });
50296 if (this.enableBlocks) {
50298 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50299 if (img.closest('figure')) { // assume!! that it's aready
50302 var fig = new Roo.htmleditor.BlockFigure({
50303 image_src : img.src
50305 fig.updateElement(img); // replace it..
50311 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
50312 if (this.enableBlocks) {
50313 Roo.htmleditor.Block.initAll(this.doc.body);
50317 e.preventDefault();
50319 // default behaveiour should be our local cleanup paste? (optional?)
50320 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50321 //this.owner.fireEvent('paste', e, v);
50324 onDestroy : function(){
50330 //for (var i =0; i < this.toolbars.length;i++) {
50331 // // fixme - ask toolbars for heights?
50332 // this.toolbars[i].onDestroy();
50335 //this.wrap.dom.innerHTML = '';
50336 //this.wrap.remove();
50341 onFirstFocus : function(){
50343 this.assignDocWin();
50344 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50346 this.activated = true;
50349 if(Roo.isGecko){ // prevent silly gecko errors
50351 var s = this.win.getSelection();
50352 if(!s.focusNode || s.focusNode.nodeType != 3){
50353 var r = s.getRangeAt(0);
50354 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50359 this.execCmd('useCSS', true);
50360 this.execCmd('styleWithCSS', false);
50363 this.owner.fireEvent('activate', this);
50367 adjustFont: function(btn){
50368 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50369 //if(Roo.isSafari){ // safari
50372 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50373 if(Roo.isSafari){ // safari
50374 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50375 v = (v < 10) ? 10 : v;
50376 v = (v > 48) ? 48 : v;
50377 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50382 v = Math.max(1, v+adjust);
50384 this.execCmd('FontSize', v );
50387 onEditorEvent : function(e)
50391 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50392 return; // we do not handle this.. (undo manager does..)
50394 // in theory this detects if the last element is not a br, then we try and do that.
50395 // its so clicking in space at bottom triggers adding a br and moving the cursor.
50397 e.target.nodeName == 'BODY' &&
50398 e.type == "mouseup" &&
50399 this.doc.body.lastChild
50401 var lc = this.doc.body.lastChild;
50402 // gtx-trans is google translate plugin adding crap.
50403 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50404 lc = lc.previousSibling;
50406 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50407 // if last element is <BR> - then dont do anything.
50409 var ns = this.doc.createElement('br');
50410 this.doc.body.appendChild(ns);
50411 range = this.doc.createRange();
50412 range.setStartAfter(ns);
50413 range.collapse(true);
50414 var sel = this.win.getSelection();
50415 sel.removeAllRanges();
50416 sel.addRange(range);
50422 this.fireEditorEvent(e);
50423 // this.updateToolbar();
50424 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50427 fireEditorEvent: function(e)
50429 this.owner.fireEvent('editorevent', this, e);
50432 insertTag : function(tg)
50434 // could be a bit smarter... -> wrap the current selected tRoo..
50435 if (tg.toLowerCase() == 'span' ||
50436 tg.toLowerCase() == 'code' ||
50437 tg.toLowerCase() == 'sup' ||
50438 tg.toLowerCase() == 'sub'
50441 range = this.createRange(this.getSelection());
50442 var wrappingNode = this.doc.createElement(tg.toLowerCase());
50443 wrappingNode.appendChild(range.extractContents());
50444 range.insertNode(wrappingNode);
50451 this.execCmd("formatblock", tg);
50452 this.undoManager.addEvent();
50455 insertText : function(txt)
50459 var range = this.createRange();
50460 range.deleteContents();
50461 //alert(Sender.getAttribute('label'));
50463 range.insertNode(this.doc.createTextNode(txt));
50464 this.undoManager.addEvent();
50470 * Executes a Midas editor command on the editor document and performs necessary focus and
50471 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50472 * @param {String} cmd The Midas command
50473 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50475 relayCmd : function(cmd, value)
50479 case 'justifyleft':
50480 case 'justifyright':
50481 case 'justifycenter':
50482 // if we are in a cell, then we will adjust the
50483 var n = this.getParentElement();
50484 var td = n.closest('td');
50486 var bl = Roo.htmleditor.Block.factory(td);
50487 bl.textAlign = cmd.replace('justify','');
50488 bl.updateElement();
50489 this.owner.fireEvent('editorevent', this);
50492 this.execCmd('styleWithCSS', true); //
50496 // if there is no selection, then we insert, and set the curson inside it..
50497 this.execCmd('styleWithCSS', false);
50507 this.execCmd(cmd, value);
50508 this.owner.fireEvent('editorevent', this);
50509 //this.updateToolbar();
50510 this.owner.deferFocus();
50514 * Executes a Midas editor command directly on the editor document.
50515 * For visual commands, you should use {@link #relayCmd} instead.
50516 * <b>This should only be called after the editor is initialized.</b>
50517 * @param {String} cmd The Midas command
50518 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50520 execCmd : function(cmd, value){
50521 this.doc.execCommand(cmd, false, value === undefined ? null : value);
50528 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50530 * @param {String} text | dom node..
50532 insertAtCursor : function(text)
50535 if(!this.activated){
50539 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50543 // from jquery ui (MIT licenced)
50545 var win = this.win;
50547 if (win.getSelection && win.getSelection().getRangeAt) {
50549 // delete the existing?
50551 this.createRange(this.getSelection()).deleteContents();
50552 range = win.getSelection().getRangeAt(0);
50553 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50554 range.insertNode(node);
50555 range = range.cloneRange();
50556 range.collapse(false);
50558 win.getSelection().removeAllRanges();
50559 win.getSelection().addRange(range);
50563 } else if (win.document.selection && win.document.selection.createRange) {
50564 // no firefox support
50565 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50566 win.document.selection.createRange().pasteHTML(txt);
50569 // no firefox support
50570 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50571 this.execCmd('InsertHTML', txt);
50579 mozKeyPress : function(e){
50581 var c = e.getCharCode(), cmd;
50584 c = String.fromCharCode(c).toLowerCase();
50598 // this.cleanUpPaste.defer(100, this);
50604 this.relayCmd(cmd);
50605 //this.win.focus();
50606 //this.execCmd(cmd);
50607 //this.deferFocus();
50608 e.preventDefault();
50616 fixKeys : function(){ // load time branching for fastest keydown performance
50620 return function(e){
50621 var k = e.getKey(), r;
50624 r = this.doc.selection.createRange();
50627 r.pasteHTML('    ');
50632 /// this is handled by Roo.htmleditor.KeyEnter
50635 r = this.doc.selection.createRange();
50637 var target = r.parentElement();
50638 if(!target || target.tagName.toLowerCase() != 'li'){
50640 r.pasteHTML('<br/>');
50647 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50648 // this.cleanUpPaste.defer(100, this);
50654 }else if(Roo.isOpera){
50655 return function(e){
50656 var k = e.getKey();
50660 this.execCmd('InsertHTML','    ');
50664 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50665 // this.cleanUpPaste.defer(100, this);
50670 }else if(Roo.isSafari){
50671 return function(e){
50672 var k = e.getKey();
50676 this.execCmd('InsertText','\t');
50680 this.mozKeyPress(e);
50682 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50683 // this.cleanUpPaste.defer(100, this);
50691 getAllAncestors: function()
50693 var p = this.getSelectedNode();
50696 a.push(p); // push blank onto stack..
50697 p = this.getParentElement();
50701 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50705 a.push(this.doc.body);
50709 lastSelNode : false,
50712 getSelection : function()
50714 this.assignDocWin();
50715 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50718 * Select a dom node
50719 * @param {DomElement} node the node to select
50721 selectNode : function(node, collapse)
50723 var nodeRange = node.ownerDocument.createRange();
50725 nodeRange.selectNode(node);
50727 nodeRange.selectNodeContents(node);
50729 if (collapse === true) {
50730 nodeRange.collapse(true);
50733 var s = this.win.getSelection();
50734 s.removeAllRanges();
50735 s.addRange(nodeRange);
50738 getSelectedNode: function()
50740 // this may only work on Gecko!!!
50742 // should we cache this!!!!
50746 var range = this.createRange(this.getSelection()).cloneRange();
50749 var parent = range.parentElement();
50751 var testRange = range.duplicate();
50752 testRange.moveToElementText(parent);
50753 if (testRange.inRange(range)) {
50756 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
50759 parent = parent.parentElement;
50764 // is ancestor a text element.
50765 var ac = range.commonAncestorContainer;
50766 if (ac.nodeType == 3) {
50767 ac = ac.parentNode;
50770 var ar = ac.childNodes;
50773 var other_nodes = [];
50774 var has_other_nodes = false;
50775 for (var i=0;i<ar.length;i++) {
50776 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
50779 // fullly contained node.
50781 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
50786 // probably selected..
50787 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
50788 other_nodes.push(ar[i]);
50792 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
50797 has_other_nodes = true;
50799 if (!nodes.length && other_nodes.length) {
50800 nodes= other_nodes;
50802 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
50810 createRange: function(sel)
50812 // this has strange effects when using with
50813 // top toolbar - not sure if it's a great idea.
50814 //this.editor.contentWindow.focus();
50815 if (typeof sel != "undefined") {
50817 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
50819 return this.doc.createRange();
50822 return this.doc.createRange();
50825 getParentElement: function()
50828 this.assignDocWin();
50829 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
50831 var range = this.createRange(sel);
50834 var p = range.commonAncestorContainer;
50835 while (p.nodeType == 3) { // text node
50846 * Range intersection.. the hard stuff...
50850 * [ -- selected range --- ]
50854 * if end is before start or hits it. fail.
50855 * if start is after end or hits it fail.
50857 * if either hits (but other is outside. - then it's not
50863 // @see http://www.thismuchiknow.co.uk/?p=64.
50864 rangeIntersectsNode : function(range, node)
50866 var nodeRange = node.ownerDocument.createRange();
50868 nodeRange.selectNode(node);
50870 nodeRange.selectNodeContents(node);
50873 var rangeStartRange = range.cloneRange();
50874 rangeStartRange.collapse(true);
50876 var rangeEndRange = range.cloneRange();
50877 rangeEndRange.collapse(false);
50879 var nodeStartRange = nodeRange.cloneRange();
50880 nodeStartRange.collapse(true);
50882 var nodeEndRange = nodeRange.cloneRange();
50883 nodeEndRange.collapse(false);
50885 return rangeStartRange.compareBoundaryPoints(
50886 Range.START_TO_START, nodeEndRange) == -1 &&
50887 rangeEndRange.compareBoundaryPoints(
50888 Range.START_TO_START, nodeStartRange) == 1;
50892 rangeCompareNode : function(range, node)
50894 var nodeRange = node.ownerDocument.createRange();
50896 nodeRange.selectNode(node);
50898 nodeRange.selectNodeContents(node);
50902 range.collapse(true);
50904 nodeRange.collapse(true);
50906 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
50907 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
50909 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
50911 var nodeIsBefore = ss == 1;
50912 var nodeIsAfter = ee == -1;
50914 if (nodeIsBefore && nodeIsAfter) {
50917 if (!nodeIsBefore && nodeIsAfter) {
50918 return 1; //right trailed.
50921 if (nodeIsBefore && !nodeIsAfter) {
50922 return 2; // left trailed.
50928 cleanWordChars : function(input) {// change the chars to hex code
50931 [ 8211, "–" ],
50932 [ 8212, "—" ],
50940 var output = input;
50941 Roo.each(swapCodes, function(sw) {
50942 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
50944 output = output.replace(swapper, sw[1]);
50954 cleanUpChild : function (node)
50957 new Roo.htmleditor.FilterComment({node : node});
50958 new Roo.htmleditor.FilterAttributes({
50960 attrib_black : this.ablack,
50961 attrib_clean : this.aclean,
50962 style_white : this.cwhite,
50963 style_black : this.cblack
50965 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
50966 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
50972 * Clean up MS wordisms...
50973 * @deprecated - use filter directly
50975 cleanWord : function(node)
50977 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
50984 * @deprecated - use filters
50986 cleanTableWidths : function(node)
50988 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
50995 applyBlacklists : function()
50997 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
50998 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
51000 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
51001 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
51002 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
51006 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51007 if (b.indexOf(tag) > -1) {
51010 this.white.push(tag);
51014 Roo.each(w, function(tag) {
51015 if (b.indexOf(tag) > -1) {
51018 if (this.white.indexOf(tag) > -1) {
51021 this.white.push(tag);
51026 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51027 if (w.indexOf(tag) > -1) {
51030 this.black.push(tag);
51034 Roo.each(b, function(tag) {
51035 if (w.indexOf(tag) > -1) {
51038 if (this.black.indexOf(tag) > -1) {
51041 this.black.push(tag);
51046 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
51047 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
51051 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51052 if (b.indexOf(tag) > -1) {
51055 this.cwhite.push(tag);
51059 Roo.each(w, function(tag) {
51060 if (b.indexOf(tag) > -1) {
51063 if (this.cwhite.indexOf(tag) > -1) {
51066 this.cwhite.push(tag);
51071 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51072 if (w.indexOf(tag) > -1) {
51075 this.cblack.push(tag);
51079 Roo.each(b, function(tag) {
51080 if (w.indexOf(tag) > -1) {
51083 if (this.cblack.indexOf(tag) > -1) {
51086 this.cblack.push(tag);
51091 setStylesheets : function(stylesheets)
51093 if(typeof(stylesheets) == 'string'){
51094 Roo.get(this.iframe.contentDocument.head).createChild({
51096 rel : 'stylesheet',
51105 Roo.each(stylesheets, function(s) {
51110 Roo.get(_this.iframe.contentDocument.head).createChild({
51112 rel : 'stylesheet',
51122 updateLanguage : function()
51124 if (!this.iframe || !this.iframe.contentDocument) {
51127 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51131 removeStylesheets : function()
51135 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51140 setStyle : function(style)
51142 Roo.get(this.iframe.contentDocument.head).createChild({
51151 // hide stuff that is not compatible
51165 * @event specialkey
51169 * @cfg {String} fieldClass @hide
51172 * @cfg {String} focusClass @hide
51175 * @cfg {String} autoCreate @hide
51178 * @cfg {String} inputType @hide
51181 * @cfg {String} invalidClass @hide
51184 * @cfg {String} invalidText @hide
51187 * @cfg {String} msgFx @hide
51190 * @cfg {String} validateOnBlur @hide
51194 Roo.HtmlEditorCore.white = [
51195 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51197 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
51198 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
51199 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
51200 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
51201 'TABLE', 'UL', 'XMP',
51203 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
51206 'DIR', 'MENU', 'OL', 'UL', 'DL',
51212 Roo.HtmlEditorCore.black = [
51213 // 'embed', 'object', // enable - backend responsiblity to clean thiese
51215 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
51216 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
51217 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
51218 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
51219 //'FONT' // CLEAN LATER..
51220 'COLGROUP', 'COL' // messy tables.
51224 Roo.HtmlEditorCore.clean = [ // ?? needed???
51225 'SCRIPT', 'STYLE', 'TITLE', 'XML'
51227 Roo.HtmlEditorCore.tag_remove = [
51232 Roo.HtmlEditorCore.ablack = [
51236 Roo.HtmlEditorCore.aclean = [
51237 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
51241 Roo.HtmlEditorCore.pwhite= [
51242 'http', 'https', 'mailto'
51245 // white listed style attributes.
51246 Roo.HtmlEditorCore.cwhite= [
51247 // 'text-align', /// default is to allow most things..
51253 // black listed style attributes.
51254 Roo.HtmlEditorCore.cblack= [
51255 // 'font-size' -- this can be set by the project
51261 //<script type="text/javascript">
51264 * Ext JS Library 1.1.1
51265 * Copyright(c) 2006-2007, Ext JS, LLC.
51271 Roo.form.HtmlEditor = function(config){
51275 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51277 if (!this.toolbars) {
51278 this.toolbars = [];
51280 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51286 * @class Roo.form.HtmlEditor
51287 * @extends Roo.form.Field
51288 * Provides a lightweight HTML Editor component.
51290 * This has been tested on Fireforx / Chrome.. IE may not be so great..
51292 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51293 * supported by this editor.</b><br/><br/>
51294 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51295 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51297 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51299 * @cfg {Boolean} clearUp
51303 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51308 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
51313 * @cfg {Number} height (in pixels)
51317 * @cfg {Number} width (in pixels)
51322 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
51325 stylesheets: false,
51329 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51334 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51340 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51345 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51350 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51352 allowComments: false,
51354 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51356 enableBlocks : true,
51359 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51360 * if you are doing an email editor, this probably needs disabling, it's designed
51364 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51368 * @cfg {String} language default en - language of text (usefull for rtl languages)
51377 // private properties
51378 validationEvent : false,
51380 initialized : false,
51383 onFocus : Roo.emptyFn,
51385 hideMode:'offsets',
51387 actionMode : 'container', // defaults to hiding it...
51389 defaultAutoCreate : { // modified by initCompnoent..
51391 style:"width:500px;height:300px;",
51392 autocomplete: "new-password"
51396 initComponent : function(){
51399 * @event initialize
51400 * Fires when the editor is fully initialized (including the iframe)
51401 * @param {HtmlEditor} this
51406 * Fires when the editor is first receives the focus. Any insertion must wait
51407 * until after this event.
51408 * @param {HtmlEditor} this
51412 * @event beforesync
51413 * Fires before the textarea is updated with content from the editor iframe. Return false
51414 * to cancel the sync.
51415 * @param {HtmlEditor} this
51416 * @param {String} html
51420 * @event beforepush
51421 * Fires before the iframe editor is updated with content from the textarea. Return false
51422 * to cancel the push.
51423 * @param {HtmlEditor} this
51424 * @param {String} html
51429 * Fires when the textarea is updated with content from the editor iframe.
51430 * @param {HtmlEditor} this
51431 * @param {String} html
51436 * Fires when the iframe editor is updated with content from the textarea.
51437 * @param {HtmlEditor} this
51438 * @param {String} html
51442 * @event editmodechange
51443 * Fires when the editor switches edit modes
51444 * @param {HtmlEditor} this
51445 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51447 editmodechange: true,
51449 * @event editorevent
51450 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51451 * @param {HtmlEditor} this
51455 * @event firstfocus
51456 * Fires when on first focus - needed by toolbars..
51457 * @param {HtmlEditor} this
51462 * Auto save the htmlEditor value as a file into Events
51463 * @param {HtmlEditor} this
51467 * @event savedpreview
51468 * preview the saved version of htmlEditor
51469 * @param {HtmlEditor} this
51471 savedpreview: true,
51474 * @event stylesheetsclick
51475 * Fires when press the Sytlesheets button
51476 * @param {Roo.HtmlEditorCore} this
51478 stylesheetsclick: true,
51481 * Fires when press user pastes into the editor
51482 * @param {Roo.HtmlEditorCore} this
51486 this.defaultAutoCreate = {
51488 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51489 autocomplete: "new-password"
51494 * Protected method that will not generally be called directly. It
51495 * is called when the editor creates its toolbar. Override this method if you need to
51496 * add custom toolbar buttons.
51497 * @param {HtmlEditor} editor
51499 createToolbar : function(editor){
51500 Roo.log("create toolbars");
51501 if (!editor.toolbars || !editor.toolbars.length) {
51502 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51505 for (var i =0 ; i < editor.toolbars.length;i++) {
51506 editor.toolbars[i] = Roo.factory(
51507 typeof(editor.toolbars[i]) == 'string' ?
51508 { xtype: editor.toolbars[i]} : editor.toolbars[i],
51509 Roo.form.HtmlEditor);
51510 editor.toolbars[i].init(editor);
51516 * get the Context selected node
51517 * @returns {DomElement|boolean} selected node if active or false if none
51520 getSelectedNode : function()
51522 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51525 return this.toolbars[1].tb.selectedNode;
51529 onRender : function(ct, position)
51532 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51534 this.wrap = this.el.wrap({
51535 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51538 this.editorcore.onRender(ct, position);
51540 if (this.resizable) {
51541 this.resizeEl = new Roo.Resizable(this.wrap, {
51545 minHeight : this.height,
51546 height: this.height,
51547 handles : this.resizable,
51550 resize : function(r, w, h) {
51551 _t.onResize(w,h); // -something
51557 this.createToolbar(this);
51561 this.setSize(this.wrap.getSize());
51563 if (this.resizeEl) {
51564 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51565 // should trigger onReize..
51568 this.keyNav = new Roo.KeyNav(this.el, {
51570 "tab" : function(e){
51571 e.preventDefault();
51573 var value = this.getValue();
51575 var start = this.el.dom.selectionStart;
51576 var end = this.el.dom.selectionEnd;
51580 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51581 this.el.dom.setSelectionRange(end + 1, end + 1);
51585 var f = value.substring(0, start).split("\t");
51587 if(f.pop().length != 0){
51591 this.setValue(f.join("\t") + value.substring(end));
51592 this.el.dom.setSelectionRange(start - 1, start - 1);
51596 "home" : function(e){
51597 e.preventDefault();
51599 var curr = this.el.dom.selectionStart;
51600 var lines = this.getValue().split("\n");
51607 this.el.dom.setSelectionRange(0, 0);
51613 for (var i = 0; i < lines.length;i++) {
51614 pos += lines[i].length;
51624 pos -= lines[i].length;
51630 this.el.dom.setSelectionRange(pos, pos);
51634 this.el.dom.selectionStart = pos;
51635 this.el.dom.selectionEnd = curr;
51638 "end" : function(e){
51639 e.preventDefault();
51641 var curr = this.el.dom.selectionStart;
51642 var lines = this.getValue().split("\n");
51649 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51655 for (var i = 0; i < lines.length;i++) {
51657 pos += lines[i].length;
51671 this.el.dom.setSelectionRange(pos, pos);
51675 this.el.dom.selectionStart = curr;
51676 this.el.dom.selectionEnd = pos;
51681 doRelay : function(foo, bar, hname){
51682 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51688 // if(this.autosave && this.w){
51689 // this.autoSaveFn = setInterval(this.autosave, 1000);
51694 onResize : function(w, h)
51696 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51701 if(typeof w == 'number'){
51702 var aw = w - this.wrap.getFrameWidth('lr');
51703 this.el.setWidth(this.adjustWidth('textarea', aw));
51706 if(typeof h == 'number'){
51708 for (var i =0; i < this.toolbars.length;i++) {
51709 // fixme - ask toolbars for heights?
51710 tbh += this.toolbars[i].tb.el.getHeight();
51711 if (this.toolbars[i].footer) {
51712 tbh += this.toolbars[i].footer.el.getHeight();
51719 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51720 ah -= 5; // knock a few pixes off for look..
51722 this.el.setHeight(this.adjustWidth('textarea', ah));
51726 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51727 this.editorcore.onResize(ew,eh);
51732 * Toggles the editor between standard and source edit mode.
51733 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51735 toggleSourceEdit : function(sourceEditMode)
51737 this.editorcore.toggleSourceEdit(sourceEditMode);
51739 if(this.editorcore.sourceEditMode){
51740 Roo.log('editor - showing textarea');
51743 // Roo.log(this.syncValue());
51744 this.editorcore.syncValue();
51745 this.el.removeClass('x-hidden');
51746 this.el.dom.removeAttribute('tabIndex');
51748 this.el.dom.scrollTop = 0;
51751 for (var i = 0; i < this.toolbars.length; i++) {
51752 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51753 this.toolbars[i].tb.hide();
51754 this.toolbars[i].footer.hide();
51759 Roo.log('editor - hiding textarea');
51761 // Roo.log(this.pushValue());
51762 this.editorcore.pushValue();
51764 this.el.addClass('x-hidden');
51765 this.el.dom.setAttribute('tabIndex', -1);
51767 for (var i = 0; i < this.toolbars.length; i++) {
51768 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51769 this.toolbars[i].tb.show();
51770 this.toolbars[i].footer.show();
51774 //this.deferFocus();
51777 this.setSize(this.wrap.getSize());
51778 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
51780 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
51783 // private (for BoxComponent)
51784 adjustSize : Roo.BoxComponent.prototype.adjustSize,
51786 // private (for BoxComponent)
51787 getResizeEl : function(){
51791 // private (for BoxComponent)
51792 getPositionEl : function(){
51797 initEvents : function(){
51798 this.originalValue = this.getValue();
51802 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51805 markInvalid : Roo.emptyFn,
51807 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51810 clearInvalid : Roo.emptyFn,
51812 setValue : function(v){
51813 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
51814 this.editorcore.pushValue();
51818 * update the language in the body - really done by core
51819 * @param {String} language - eg. en / ar / zh-CN etc..
51821 updateLanguage : function(lang)
51823 this.language = lang;
51824 this.editorcore.language = lang;
51825 this.editorcore.updateLanguage();
51829 deferFocus : function(){
51830 this.focus.defer(10, this);
51834 focus : function(){
51835 this.editorcore.focus();
51841 onDestroy : function(){
51847 for (var i =0; i < this.toolbars.length;i++) {
51848 // fixme - ask toolbars for heights?
51849 this.toolbars[i].onDestroy();
51852 this.wrap.dom.innerHTML = '';
51853 this.wrap.remove();
51858 onFirstFocus : function(){
51859 //Roo.log("onFirstFocus");
51860 this.editorcore.onFirstFocus();
51861 for (var i =0; i < this.toolbars.length;i++) {
51862 this.toolbars[i].onFirstFocus();
51868 syncValue : function()
51870 this.editorcore.syncValue();
51873 pushValue : function()
51875 this.editorcore.pushValue();
51878 setStylesheets : function(stylesheets)
51880 this.editorcore.setStylesheets(stylesheets);
51883 removeStylesheets : function()
51885 this.editorcore.removeStylesheets();
51889 // hide stuff that is not compatible
51903 * @event specialkey
51907 * @cfg {String} fieldClass @hide
51910 * @cfg {String} focusClass @hide
51913 * @cfg {String} autoCreate @hide
51916 * @cfg {String} inputType @hide
51919 * @cfg {String} invalidClass @hide
51922 * @cfg {String} invalidText @hide
51925 * @cfg {String} msgFx @hide
51928 * @cfg {String} validateOnBlur @hide
51934 * Ext JS Library 1.1.1
51935 * Copyright(c) 2006-2007, Ext JS, LLC.
51941 * @class Roo.form.HtmlEditor.ToolbarStandard
51946 new Roo.form.HtmlEditor({
51949 new Roo.form.HtmlEditorToolbar1({
51950 disable : { fonts: 1 , format: 1, ..., ... , ...],
51956 * @cfg {Object} disable List of elements to disable..
51957 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
51961 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
51964 Roo.form.HtmlEditor.ToolbarStandard = function(config)
51967 Roo.apply(this, config);
51969 // default disabled, based on 'good practice'..
51970 this.disable = this.disable || {};
51971 Roo.applyIf(this.disable, {
51974 specialElements : true
51978 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
51979 // dont call parent... till later.
51982 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
51989 editorcore : false,
51991 * @cfg {Object} disable List of toolbar elements to disable
51998 * @cfg {String} createLinkText The default text for the create link prompt
52000 createLinkText : 'Please enter the URL for the link:',
52002 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52004 defaultLinkValue : 'http:/'+'/',
52008 * @cfg {Array} fontFamilies An array of available font families
52026 // "á" , ?? a acute?
52031 "°" // , // degrees
52033 // "é" , // e ecute
52034 // "ú" , // u ecute?
52037 specialElements : [
52039 text: "Insert Table",
52042 ihtml : '<table><tr><td>Cell</td></tr></table>'
52046 text: "Insert Image",
52049 ihtml : '<img src="about:blank"/>'
52058 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
52059 "input:submit", "input:button", "select", "textarea", "label" ],
52062 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
52064 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52073 * @cfg {String} defaultFont default font to use.
52075 defaultFont: 'tahoma',
52077 fontSelect : false,
52080 formatCombo : false,
52082 init : function(editor)
52084 this.editor = editor;
52085 this.editorcore = editor.editorcore ? editor.editorcore : editor;
52086 var editorcore = this.editorcore;
52090 var fid = editorcore.frameId;
52092 function btn(id, toggle, handler){
52093 var xid = fid + '-'+ id ;
52097 cls : 'x-btn-icon x-edit-'+id,
52098 enableToggle:toggle !== false,
52099 scope: _t, // was editor...
52100 handler:handler||_t.relayBtnCmd,
52101 clickEvent:'mousedown',
52102 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52109 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52111 // stop form submits
52112 tb.el.on('click', function(e){
52113 e.preventDefault(); // what does this do?
52116 if(!this.disable.font) { // && !Roo.isSafari){
52117 /* why no safari for fonts
52118 editor.fontSelect = tb.el.createChild({
52121 cls:'x-font-select',
52122 html: this.createFontOptions()
52125 editor.fontSelect.on('change', function(){
52126 var font = editor.fontSelect.dom.value;
52127 editor.relayCmd('fontname', font);
52128 editor.deferFocus();
52132 editor.fontSelect.dom,
52138 if(!this.disable.formats){
52139 this.formatCombo = new Roo.form.ComboBox({
52140 store: new Roo.data.SimpleStore({
52143 data : this.formats // from states.js
52147 //autoCreate : {tag: "div", size: "20"},
52148 displayField:'tag',
52152 triggerAction: 'all',
52153 emptyText:'Add tag',
52154 selectOnFocus:true,
52157 'select': function(c, r, i) {
52158 editorcore.insertTag(r.get('tag'));
52164 tb.addField(this.formatCombo);
52168 if(!this.disable.format){
52173 btn('strikethrough')
52176 if(!this.disable.fontSize){
52181 btn('increasefontsize', false, editorcore.adjustFont),
52182 btn('decreasefontsize', false, editorcore.adjustFont)
52187 if(!this.disable.colors){
52190 id:editorcore.frameId +'-forecolor',
52191 cls:'x-btn-icon x-edit-forecolor',
52192 clickEvent:'mousedown',
52193 tooltip: this.buttonTips['forecolor'] || undefined,
52195 menu : new Roo.menu.ColorMenu({
52196 allowReselect: true,
52197 focus: Roo.emptyFn,
52200 selectHandler: function(cp, color){
52201 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52202 editor.deferFocus();
52205 clickEvent:'mousedown'
52208 id:editorcore.frameId +'backcolor',
52209 cls:'x-btn-icon x-edit-backcolor',
52210 clickEvent:'mousedown',
52211 tooltip: this.buttonTips['backcolor'] || undefined,
52213 menu : new Roo.menu.ColorMenu({
52214 focus: Roo.emptyFn,
52217 allowReselect: true,
52218 selectHandler: function(cp, color){
52220 editorcore.execCmd('useCSS', false);
52221 editorcore.execCmd('hilitecolor', color);
52222 editorcore.execCmd('useCSS', true);
52223 editor.deferFocus();
52225 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
52226 Roo.isSafari || Roo.isIE ? '#'+color : color);
52227 editor.deferFocus();
52231 clickEvent:'mousedown'
52236 // now add all the items...
52239 if(!this.disable.alignments){
52242 btn('justifyleft'),
52243 btn('justifycenter'),
52244 btn('justifyright')
52248 //if(!Roo.isSafari){
52249 if(!this.disable.links){
52252 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
52256 if(!this.disable.lists){
52259 btn('insertorderedlist'),
52260 btn('insertunorderedlist')
52263 if(!this.disable.sourceEdit){
52266 btn('sourceedit', true, function(btn){
52267 this.toggleSourceEdit(btn.pressed);
52274 // special menu.. - needs to be tidied up..
52275 if (!this.disable.special) {
52278 cls: 'x-edit-none',
52284 for (var i =0; i < this.specialChars.length; i++) {
52285 smenu.menu.items.push({
52287 html: this.specialChars[i],
52288 handler: function(a,b) {
52289 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52290 //editor.insertAtCursor(a.html);
52304 if (!this.disable.cleanStyles) {
52306 cls: 'x-btn-icon x-btn-clear',
52312 for (var i =0; i < this.cleanStyles.length; i++) {
52313 cmenu.menu.items.push({
52314 actiontype : this.cleanStyles[i],
52315 html: 'Remove ' + this.cleanStyles[i],
52316 handler: function(a,b) {
52319 var c = Roo.get(editorcore.doc.body);
52320 c.select('[style]').each(function(s) {
52321 s.dom.style.removeProperty(a.actiontype);
52323 editorcore.syncValue();
52328 cmenu.menu.items.push({
52329 actiontype : 'tablewidths',
52330 html: 'Remove Table Widths',
52331 handler: function(a,b) {
52332 editorcore.cleanTableWidths();
52333 editorcore.syncValue();
52337 cmenu.menu.items.push({
52338 actiontype : 'word',
52339 html: 'Remove MS Word Formating',
52340 handler: function(a,b) {
52341 editorcore.cleanWord();
52342 editorcore.syncValue();
52347 cmenu.menu.items.push({
52348 actiontype : 'all',
52349 html: 'Remove All Styles',
52350 handler: function(a,b) {
52352 var c = Roo.get(editorcore.doc.body);
52353 c.select('[style]').each(function(s) {
52354 s.dom.removeAttribute('style');
52356 editorcore.syncValue();
52361 cmenu.menu.items.push({
52362 actiontype : 'all',
52363 html: 'Remove All CSS Classes',
52364 handler: function(a,b) {
52366 var c = Roo.get(editorcore.doc.body);
52367 c.select('[class]').each(function(s) {
52368 s.dom.removeAttribute('class');
52370 editorcore.cleanWord();
52371 editorcore.syncValue();
52376 cmenu.menu.items.push({
52377 actiontype : 'tidy',
52378 html: 'Tidy HTML Source',
52379 handler: function(a,b) {
52380 new Roo.htmleditor.Tidy(editorcore.doc.body);
52381 editorcore.syncValue();
52390 if (!this.disable.specialElements) {
52393 cls: 'x-edit-none',
52398 for (var i =0; i < this.specialElements.length; i++) {
52399 semenu.menu.items.push(
52401 handler: function(a,b) {
52402 editor.insertAtCursor(this.ihtml);
52404 }, this.specialElements[i])
52416 for(var i =0; i< this.btns.length;i++) {
52417 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52418 b.cls = 'x-edit-none';
52420 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52421 b.cls += ' x-init-enable';
52424 b.scope = editorcore;
52432 // disable everything...
52434 this.tb.items.each(function(item){
52437 item.id != editorcore.frameId+ '-sourceedit' &&
52438 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52444 this.rendered = true;
52446 // the all the btns;
52447 editor.on('editorevent', this.updateToolbar, this);
52448 // other toolbars need to implement this..
52449 //editor.on('editmodechange', this.updateToolbar, this);
52453 relayBtnCmd : function(btn) {
52454 this.editorcore.relayCmd(btn.cmd);
52456 // private used internally
52457 createLink : function(){
52458 //Roo.log("create link?");
52459 var ec = this.editorcore;
52460 var ar = ec.getAllAncestors();
52462 for(var i = 0;i< ar.length;i++) {
52463 if (ar[i] && ar[i].nodeName == 'A') {
52471 Roo.MessageBox.show({
52472 title : "Add / Edit Link URL",
52473 msg : "Enter the url for the link",
52474 buttons: Roo.MessageBox.OKCANCEL,
52475 fn: function(btn, url){
52479 if(url && url != 'http:/'+'/'){
52481 n.setAttribute('href', url);
52483 ec.relayCmd('createlink', url);
52489 //multiline: multiline,
52491 value : n ? n.getAttribute('href') : ''
52495 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52501 * Protected method that will not generally be called directly. It triggers
52502 * a toolbar update by reading the markup state of the current selection in the editor.
52504 updateToolbar: function(){
52506 if(!this.editorcore.activated){
52507 this.editor.onFirstFocus();
52511 var btns = this.tb.items.map,
52512 doc = this.editorcore.doc,
52513 frameId = this.editorcore.frameId;
52515 if(!this.disable.font && !Roo.isSafari){
52517 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52518 if(name != this.fontSelect.dom.value){
52519 this.fontSelect.dom.value = name;
52523 if(!this.disable.format){
52524 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52525 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52526 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52527 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52529 if(!this.disable.alignments){
52530 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52531 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52532 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52534 if(!Roo.isSafari && !this.disable.lists){
52535 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52536 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52539 var ans = this.editorcore.getAllAncestors();
52540 if (this.formatCombo) {
52543 var store = this.formatCombo.store;
52544 this.formatCombo.setValue("");
52545 for (var i =0; i < ans.length;i++) {
52546 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52548 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52556 // hides menus... - so this cant be on a menu...
52557 Roo.menu.MenuMgr.hideAll();
52559 //this.editorsyncValue();
52563 createFontOptions : function(){
52564 var buf = [], fs = this.fontFamilies, ff, lc;
52568 for(var i = 0, len = fs.length; i< len; i++){
52570 lc = ff.toLowerCase();
52572 '<option value="',lc,'" style="font-family:',ff,';"',
52573 (this.defaultFont == lc ? ' selected="true">' : '>'),
52578 return buf.join('');
52581 toggleSourceEdit : function(sourceEditMode){
52583 Roo.log("toolbar toogle");
52584 if(sourceEditMode === undefined){
52585 sourceEditMode = !this.sourceEditMode;
52587 this.sourceEditMode = sourceEditMode === true;
52588 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52589 // just toggle the button?
52590 if(btn.pressed !== this.sourceEditMode){
52591 btn.toggle(this.sourceEditMode);
52595 if(sourceEditMode){
52596 Roo.log("disabling buttons");
52597 this.tb.items.each(function(item){
52598 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52604 Roo.log("enabling buttons");
52605 if(this.editorcore.initialized){
52606 this.tb.items.each(function(item){
52609 // initialize 'blocks'
52610 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52611 Roo.htmleditor.Block.factory(e).updateElement(e);
52617 Roo.log("calling toggole on editor");
52618 // tell the editor that it's been pressed..
52619 this.editor.toggleSourceEdit(sourceEditMode);
52623 * Object collection of toolbar tooltips for the buttons in the editor. The key
52624 * is the command id associated with that button and the value is a valid QuickTips object.
52629 title: 'Bold (Ctrl+B)',
52630 text: 'Make the selected text bold.',
52631 cls: 'x-html-editor-tip'
52634 title: 'Italic (Ctrl+I)',
52635 text: 'Make the selected text italic.',
52636 cls: 'x-html-editor-tip'
52644 title: 'Bold (Ctrl+B)',
52645 text: 'Make the selected text bold.',
52646 cls: 'x-html-editor-tip'
52649 title: 'Italic (Ctrl+I)',
52650 text: 'Make the selected text italic.',
52651 cls: 'x-html-editor-tip'
52654 title: 'Underline (Ctrl+U)',
52655 text: 'Underline the selected text.',
52656 cls: 'x-html-editor-tip'
52659 title: 'Strikethrough',
52660 text: 'Strikethrough the selected text.',
52661 cls: 'x-html-editor-tip'
52663 increasefontsize : {
52664 title: 'Grow Text',
52665 text: 'Increase the font size.',
52666 cls: 'x-html-editor-tip'
52668 decreasefontsize : {
52669 title: 'Shrink Text',
52670 text: 'Decrease the font size.',
52671 cls: 'x-html-editor-tip'
52674 title: 'Text Highlight Color',
52675 text: 'Change the background color of the selected text.',
52676 cls: 'x-html-editor-tip'
52679 title: 'Font Color',
52680 text: 'Change the color of the selected text.',
52681 cls: 'x-html-editor-tip'
52684 title: 'Align Text Left',
52685 text: 'Align text to the left.',
52686 cls: 'x-html-editor-tip'
52689 title: 'Center Text',
52690 text: 'Center text in the editor.',
52691 cls: 'x-html-editor-tip'
52694 title: 'Align Text Right',
52695 text: 'Align text to the right.',
52696 cls: 'x-html-editor-tip'
52698 insertunorderedlist : {
52699 title: 'Bullet List',
52700 text: 'Start a bulleted list.',
52701 cls: 'x-html-editor-tip'
52703 insertorderedlist : {
52704 title: 'Numbered List',
52705 text: 'Start a numbered list.',
52706 cls: 'x-html-editor-tip'
52709 title: 'Hyperlink',
52710 text: 'Make the selected text a hyperlink.',
52711 cls: 'x-html-editor-tip'
52714 title: 'Source Edit',
52715 text: 'Switch to source editing mode.',
52716 cls: 'x-html-editor-tip'
52720 onDestroy : function(){
52723 this.tb.items.each(function(item){
52725 item.menu.removeAll();
52727 item.menu.el.destroy();
52735 onFirstFocus: function() {
52736 this.tb.items.each(function(item){
52745 // <script type="text/javascript">
52748 * Ext JS Library 1.1.1
52749 * Copyright(c) 2006-2007, Ext JS, LLC.
52756 * @class Roo.form.HtmlEditor.ToolbarContext
52761 new Roo.form.HtmlEditor({
52764 { xtype: 'ToolbarStandard', styles : {} }
52765 { xtype: 'ToolbarContext', disable : {} }
52771 * @config : {Object} disable List of elements to disable.. (not done yet.)
52772 * @config : {Object} styles Map of styles available.
52776 Roo.form.HtmlEditor.ToolbarContext = function(config)
52779 Roo.apply(this, config);
52780 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52781 // dont call parent... till later.
52782 this.styles = this.styles || {};
52787 Roo.form.HtmlEditor.ToolbarContext.types = {
52802 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52828 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52899 name : 'selectoptions',
52905 // should we really allow this??
52906 // should this just be
52923 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
52924 Roo.form.HtmlEditor.ToolbarContext.stores = false;
52926 Roo.form.HtmlEditor.ToolbarContext.options = {
52928 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
52929 [ 'Courier New', 'Courier New'],
52930 [ 'Tahoma', 'Tahoma'],
52931 [ 'Times New Roman,serif', 'Times'],
52932 [ 'Verdana','Verdana' ]
52936 // fixme - these need to be configurable..
52939 //Roo.form.HtmlEditor.ToolbarContext.types
52942 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
52949 editorcore : false,
52951 * @cfg {Object} disable List of toolbar elements to disable
52956 * @cfg {Object} styles List of styles
52957 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
52959 * These must be defined in the page, so they get rendered correctly..
52970 init : function(editor)
52972 this.editor = editor;
52973 this.editorcore = editor.editorcore ? editor.editorcore : editor;
52974 var editorcore = this.editorcore;
52976 var fid = editorcore.frameId;
52978 function btn(id, toggle, handler){
52979 var xid = fid + '-'+ id ;
52983 cls : 'x-btn-icon x-edit-'+id,
52984 enableToggle:toggle !== false,
52985 scope: editorcore, // was editor...
52986 handler:handler||editorcore.relayBtnCmd,
52987 clickEvent:'mousedown',
52988 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52992 // create a new element.
52993 var wdiv = editor.wrap.createChild({
52995 }, editor.wrap.dom.firstChild.nextSibling, true);
52997 // can we do this more than once??
52999 // stop form submits
53002 // disable everything...
53003 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53004 this.toolbars = {};
53005 // block toolbars are built in updateToolbar when needed.
53006 for (var i in ty) {
53008 this.toolbars[i] = this.buildToolbar(ty[i],i);
53010 this.tb = this.toolbars.BODY;
53012 this.buildFooter();
53013 this.footer.show();
53014 editor.on('hide', function( ) { this.footer.hide() }, this);
53015 editor.on('show', function( ) { this.footer.show() }, this);
53018 this.rendered = true;
53020 // the all the btns;
53021 editor.on('editorevent', this.updateToolbar, this);
53022 // other toolbars need to implement this..
53023 //editor.on('editmodechange', this.updateToolbar, this);
53029 * Protected method that will not generally be called directly. It triggers
53030 * a toolbar update by reading the markup state of the current selection in the editor.
53032 * Note you can force an update by calling on('editorevent', scope, false)
53034 updateToolbar: function(editor ,ev, sel)
53038 ev.stopEvent(); // se if we can stop this looping with mutiple events.
53042 // capture mouse up - this is handy for selecting images..
53043 // perhaps should go somewhere else...
53044 if(!this.editorcore.activated){
53045 this.editor.onFirstFocus();
53048 //Roo.log(ev ? ev.target : 'NOTARGET');
53051 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53052 // selectNode - might want to handle IE?
53057 (ev.type == 'mouseup' || ev.type == 'click' ) &&
53058 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53059 // they have click on an image...
53060 // let's see if we can change the selection...
53063 // this triggers looping?
53064 //this.editorcore.selectNode(sel);
53068 // this forces an id..
53069 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53070 e.classList.remove('roo-ed-selection');
53072 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53073 //Roo.get(node).addClass('roo-ed-selection');
53075 //var updateFooter = sel ? false : true;
53078 var ans = this.editorcore.getAllAncestors();
53081 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53084 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
53085 sel = sel ? sel : this.editorcore.doc.body;
53086 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53090 var tn = sel.tagName.toUpperCase();
53091 var lastSel = this.tb.selectedNode;
53092 this.tb.selectedNode = sel;
53093 var left_label = tn;
53095 // ok see if we are editing a block?
53098 // you are not actually selecting the block.
53099 if (sel && sel.hasAttribute('data-block')) {
53101 } else if (sel && sel.closest('[data-block]')) {
53103 db = sel.closest('[data-block]');
53104 //var cepar = sel.closest('[contenteditable=true]');
53105 //if (db && cepar && cepar.tagName != 'BODY') {
53106 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53112 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53113 if (db && this.editorcore.enableBlocks) {
53114 block = Roo.htmleditor.Block.factory(db);
53119 db.classList.length > 0 ? db.className + ' ' : ''
53120 ) + 'roo-ed-selection';
53122 // since we removed it earlier... its not there..
53123 tn = 'BLOCK.' + db.getAttribute('data-block');
53125 //this.editorcore.selectNode(db);
53126 if (typeof(this.toolbars[tn]) == 'undefined') {
53127 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
53129 this.toolbars[tn].selectedNode = db;
53130 left_label = block.friendly_name;
53131 ans = this.editorcore.getAllAncestors();
53139 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53140 return; // no change?
53146 ///console.log("show: " + tn);
53147 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53151 this.tb.items.first().el.innerHTML = left_label + ': ';
53154 // update attributes
53155 if (block && this.tb.fields) {
53157 this.tb.fields.each(function(e) {
53158 e.setValue(block[e.name]);
53162 } else if (this.tb.fields && this.tb.selectedNode) {
53163 this.tb.fields.each( function(e) {
53165 e.setValue(this.tb.selectedNode.style[e.stylename]);
53168 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53170 this.updateToolbarStyles(this.tb.selectedNode);
53175 Roo.menu.MenuMgr.hideAll();
53180 // update the footer
53182 this.updateFooter(ans);
53186 updateToolbarStyles : function(sel)
53188 var hasStyles = false;
53189 for(var i in this.styles) {
53195 if (hasStyles && this.tb.hasStyles) {
53196 var st = this.tb.fields.item(0);
53198 st.store.removeAll();
53199 var cn = sel.className.split(/\s+/);
53202 if (this.styles['*']) {
53204 Roo.each(this.styles['*'], function(v) {
53205 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53208 if (this.styles[tn]) {
53209 Roo.each(this.styles[tn], function(v) {
53210 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53214 st.store.loadData(avs);
53221 updateFooter : function(ans)
53224 if (ans === false) {
53225 this.footDisp.dom.innerHTML = '';
53229 this.footerEls = ans.reverse();
53230 Roo.each(this.footerEls, function(a,i) {
53231 if (!a) { return; }
53232 html += html.length ? ' > ' : '';
53234 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53239 var sz = this.footDisp.up('td').getSize();
53240 this.footDisp.dom.style.width = (sz.width -10) + 'px';
53241 this.footDisp.dom.style.marginLeft = '5px';
53243 this.footDisp.dom.style.overflow = 'hidden';
53245 this.footDisp.dom.innerHTML = html;
53252 onDestroy : function(){
53255 this.tb.items.each(function(item){
53257 item.menu.removeAll();
53259 item.menu.el.destroy();
53267 onFirstFocus: function() {
53268 // need to do this for all the toolbars..
53269 this.tb.items.each(function(item){
53273 buildToolbar: function(tlist, nm, friendly_name, block)
53275 var editor = this.editor;
53276 var editorcore = this.editorcore;
53277 // create a new element.
53278 var wdiv = editor.wrap.createChild({
53280 }, editor.wrap.dom.firstChild.nextSibling, true);
53283 var tb = new Roo.Toolbar(wdiv);
53284 ///this.tb = tb; // << this sets the active toolbar..
53285 if (tlist === false && block) {
53286 tlist = block.contextMenu(this);
53289 tb.hasStyles = false;
53292 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
53294 var styles = Array.from(this.styles);
53298 if (styles && styles.length) {
53299 tb.hasStyles = true;
53300 // this needs a multi-select checkbox...
53301 tb.addField( new Roo.form.ComboBox({
53302 store: new Roo.data.SimpleStore({
53304 fields: ['val', 'selected'],
53307 name : '-roo-edit-className',
53308 attrname : 'className',
53309 displayField: 'val',
53313 triggerAction: 'all',
53314 emptyText:'Select Style',
53315 selectOnFocus:true,
53318 'select': function(c, r, i) {
53319 // initial support only for on class per el..
53320 tb.selectedNode.className = r ? r.get('val') : '';
53321 editorcore.syncValue();
53328 var tbc = Roo.form.HtmlEditor.ToolbarContext;
53331 for (var i = 0; i < tlist.length; i++) {
53333 // newer versions will use xtype cfg to create menus.
53334 if (typeof(tlist[i].xtype) != 'undefined') {
53336 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53342 var item = tlist[i];
53343 tb.add(item.title + ": ");
53346 //optname == used so you can configure the options available..
53347 var opts = item.opts ? item.opts : false;
53348 if (item.optname) { // use the b
53349 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53354 // opts == pulldown..
53355 tb.addField( new Roo.form.ComboBox({
53356 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53358 fields: ['val', 'display'],
53361 name : '-roo-edit-' + tlist[i].name,
53363 attrname : tlist[i].name,
53364 stylename : item.style ? item.style : false,
53366 displayField: item.displayField ? item.displayField : 'val',
53367 valueField : 'val',
53369 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
53371 triggerAction: 'all',
53372 emptyText:'Select',
53373 selectOnFocus:true,
53374 width: item.width ? item.width : 130,
53376 'select': function(c, r, i) {
53380 tb.selectedNode.style[c.stylename] = r.get('val');
53381 editorcore.syncValue();
53385 tb.selectedNode.removeAttribute(c.attrname);
53386 editorcore.syncValue();
53389 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53390 editorcore.syncValue();
53399 tb.addField( new Roo.form.TextField({
53402 //allowBlank:false,
53408 tb.addField( new Roo.form.TextField({
53409 name: '-roo-edit-' + tlist[i].name,
53410 attrname : tlist[i].name,
53416 'change' : function(f, nv, ov) {
53419 tb.selectedNode.setAttribute(f.attrname, nv);
53420 editorcore.syncValue();
53428 var show_delete = !block || block.deleteTitle !== false;
53430 show_delete = false;
53434 text: 'Stylesheets',
53437 click : function ()
53439 _this.editor.fireEvent('stylesheetsclick', _this.editor);
53448 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53451 click : function ()
53453 var sn = tb.selectedNode;
53455 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53461 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53462 if (sn.hasAttribute('data-block')) {
53463 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
53464 sn.parentNode.removeChild(sn);
53466 } else if (sn && sn.tagName != 'BODY') {
53467 // remove and keep parents.
53468 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53473 var range = editorcore.createRange();
53475 range.setStart(stn,0);
53476 range.setEnd(stn,0);
53477 var selection = editorcore.getSelection();
53478 selection.removeAllRanges();
53479 selection.addRange(range);
53482 //_this.updateToolbar(null, null, pn);
53483 _this.updateToolbar(null, null, null);
53484 _this.updateFooter(false);
53495 tb.el.on('click', function(e){
53496 e.preventDefault(); // what does this do?
53498 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53501 // dont need to disable them... as they will get hidden
53506 buildFooter : function()
53509 var fel = this.editor.wrap.createChild();
53510 this.footer = new Roo.Toolbar(fel);
53511 // toolbar has scrolly on left / right?
53512 var footDisp= new Roo.Toolbar.Fill();
53518 handler : function() {
53519 _t.footDisp.scrollTo('left',0,true)
53523 this.footer.add( footDisp );
53528 handler : function() {
53530 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53534 var fel = Roo.get(footDisp.el);
53535 fel.addClass('x-editor-context');
53536 this.footDispWrap = fel;
53537 this.footDispWrap.overflow = 'hidden';
53539 this.footDisp = fel.createChild();
53540 this.footDispWrap.on('click', this.onContextClick, this)
53544 // when the footer contect changes
53545 onContextClick : function (ev,dom)
53547 ev.preventDefault();
53548 var cn = dom.className;
53550 if (!cn.match(/x-ed-loc-/)) {
53553 var n = cn.split('-').pop();
53554 var ans = this.footerEls;
53557 this.editorcore.selectNode(sel);
53560 this.updateToolbar(null, null, sel);
53577 * Ext JS Library 1.1.1
53578 * Copyright(c) 2006-2007, Ext JS, LLC.
53580 * Originally Released Under LGPL - original licence link has changed is not relivant.
53583 * <script type="text/javascript">
53587 * @class Roo.form.BasicForm
53588 * @extends Roo.util.Observable
53589 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53591 * @param {String/HTMLElement/Roo.Element} el The form element or its id
53592 * @param {Object} config Configuration options
53594 Roo.form.BasicForm = function(el, config){
53595 this.allItems = [];
53596 this.childForms = [];
53597 Roo.apply(this, config);
53599 * The Roo.form.Field items in this form.
53600 * @type MixedCollection
53604 this.items = new Roo.util.MixedCollection(false, function(o){
53605 return o.id || (o.id = Roo.id());
53609 * @event beforeaction
53610 * Fires before any action is performed. Return false to cancel the action.
53611 * @param {Form} this
53612 * @param {Action} action The action to be performed
53614 beforeaction: true,
53616 * @event actionfailed
53617 * Fires when an action fails.
53618 * @param {Form} this
53619 * @param {Action} action The action that failed
53621 actionfailed : true,
53623 * @event actioncomplete
53624 * Fires when an action is completed.
53625 * @param {Form} this
53626 * @param {Action} action The action that completed
53628 actioncomplete : true
53633 Roo.form.BasicForm.superclass.constructor.call(this);
53635 Roo.form.BasicForm.popover.apply();
53638 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53640 * @cfg {String} method
53641 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53644 * @cfg {DataReader} reader
53645 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53646 * This is optional as there is built-in support for processing JSON.
53649 * @cfg {DataReader} errorReader
53650 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53651 * This is completely optional as there is built-in support for processing JSON.
53654 * @cfg {String} url
53655 * The URL to use for form actions if one isn't supplied in the action options.
53658 * @cfg {Boolean} fileUpload
53659 * Set to true if this form is a file upload.
53663 * @cfg {Object} baseParams
53664 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53669 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53674 activeAction : null,
53677 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53678 * or setValues() data instead of when the form was first created.
53680 trackResetOnLoad : false,
53684 * childForms - used for multi-tab forms
53687 childForms : false,
53690 * allItems - full list of fields.
53696 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53697 * element by passing it or its id or mask the form itself by passing in true.
53700 waitMsgTarget : false,
53705 disableMask : false,
53708 * @cfg {Boolean} errorMask (true|false) default false
53713 * @cfg {Number} maskOffset Default 100
53718 initEl : function(el){
53719 this.el = Roo.get(el);
53720 this.id = this.el.id || Roo.id();
53721 this.el.on('submit', this.onSubmit, this);
53722 this.el.addClass('x-form');
53726 onSubmit : function(e){
53731 * Returns true if client-side validation on the form is successful.
53734 isValid : function(){
53736 var target = false;
53737 this.items.each(function(f){
53744 if(!target && f.el.isVisible(true)){
53749 if(this.errorMask && !valid){
53750 Roo.form.BasicForm.popover.mask(this, target);
53756 * Returns array of invalid form fields.
53760 invalidFields : function()
53763 this.items.each(function(f){
53776 * DEPRICATED Returns true if any fields in this form have changed since their original load.
53779 isDirty : function(){
53781 this.items.each(function(f){
53791 * Returns true if any fields in this form have changed since their original load. (New version)
53795 hasChanged : function()
53798 this.items.each(function(f){
53799 if(f.hasChanged()){
53808 * Resets all hasChanged to 'false' -
53809 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
53810 * So hasChanged storage is only to be used for this purpose
53813 resetHasChanged : function()
53815 this.items.each(function(f){
53816 f.resetHasChanged();
53823 * Performs a predefined action (submit or load) or custom actions you define on this form.
53824 * @param {String} actionName The name of the action type
53825 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
53826 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
53827 * accept other config options):
53829 Property Type Description
53830 ---------------- --------------- ----------------------------------------------------------------------------------
53831 url String The url for the action (defaults to the form's url)
53832 method String The form method to use (defaults to the form's method, or POST if not defined)
53833 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
53834 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
53835 validate the form on the client (defaults to false)
53837 * @return {BasicForm} this
53839 doAction : function(action, options){
53840 if(typeof action == 'string'){
53841 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
53843 if(this.fireEvent('beforeaction', this, action) !== false){
53844 this.beforeAction(action);
53845 action.run.defer(100, action);
53851 * Shortcut to do a submit action.
53852 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53853 * @return {BasicForm} this
53855 submit : function(options){
53856 this.doAction('submit', options);
53861 * Shortcut to do a load action.
53862 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53863 * @return {BasicForm} this
53865 load : function(options){
53866 this.doAction('load', options);
53871 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
53872 * @param {Record} record The record to edit
53873 * @return {BasicForm} this
53875 updateRecord : function(record){
53876 record.beginEdit();
53877 var fs = record.fields;
53878 fs.each(function(f){
53879 var field = this.findField(f.name);
53881 record.set(f.name, field.getValue());
53889 * Loads an Roo.data.Record into this form.
53890 * @param {Record} record The record to load
53891 * @return {BasicForm} this
53893 loadRecord : function(record){
53894 this.setValues(record.data);
53899 beforeAction : function(action){
53900 var o = action.options;
53902 if(!this.disableMask) {
53903 if(this.waitMsgTarget === true){
53904 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
53905 }else if(this.waitMsgTarget){
53906 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
53907 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
53909 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
53917 afterAction : function(action, success){
53918 this.activeAction = null;
53919 var o = action.options;
53921 if(!this.disableMask) {
53922 if(this.waitMsgTarget === true){
53924 }else if(this.waitMsgTarget){
53925 this.waitMsgTarget.unmask();
53927 Roo.MessageBox.updateProgress(1);
53928 Roo.MessageBox.hide();
53936 Roo.callback(o.success, o.scope, [this, action]);
53937 this.fireEvent('actioncomplete', this, action);
53941 // failure condition..
53942 // we have a scenario where updates need confirming.
53943 // eg. if a locking scenario exists..
53944 // we look for { errors : { needs_confirm : true }} in the response.
53946 (typeof(action.result) != 'undefined') &&
53947 (typeof(action.result.errors) != 'undefined') &&
53948 (typeof(action.result.errors.needs_confirm) != 'undefined')
53951 Roo.MessageBox.confirm(
53952 "Change requires confirmation",
53953 action.result.errorMsg,
53958 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
53968 Roo.callback(o.failure, o.scope, [this, action]);
53969 // show an error message if no failed handler is set..
53970 if (!this.hasListener('actionfailed')) {
53971 Roo.MessageBox.alert("Error",
53972 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
53973 action.result.errorMsg :
53974 "Saving Failed, please check your entries or try again"
53978 this.fireEvent('actionfailed', this, action);
53984 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
53985 * @param {String} id The value to search for
53988 findField : function(id){
53989 var field = this.items.get(id);
53991 this.items.each(function(f){
53992 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
53998 return field || null;
54002 * Add a secondary form to this one,
54003 * Used to provide tabbed forms. One form is primary, with hidden values
54004 * which mirror the elements from the other forms.
54006 * @param {Roo.form.Form} form to add.
54009 addForm : function(form)
54012 if (this.childForms.indexOf(form) > -1) {
54016 this.childForms.push(form);
54018 Roo.each(form.allItems, function (fe) {
54020 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54021 if (this.findField(n)) { // already added..
54024 var add = new Roo.form.Hidden({
54027 add.render(this.el);
54034 * Mark fields in this form invalid in bulk.
54035 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54036 * @return {BasicForm} this
54038 markInvalid : function(errors){
54039 if(errors instanceof Array){
54040 for(var i = 0, len = errors.length; i < len; i++){
54041 var fieldError = errors[i];
54042 var f = this.findField(fieldError.id);
54044 f.markInvalid(fieldError.msg);
54050 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54051 field.markInvalid(errors[id]);
54055 Roo.each(this.childForms || [], function (f) {
54056 f.markInvalid(errors);
54063 * Set values for fields in this form in bulk.
54064 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54065 * @return {BasicForm} this
54067 setValues : function(values){
54068 if(values instanceof Array){ // array of objects
54069 for(var i = 0, len = values.length; i < len; i++){
54071 var f = this.findField(v.id);
54073 f.setValue(v.value);
54074 if(this.trackResetOnLoad){
54075 f.originalValue = f.getValue();
54079 }else{ // object hash
54082 if(typeof values[id] != 'function' && (field = this.findField(id))){
54084 if (field.setFromData &&
54085 field.valueField &&
54086 field.displayField &&
54087 // combos' with local stores can
54088 // be queried via setValue()
54089 // to set their value..
54090 (field.store && !field.store.isLocal)
54094 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54095 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54096 field.setFromData(sd);
54099 field.setValue(values[id]);
54103 if(this.trackResetOnLoad){
54104 field.originalValue = field.getValue();
54109 this.resetHasChanged();
54112 Roo.each(this.childForms || [], function (f) {
54113 f.setValues(values);
54114 f.resetHasChanged();
54121 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54122 * they are returned as an array.
54123 * @param {Boolean} asString
54126 getValues : function(asString)
54128 if (this.childForms) {
54129 // copy values from the child forms
54130 Roo.each(this.childForms, function (f) {
54131 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54136 if (typeof(FormData) != 'undefined' && asString !== true) {
54137 // this relies on a 'recent' version of chrome apparently...
54139 var fd = (new FormData(this.el.dom)).entries();
54141 var ent = fd.next();
54142 while (!ent.done) {
54143 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54154 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54155 if(asString === true){
54158 return Roo.urlDecode(fs);
54162 * Returns the fields in this form as an object with key/value pairs.
54163 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54164 * Normally this will not return readOnly data
54165 * @param {Boolean} with_readonly return readonly field data.
54168 getFieldValues : function(with_readonly)
54170 if (this.childForms) {
54171 // copy values from the child forms
54172 // should this call getFieldValues - probably not as we do not currently copy
54173 // hidden fields when we generate..
54174 Roo.each(this.childForms, function (f) {
54175 this.setValues(f.getFieldValues());
54180 this.items.each(function(f){
54182 if (f.readOnly && with_readonly !== true) {
54183 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54184 // if a subform contains a copy of them.
54185 // if you have subforms with the same editable data, you will need to copy the data back
54189 if (!f.getName()) {
54192 var v = f.getValue();
54193 if (f.inputType =='radio') {
54194 if (typeof(ret[f.getName()]) == 'undefined') {
54195 ret[f.getName()] = ''; // empty..
54198 if (!f.el.dom.checked) {
54202 v = f.el.dom.value;
54206 // not sure if this supported any more..
54207 if ((typeof(v) == 'object') && f.getRawValue) {
54208 v = f.getRawValue() ; // dates..
54210 // combo boxes where name != hiddenName...
54211 if (f.name != f.getName()) {
54212 ret[f.name] = f.getRawValue();
54214 ret[f.getName()] = v;
54221 * Clears all invalid messages in this form.
54222 * @return {BasicForm} this
54224 clearInvalid : function(){
54225 this.items.each(function(f){
54229 Roo.each(this.childForms || [], function (f) {
54238 * Resets this form.
54239 * @return {BasicForm} this
54241 reset : function(){
54242 this.items.each(function(f){
54246 Roo.each(this.childForms || [], function (f) {
54249 this.resetHasChanged();
54255 * Add Roo.form components to this form.
54256 * @param {Field} field1
54257 * @param {Field} field2 (optional)
54258 * @param {Field} etc (optional)
54259 * @return {BasicForm} this
54262 this.items.addAll(Array.prototype.slice.call(arguments, 0));
54268 * Removes a field from the items collection (does NOT remove its markup).
54269 * @param {Field} field
54270 * @return {BasicForm} this
54272 remove : function(field){
54273 this.items.remove(field);
54278 * Looks at the fields in this form, checks them for an id attribute,
54279 * and calls applyTo on the existing dom element with that id.
54280 * @return {BasicForm} this
54282 render : function(){
54283 this.items.each(function(f){
54284 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54292 * Calls {@link Ext#apply} for all fields in this form with the passed object.
54293 * @param {Object} values
54294 * @return {BasicForm} this
54296 applyToFields : function(o){
54297 this.items.each(function(f){
54304 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54305 * @param {Object} values
54306 * @return {BasicForm} this
54308 applyIfToFields : function(o){
54309 this.items.each(function(f){
54317 Roo.BasicForm = Roo.form.BasicForm;
54319 Roo.apply(Roo.form.BasicForm, {
54333 intervalID : false,
54339 if(this.isApplied){
54344 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54345 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54346 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54347 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54350 this.maskEl.top.enableDisplayMode("block");
54351 this.maskEl.left.enableDisplayMode("block");
54352 this.maskEl.bottom.enableDisplayMode("block");
54353 this.maskEl.right.enableDisplayMode("block");
54355 Roo.get(document.body).on('click', function(){
54359 Roo.get(document.body).on('touchstart', function(){
54363 this.isApplied = true
54366 mask : function(form, target)
54370 this.target = target;
54372 if(!this.form.errorMask || !target.el){
54376 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54378 var ot = this.target.el.calcOffsetsTo(scrollable);
54380 var scrollTo = ot[1] - this.form.maskOffset;
54382 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54384 scrollable.scrollTo('top', scrollTo);
54386 var el = this.target.wrap || this.target.el;
54388 var box = el.getBox();
54390 this.maskEl.top.setStyle('position', 'absolute');
54391 this.maskEl.top.setStyle('z-index', 10000);
54392 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54393 this.maskEl.top.setLeft(0);
54394 this.maskEl.top.setTop(0);
54395 this.maskEl.top.show();
54397 this.maskEl.left.setStyle('position', 'absolute');
54398 this.maskEl.left.setStyle('z-index', 10000);
54399 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54400 this.maskEl.left.setLeft(0);
54401 this.maskEl.left.setTop(box.y - this.padding);
54402 this.maskEl.left.show();
54404 this.maskEl.bottom.setStyle('position', 'absolute');
54405 this.maskEl.bottom.setStyle('z-index', 10000);
54406 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54407 this.maskEl.bottom.setLeft(0);
54408 this.maskEl.bottom.setTop(box.bottom + this.padding);
54409 this.maskEl.bottom.show();
54411 this.maskEl.right.setStyle('position', 'absolute');
54412 this.maskEl.right.setStyle('z-index', 10000);
54413 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54414 this.maskEl.right.setLeft(box.right + this.padding);
54415 this.maskEl.right.setTop(box.y - this.padding);
54416 this.maskEl.right.show();
54418 this.intervalID = window.setInterval(function() {
54419 Roo.form.BasicForm.popover.unmask();
54422 window.onwheel = function(){ return false;};
54424 (function(){ this.isMasked = true; }).defer(500, this);
54428 unmask : function()
54430 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54434 this.maskEl.top.setStyle('position', 'absolute');
54435 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54436 this.maskEl.top.hide();
54438 this.maskEl.left.setStyle('position', 'absolute');
54439 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54440 this.maskEl.left.hide();
54442 this.maskEl.bottom.setStyle('position', 'absolute');
54443 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54444 this.maskEl.bottom.hide();
54446 this.maskEl.right.setStyle('position', 'absolute');
54447 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54448 this.maskEl.right.hide();
54450 window.onwheel = function(){ return true;};
54452 if(this.intervalID){
54453 window.clearInterval(this.intervalID);
54454 this.intervalID = false;
54457 this.isMasked = false;
54465 * Ext JS Library 1.1.1
54466 * Copyright(c) 2006-2007, Ext JS, LLC.
54468 * Originally Released Under LGPL - original licence link has changed is not relivant.
54471 * <script type="text/javascript">
54475 * @class Roo.form.Form
54476 * @extends Roo.form.BasicForm
54477 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54478 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54480 * @param {Object} config Configuration options
54482 Roo.form.Form = function(config){
54484 if (config.items) {
54485 xitems = config.items;
54486 delete config.items;
54490 Roo.form.Form.superclass.constructor.call(this, null, config);
54491 this.url = this.url || this.action;
54493 this.root = new Roo.form.Layout(Roo.applyIf({
54497 this.active = this.root;
54499 * Array of all the buttons that have been added to this form via {@link addButton}
54503 this.allItems = [];
54506 * @event clientvalidation
54507 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54508 * @param {Form} this
54509 * @param {Boolean} valid true if the form has passed client-side validation
54511 clientvalidation: true,
54514 * Fires when the form is rendered
54515 * @param {Roo.form.Form} form
54520 if (this.progressUrl) {
54521 // push a hidden field onto the list of fields..
54525 name : 'UPLOAD_IDENTIFIER'
54530 Roo.each(xitems, this.addxtype, this);
54534 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54536 * @cfg {Roo.Button} buttons[] buttons at bottom of form
54540 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54543 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54546 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54548 buttonAlign:'center',
54551 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54556 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54557 * This property cascades to child containers if not set.
54562 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54563 * fires a looping event with that state. This is required to bind buttons to the valid
54564 * state using the config value formBind:true on the button.
54566 monitorValid : false,
54569 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54574 * @cfg {String} progressUrl - Url to return progress data
54577 progressUrl : false,
54579 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54580 * sending a formdata with extra parameters - eg uploaded elements.
54586 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54587 * fields are added and the column is closed. If no fields are passed the column remains open
54588 * until end() is called.
54589 * @param {Object} config The config to pass to the column
54590 * @param {Field} field1 (optional)
54591 * @param {Field} field2 (optional)
54592 * @param {Field} etc (optional)
54593 * @return Column The column container object
54595 column : function(c){
54596 var col = new Roo.form.Column(c);
54598 if(arguments.length > 1){ // duplicate code required because of Opera
54599 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54606 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54607 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54608 * until end() is called.
54609 * @param {Object} config The config to pass to the fieldset
54610 * @param {Field} field1 (optional)
54611 * @param {Field} field2 (optional)
54612 * @param {Field} etc (optional)
54613 * @return FieldSet The fieldset container object
54615 fieldset : function(c){
54616 var fs = new Roo.form.FieldSet(c);
54618 if(arguments.length > 1){ // duplicate code required because of Opera
54619 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54626 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54627 * fields are added and the container is closed. If no fields are passed the container remains open
54628 * until end() is called.
54629 * @param {Object} config The config to pass to the Layout
54630 * @param {Field} field1 (optional)
54631 * @param {Field} field2 (optional)
54632 * @param {Field} etc (optional)
54633 * @return Layout The container object
54635 container : function(c){
54636 var l = new Roo.form.Layout(c);
54638 if(arguments.length > 1){ // duplicate code required because of Opera
54639 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54646 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54647 * @param {Object} container A Roo.form.Layout or subclass of Layout
54648 * @return {Form} this
54650 start : function(c){
54651 // cascade label info
54652 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54653 this.active.stack.push(c);
54654 c.ownerCt = this.active;
54660 * Closes the current open container
54661 * @return {Form} this
54664 if(this.active == this.root){
54667 this.active = this.active.ownerCt;
54672 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
54673 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54674 * as the label of the field.
54675 * @param {Field} field1
54676 * @param {Field} field2 (optional)
54677 * @param {Field} etc. (optional)
54678 * @return {Form} this
54681 this.active.stack.push.apply(this.active.stack, arguments);
54682 this.allItems.push.apply(this.allItems,arguments);
54684 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54685 if(a[i].isFormField){
54690 Roo.form.Form.superclass.add.apply(this, r);
54700 * Find any element that has been added to a form, using it's ID or name
54701 * This can include framesets, columns etc. along with regular fields..
54702 * @param {String} id - id or name to find.
54704 * @return {Element} e - or false if nothing found.
54706 findbyId : function(id)
54712 Roo.each(this.allItems, function(f){
54713 if (f.id == id || f.name == id ){
54724 * Render this form into the passed container. This should only be called once!
54725 * @param {String/HTMLElement/Element} container The element this component should be rendered into
54726 * @return {Form} this
54728 render : function(ct)
54734 var o = this.autoCreate || {
54736 method : this.method || 'POST',
54737 id : this.id || Roo.id()
54739 this.initEl(ct.createChild(o));
54741 this.root.render(this.el);
54745 this.items.each(function(f){
54746 f.render('x-form-el-'+f.id);
54749 if(this.buttons.length > 0){
54750 // tables are required to maintain order and for correct IE layout
54751 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
54752 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
54753 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
54755 var tr = tb.getElementsByTagName('tr')[0];
54756 for(var i = 0, len = this.buttons.length; i < len; i++) {
54757 var b = this.buttons[i];
54758 var td = document.createElement('td');
54759 td.className = 'x-form-btn-td';
54760 b.render(tr.appendChild(td));
54763 if(this.monitorValid){ // initialize after render
54764 this.startMonitoring();
54766 this.fireEvent('rendered', this);
54771 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
54772 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
54773 * object or a valid Roo.DomHelper element config
54774 * @param {Function} handler The function called when the button is clicked
54775 * @param {Object} scope (optional) The scope of the handler function
54776 * @return {Roo.Button}
54778 addButton : function(config, handler, scope){
54782 minWidth: this.minButtonWidth,
54785 if(typeof config == "string"){
54788 Roo.apply(bc, config);
54790 var btn = new Roo.Button(null, bc);
54791 this.buttons.push(btn);
54796 * Adds a series of form elements (using the xtype property as the factory method.
54797 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
54798 * @param {Object} config
54801 addxtype : function()
54803 var ar = Array.prototype.slice.call(arguments, 0);
54805 for(var i = 0; i < ar.length; i++) {
54807 continue; // skip -- if this happends something invalid got sent, we
54808 // should ignore it, as basically that interface element will not show up
54809 // and that should be pretty obvious!!
54812 if (Roo.form[ar[i].xtype]) {
54814 var fe = Roo.factory(ar[i], Roo.form);
54820 fe.store.form = this;
54825 this.allItems.push(fe);
54826 if (fe.items && fe.addxtype) {
54827 fe.addxtype.apply(fe, fe.items);
54837 // console.log('adding ' + ar[i].xtype);
54839 if (ar[i].xtype == 'Button') {
54840 //console.log('adding button');
54841 //console.log(ar[i]);
54842 this.addButton(ar[i]);
54843 this.allItems.push(fe);
54847 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
54848 alert('end is not supported on xtype any more, use items');
54850 // //console.log('adding end');
54858 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
54859 * option "monitorValid"
54861 startMonitoring : function(){
54864 Roo.TaskMgr.start({
54865 run : this.bindHandler,
54866 interval : this.monitorPoll || 200,
54873 * Stops monitoring of the valid state of this form
54875 stopMonitoring : function(){
54876 this.bound = false;
54880 bindHandler : function(){
54882 return false; // stops binding
54885 this.items.each(function(f){
54886 if(!f.isValid(true)){
54891 for(var i = 0, len = this.buttons.length; i < len; i++){
54892 var btn = this.buttons[i];
54893 if(btn.formBind === true && btn.disabled === valid){
54894 btn.setDisabled(!valid);
54897 this.fireEvent('clientvalidation', this, valid);
54911 Roo.Form = Roo.form.Form;
54914 * Ext JS Library 1.1.1
54915 * Copyright(c) 2006-2007, Ext JS, LLC.
54917 * Originally Released Under LGPL - original licence link has changed is not relivant.
54920 * <script type="text/javascript">
54923 // as we use this in bootstrap.
54924 Roo.namespace('Roo.form');
54926 * @class Roo.form.Action
54927 * Internal Class used to handle form actions
54929 * @param {Roo.form.BasicForm} el The form element or its id
54930 * @param {Object} config Configuration options
54935 // define the action interface
54936 Roo.form.Action = function(form, options){
54938 this.options = options || {};
54941 * Client Validation Failed
54944 Roo.form.Action.CLIENT_INVALID = 'client';
54946 * Server Validation Failed
54949 Roo.form.Action.SERVER_INVALID = 'server';
54951 * Connect to Server Failed
54954 Roo.form.Action.CONNECT_FAILURE = 'connect';
54956 * Reading Data from Server Failed
54959 Roo.form.Action.LOAD_FAILURE = 'load';
54961 Roo.form.Action.prototype = {
54963 failureType : undefined,
54964 response : undefined,
54965 result : undefined,
54967 // interface method
54968 run : function(options){
54972 // interface method
54973 success : function(response){
54977 // interface method
54978 handleResponse : function(response){
54982 // default connection failure
54983 failure : function(response){
54985 this.response = response;
54986 this.failureType = Roo.form.Action.CONNECT_FAILURE;
54987 this.form.afterAction(this, false);
54990 processResponse : function(response){
54991 this.response = response;
54992 if(!response.responseText){
54995 this.result = this.handleResponse(response);
54996 return this.result;
54999 // utility functions used internally
55000 getUrl : function(appendParams){
55001 var url = this.options.url || this.form.url || this.form.el.dom.action;
55003 var p = this.getParams();
55005 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55011 getMethod : function(){
55012 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55015 getParams : function(){
55016 var bp = this.form.baseParams;
55017 var p = this.options.params;
55019 if(typeof p == "object"){
55020 p = Roo.urlEncode(Roo.applyIf(p, bp));
55021 }else if(typeof p == 'string' && bp){
55022 p += '&' + Roo.urlEncode(bp);
55025 p = Roo.urlEncode(bp);
55030 createCallback : function(){
55032 success: this.success,
55033 failure: this.failure,
55035 timeout: (this.form.timeout*1000),
55036 upload: this.form.fileUpload ? this.success : undefined
55041 Roo.form.Action.Submit = function(form, options){
55042 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55045 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55048 haveProgress : false,
55049 uploadComplete : false,
55051 // uploadProgress indicator.
55052 uploadProgress : function()
55054 if (!this.form.progressUrl) {
55058 if (!this.haveProgress) {
55059 Roo.MessageBox.progress("Uploading", "Uploading");
55061 if (this.uploadComplete) {
55062 Roo.MessageBox.hide();
55066 this.haveProgress = true;
55068 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55070 var c = new Roo.data.Connection();
55072 url : this.form.progressUrl,
55077 success : function(req){
55078 //console.log(data);
55082 rdata = Roo.decode(req.responseText)
55084 Roo.log("Invalid data from server..");
55088 if (!rdata || !rdata.success) {
55090 Roo.MessageBox.alert(Roo.encode(rdata));
55093 var data = rdata.data;
55095 if (this.uploadComplete) {
55096 Roo.MessageBox.hide();
55101 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55102 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55105 this.uploadProgress.defer(2000,this);
55108 failure: function(data) {
55109 Roo.log('progress url failed ');
55120 // run get Values on the form, so it syncs any secondary forms.
55121 this.form.getValues();
55123 var o = this.options;
55124 var method = this.getMethod();
55125 var isPost = method == 'POST';
55126 if(o.clientValidation === false || this.form.isValid()){
55128 if (this.form.progressUrl) {
55129 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55130 (new Date() * 1) + '' + Math.random());
55135 Roo.Ajax.request(Roo.apply(this.createCallback(), {
55136 form:this.form.el.dom,
55137 url:this.getUrl(!isPost),
55139 params:isPost ? this.getParams() : null,
55140 isUpload: this.form.fileUpload,
55141 formData : this.form.formData
55144 this.uploadProgress();
55146 }else if (o.clientValidation !== false){ // client validation failed
55147 this.failureType = Roo.form.Action.CLIENT_INVALID;
55148 this.form.afterAction(this, false);
55152 success : function(response)
55154 this.uploadComplete= true;
55155 if (this.haveProgress) {
55156 Roo.MessageBox.hide();
55160 var result = this.processResponse(response);
55161 if(result === true || result.success){
55162 this.form.afterAction(this, true);
55166 this.form.markInvalid(result.errors);
55167 this.failureType = Roo.form.Action.SERVER_INVALID;
55169 this.form.afterAction(this, false);
55171 failure : function(response)
55173 this.uploadComplete= true;
55174 if (this.haveProgress) {
55175 Roo.MessageBox.hide();
55178 this.response = response;
55179 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55180 this.form.afterAction(this, false);
55183 handleResponse : function(response){
55184 if(this.form.errorReader){
55185 var rs = this.form.errorReader.read(response);
55188 for(var i = 0, len = rs.records.length; i < len; i++) {
55189 var r = rs.records[i];
55190 errors[i] = r.data;
55193 if(errors.length < 1){
55197 success : rs.success,
55203 ret = Roo.decode(response.responseText);
55207 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55217 Roo.form.Action.Load = function(form, options){
55218 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55219 this.reader = this.form.reader;
55222 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55227 Roo.Ajax.request(Roo.apply(
55228 this.createCallback(), {
55229 method:this.getMethod(),
55230 url:this.getUrl(false),
55231 params:this.getParams()
55235 success : function(response){
55237 var result = this.processResponse(response);
55238 if(result === true || !result.success || !result.data){
55239 this.failureType = Roo.form.Action.LOAD_FAILURE;
55240 this.form.afterAction(this, false);
55243 this.form.clearInvalid();
55244 this.form.setValues(result.data);
55245 this.form.afterAction(this, true);
55248 handleResponse : function(response){
55249 if(this.form.reader){
55250 var rs = this.form.reader.read(response);
55251 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55253 success : rs.success,
55257 return Roo.decode(response.responseText);
55261 Roo.form.Action.ACTION_TYPES = {
55262 'load' : Roo.form.Action.Load,
55263 'submit' : Roo.form.Action.Submit
55266 * Ext JS Library 1.1.1
55267 * Copyright(c) 2006-2007, Ext JS, LLC.
55269 * Originally Released Under LGPL - original licence link has changed is not relivant.
55272 * <script type="text/javascript">
55276 * @class Roo.form.Layout
55277 * @extends Roo.Component
55278 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55279 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55281 * @param {Object} config Configuration options
55283 Roo.form.Layout = function(config){
55285 if (config.items) {
55286 xitems = config.items;
55287 delete config.items;
55289 Roo.form.Layout.superclass.constructor.call(this, config);
55291 Roo.each(xitems, this.addxtype, this);
55295 Roo.extend(Roo.form.Layout, Roo.Component, {
55297 * @cfg {String/Object} autoCreate
55298 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55301 * @cfg {String/Object/Function} style
55302 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55303 * a function which returns such a specification.
55306 * @cfg {String} labelAlign (left|top|right)
55307 * Valid values are "left," "top" and "right" (defaults to "left")
55310 * @cfg {Number} labelWidth
55311 * Fixed width in pixels of all field labels (defaults to undefined)
55314 * @cfg {Boolean} clear
55315 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55319 * @cfg {String} labelSeparator
55320 * The separator to use after field labels (defaults to ':')
55322 labelSeparator : ':',
55324 * @cfg {Boolean} hideLabels
55325 * True to suppress the display of field labels in this layout (defaults to false)
55327 hideLabels : false,
55330 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55335 onRender : function(ct, position){
55336 if(this.el){ // from markup
55337 this.el = Roo.get(this.el);
55338 }else { // generate
55339 var cfg = this.getAutoCreate();
55340 this.el = ct.createChild(cfg, position);
55343 this.el.applyStyles(this.style);
55345 if(this.labelAlign){
55346 this.el.addClass('x-form-label-'+this.labelAlign);
55348 if(this.hideLabels){
55349 this.labelStyle = "display:none";
55350 this.elementStyle = "padding-left:0;";
55352 if(typeof this.labelWidth == 'number'){
55353 this.labelStyle = "width:"+this.labelWidth+"px;";
55354 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55356 if(this.labelAlign == 'top'){
55357 this.labelStyle = "width:auto;";
55358 this.elementStyle = "padding-left:0;";
55361 var stack = this.stack;
55362 var slen = stack.length;
55364 if(!this.fieldTpl){
55365 var t = new Roo.Template(
55366 '<div class="x-form-item {5}">',
55367 '<label for="{0}" style="{2}">{1}{4}</label>',
55368 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55370 '</div><div class="x-form-clear-left"></div>'
55372 t.disableFormats = true;
55374 Roo.form.Layout.prototype.fieldTpl = t;
55376 for(var i = 0; i < slen; i++) {
55377 if(stack[i].isFormField){
55378 this.renderField(stack[i]);
55380 this.renderComponent(stack[i]);
55385 this.el.createChild({cls:'x-form-clear'});
55390 renderField : function(f){
55391 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55394 f.labelStyle||this.labelStyle||'', //2
55395 this.elementStyle||'', //3
55396 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55397 f.itemCls||this.itemCls||'' //5
55398 ], true).getPrevSibling());
55402 renderComponent : function(c){
55403 c.render(c.isLayout ? this.el : this.el.createChild());
55406 * Adds a object form elements (using the xtype property as the factory method.)
55407 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
55408 * @param {Object} config
55410 addxtype : function(o)
55412 // create the lement.
55413 o.form = this.form;
55414 var fe = Roo.factory(o, Roo.form);
55415 this.form.allItems.push(fe);
55416 this.stack.push(fe);
55418 if (fe.isFormField) {
55419 this.form.items.add(fe);
55428 * @class Roo.form.Column
55429 * @extends Roo.form.Layout
55430 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55431 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55433 * @param {Object} config Configuration options
55435 Roo.form.Column = function(config){
55436 Roo.form.Column.superclass.constructor.call(this, config);
55439 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55441 * @cfg {Number/String} width
55442 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55445 * @cfg {String/Object} autoCreate
55446 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55450 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55453 onRender : function(ct, position){
55454 Roo.form.Column.superclass.onRender.call(this, ct, position);
55456 this.el.setWidth(this.width);
55462 * @class Roo.form.Row
55463 * @extends Roo.form.Layout
55464 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55465 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55467 * @param {Object} config Configuration options
55471 Roo.form.Row = function(config){
55472 Roo.form.Row.superclass.constructor.call(this, config);
55475 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55477 * @cfg {Number/String} width
55478 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55481 * @cfg {Number/String} height
55482 * The fixed height of the column in pixels or CSS value (defaults to "auto")
55484 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55488 onRender : function(ct, position){
55489 //console.log('row render');
55491 var t = new Roo.Template(
55492 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55493 '<label for="{0}" style="{2}">{1}{4}</label>',
55494 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55498 t.disableFormats = true;
55500 Roo.form.Layout.prototype.rowTpl = t;
55502 this.fieldTpl = this.rowTpl;
55504 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55505 var labelWidth = 100;
55507 if ((this.labelAlign != 'top')) {
55508 if (typeof this.labelWidth == 'number') {
55509 labelWidth = this.labelWidth
55511 this.padWidth = 20 + labelWidth;
55515 Roo.form.Column.superclass.onRender.call(this, ct, position);
55517 this.el.setWidth(this.width);
55520 this.el.setHeight(this.height);
55525 renderField : function(f){
55526 f.fieldEl = this.fieldTpl.append(this.el, [
55527 f.id, f.fieldLabel,
55528 f.labelStyle||this.labelStyle||'',
55529 this.elementStyle||'',
55530 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55531 f.itemCls||this.itemCls||'',
55532 f.width ? f.width + this.padWidth : 160 + this.padWidth
55539 * @class Roo.form.FieldSet
55540 * @extends Roo.form.Layout
55541 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55542 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55544 * @param {Object} config Configuration options
55546 Roo.form.FieldSet = function(config){
55547 Roo.form.FieldSet.superclass.constructor.call(this, config);
55550 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55552 * @cfg {String} legend
55553 * The text to display as the legend for the FieldSet (defaults to '')
55556 * @cfg {String/Object} autoCreate
55557 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55561 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55564 onRender : function(ct, position){
55565 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55567 this.setLegend(this.legend);
55572 setLegend : function(text){
55574 this.el.child('legend').update(text);
55579 * Ext JS Library 1.1.1
55580 * Copyright(c) 2006-2007, Ext JS, LLC.
55582 * Originally Released Under LGPL - original licence link has changed is not relivant.
55585 * <script type="text/javascript">
55588 * @class Roo.form.VTypes
55589 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55592 Roo.form.VTypes = function(){
55593 // closure these in so they are only created once.
55594 var alpha = /^[a-zA-Z_]+$/;
55595 var alphanum = /^[a-zA-Z0-9_]+$/;
55596 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55597 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55599 // All these messages and functions are configurable
55602 * The function used to validate email addresses
55603 * @param {String} value The email address
55605 'email' : function(v){
55606 return email.test(v);
55609 * The error text to display when the email validation function returns false
55612 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
55614 * The keystroke filter mask to be applied on email input
55617 'emailMask' : /[a-z0-9_\.\-@]/i,
55620 * The function used to validate URLs
55621 * @param {String} value The URL
55623 'url' : function(v){
55624 return url.test(v);
55627 * The error text to display when the url validation function returns false
55630 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55633 * The function used to validate alpha values
55634 * @param {String} value The value
55636 'alpha' : function(v){
55637 return alpha.test(v);
55640 * The error text to display when the alpha validation function returns false
55643 'alphaText' : 'This field should only contain letters and _',
55645 * The keystroke filter mask to be applied on alpha input
55648 'alphaMask' : /[a-z_]/i,
55651 * The function used to validate alphanumeric values
55652 * @param {String} value The value
55654 'alphanum' : function(v){
55655 return alphanum.test(v);
55658 * The error text to display when the alphanumeric validation function returns false
55661 'alphanumText' : 'This field should only contain letters, numbers and _',
55663 * The keystroke filter mask to be applied on alphanumeric input
55666 'alphanumMask' : /[a-z0-9_]/i
55668 }();//<script type="text/javascript">
55671 * @class Roo.form.FCKeditor
55672 * @extends Roo.form.TextArea
55673 * Wrapper around the FCKEditor http://www.fckeditor.net
55675 * Creates a new FCKeditor
55676 * @param {Object} config Configuration options
55678 Roo.form.FCKeditor = function(config){
55679 Roo.form.FCKeditor.superclass.constructor.call(this, config);
55682 * @event editorinit
55683 * Fired when the editor is initialized - you can add extra handlers here..
55684 * @param {FCKeditor} this
55685 * @param {Object} the FCK object.
55692 Roo.form.FCKeditor.editors = { };
55693 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55695 //defaultAutoCreate : {
55696 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
55700 * @cfg {Object} fck options - see fck manual for details.
55705 * @cfg {Object} fck toolbar set (Basic or Default)
55707 toolbarSet : 'Basic',
55709 * @cfg {Object} fck BasePath
55711 basePath : '/fckeditor/',
55719 onRender : function(ct, position)
55722 this.defaultAutoCreate = {
55724 style:"width:300px;height:60px;",
55725 autocomplete: "new-password"
55728 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55731 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55732 if(this.preventScrollbars){
55733 this.el.setStyle("overflow", "hidden");
55735 this.el.setHeight(this.growMin);
55738 //console.log('onrender' + this.getId() );
55739 Roo.form.FCKeditor.editors[this.getId()] = this;
55742 this.replaceTextarea() ;
55746 getEditor : function() {
55747 return this.fckEditor;
55750 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
55751 * @param {Mixed} value The value to set
55755 setValue : function(value)
55757 //console.log('setValue: ' + value);
55759 if(typeof(value) == 'undefined') { // not sure why this is happending...
55762 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55764 //if(!this.el || !this.getEditor()) {
55765 // this.value = value;
55766 //this.setValue.defer(100,this,[value]);
55770 if(!this.getEditor()) {
55774 this.getEditor().SetData(value);
55781 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
55782 * @return {Mixed} value The field value
55784 getValue : function()
55787 if (this.frame && this.frame.dom.style.display == 'none') {
55788 return Roo.form.FCKeditor.superclass.getValue.call(this);
55791 if(!this.el || !this.getEditor()) {
55793 // this.getValue.defer(100,this);
55798 var value=this.getEditor().GetData();
55799 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55800 return Roo.form.FCKeditor.superclass.getValue.call(this);
55806 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
55807 * @return {Mixed} value The field value
55809 getRawValue : function()
55811 if (this.frame && this.frame.dom.style.display == 'none') {
55812 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55815 if(!this.el || !this.getEditor()) {
55816 //this.getRawValue.defer(100,this);
55823 var value=this.getEditor().GetData();
55824 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
55825 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55829 setSize : function(w,h) {
55833 //if (this.frame && this.frame.dom.style.display == 'none') {
55834 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55837 //if(!this.el || !this.getEditor()) {
55838 // this.setSize.defer(100,this, [w,h]);
55844 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55846 this.frame.dom.setAttribute('width', w);
55847 this.frame.dom.setAttribute('height', h);
55848 this.frame.setSize(w,h);
55852 toggleSourceEdit : function(value) {
55856 this.el.dom.style.display = value ? '' : 'none';
55857 this.frame.dom.style.display = value ? 'none' : '';
55862 focus: function(tag)
55864 if (this.frame.dom.style.display == 'none') {
55865 return Roo.form.FCKeditor.superclass.focus.call(this);
55867 if(!this.el || !this.getEditor()) {
55868 this.focus.defer(100,this, [tag]);
55875 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
55876 this.getEditor().Focus();
55878 if (!this.getEditor().Selection.GetSelection()) {
55879 this.focus.defer(100,this, [tag]);
55884 var r = this.getEditor().EditorDocument.createRange();
55885 r.setStart(tgs[0],0);
55886 r.setEnd(tgs[0],0);
55887 this.getEditor().Selection.GetSelection().removeAllRanges();
55888 this.getEditor().Selection.GetSelection().addRange(r);
55889 this.getEditor().Focus();
55896 replaceTextarea : function()
55898 if ( document.getElementById( this.getId() + '___Frame' ) ) {
55901 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
55903 // We must check the elements firstly using the Id and then the name.
55904 var oTextarea = document.getElementById( this.getId() );
55906 var colElementsByName = document.getElementsByName( this.getId() ) ;
55908 oTextarea.style.display = 'none' ;
55910 if ( oTextarea.tabIndex ) {
55911 this.TabIndex = oTextarea.tabIndex ;
55914 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
55915 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
55916 this.frame = Roo.get(this.getId() + '___Frame')
55919 _getConfigHtml : function()
55923 for ( var o in this.fckconfig ) {
55924 sConfig += sConfig.length > 0 ? '&' : '';
55925 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
55928 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
55932 _getIFrameHtml : function()
55934 var sFile = 'fckeditor.html' ;
55935 /* no idea what this is about..
55938 if ( (/fcksource=true/i).test( window.top.location.search ) )
55939 sFile = 'fckeditor.original.html' ;
55944 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
55945 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
55948 var html = '<iframe id="' + this.getId() +
55949 '___Frame" src="' + sLink +
55950 '" width="' + this.width +
55951 '" height="' + this.height + '"' +
55952 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
55953 ' frameborder="0" scrolling="no"></iframe>' ;
55958 _insertHtmlBefore : function( html, element )
55960 if ( element.insertAdjacentHTML ) {
55962 element.insertAdjacentHTML( 'beforeBegin', html ) ;
55964 var oRange = document.createRange() ;
55965 oRange.setStartBefore( element ) ;
55966 var oFragment = oRange.createContextualFragment( html );
55967 element.parentNode.insertBefore( oFragment, element ) ;
55980 //Roo.reg('fckeditor', Roo.form.FCKeditor);
55982 function FCKeditor_OnComplete(editorInstance){
55983 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
55984 f.fckEditor = editorInstance;
55985 //console.log("loaded");
55986 f.fireEvent('editorinit', f, editorInstance);
56006 //<script type="text/javascript">
56008 * @class Roo.form.GridField
56009 * @extends Roo.form.Field
56010 * Embed a grid (or editable grid into a form)
56013 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56015 * xgrid.store = Roo.data.Store
56016 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56017 * xgrid.store.reader = Roo.data.JsonReader
56021 * Creates a new GridField
56022 * @param {Object} config Configuration options
56024 Roo.form.GridField = function(config){
56025 Roo.form.GridField.superclass.constructor.call(this, config);
56029 Roo.extend(Roo.form.GridField, Roo.form.Field, {
56031 * @cfg {Number} width - used to restrict width of grid..
56035 * @cfg {Number} height - used to restrict height of grid..
56039 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56045 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56046 * {tag: "input", type: "checkbox", autocomplete: "off"})
56048 // defaultAutoCreate : { tag: 'div' },
56049 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56051 * @cfg {String} addTitle Text to include for adding a title.
56055 onResize : function(){
56056 Roo.form.Field.superclass.onResize.apply(this, arguments);
56059 initEvents : function(){
56060 // Roo.form.Checkbox.superclass.initEvents.call(this);
56061 // has no events...
56066 getResizeEl : function(){
56070 getPositionEl : function(){
56075 onRender : function(ct, position){
56077 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56078 var style = this.style;
56081 Roo.form.GridField.superclass.onRender.call(this, ct, position);
56082 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56083 this.viewEl = this.wrap.createChild({ tag: 'div' });
56085 this.viewEl.applyStyles(style);
56088 this.viewEl.setWidth(this.width);
56091 this.viewEl.setHeight(this.height);
56093 //if(this.inputValue !== undefined){
56094 //this.setValue(this.value);
56097 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56100 this.grid.render();
56101 this.grid.getDataSource().on('remove', this.refreshValue, this);
56102 this.grid.getDataSource().on('update', this.refreshValue, this);
56103 this.grid.on('afteredit', this.refreshValue, this);
56109 * Sets the value of the item.
56110 * @param {String} either an object or a string..
56112 setValue : function(v){
56114 v = v || []; // empty set..
56115 // this does not seem smart - it really only affects memoryproxy grids..
56116 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56117 var ds = this.grid.getDataSource();
56118 // assumes a json reader..
56120 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
56121 ds.loadData( data);
56123 // clear selection so it does not get stale.
56124 if (this.grid.sm) {
56125 this.grid.sm.clearSelections();
56128 Roo.form.GridField.superclass.setValue.call(this, v);
56129 this.refreshValue();
56130 // should load data in the grid really....
56134 refreshValue: function() {
56136 this.grid.getDataSource().each(function(r) {
56139 this.el.dom.value = Roo.encode(val);
56147 * Ext JS Library 1.1.1
56148 * Copyright(c) 2006-2007, Ext JS, LLC.
56150 * Originally Released Under LGPL - original licence link has changed is not relivant.
56153 * <script type="text/javascript">
56156 * @class Roo.form.DisplayField
56157 * @extends Roo.form.Field
56158 * A generic Field to display non-editable data.
56159 * @cfg {Boolean} closable (true|false) default false
56161 * Creates a new Display Field item.
56162 * @param {Object} config Configuration options
56164 Roo.form.DisplayField = function(config){
56165 Roo.form.DisplayField.superclass.constructor.call(this, config);
56170 * Fires after the click the close btn
56171 * @param {Roo.form.DisplayField} this
56177 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
56178 inputType: 'hidden',
56184 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56186 focusClass : undefined,
56188 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56190 fieldClass: 'x-form-field',
56193 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56195 valueRenderer: undefined,
56199 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56200 * {tag: "input", type: "checkbox", autocomplete: "off"})
56203 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56207 onResize : function(){
56208 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56212 initEvents : function(){
56213 // Roo.form.Checkbox.superclass.initEvents.call(this);
56214 // has no events...
56217 this.closeEl.on('click', this.onClose, this);
56223 getResizeEl : function(){
56227 getPositionEl : function(){
56232 onRender : function(ct, position){
56234 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56235 //if(this.inputValue !== undefined){
56236 this.wrap = this.el.wrap();
56238 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56241 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56244 if (this.bodyStyle) {
56245 this.viewEl.applyStyles(this.bodyStyle);
56247 //this.viewEl.setStyle('padding', '2px');
56249 this.setValue(this.value);
56254 initValue : Roo.emptyFn,
56259 onClick : function(){
56264 * Sets the checked state of the checkbox.
56265 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56267 setValue : function(v){
56269 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
56270 // this might be called before we have a dom element..
56271 if (!this.viewEl) {
56274 this.viewEl.dom.innerHTML = html;
56275 Roo.form.DisplayField.superclass.setValue.call(this, v);
56279 onClose : function(e)
56281 e.preventDefault();
56283 this.fireEvent('close', this);
56292 * @class Roo.form.DayPicker
56293 * @extends Roo.form.Field
56294 * A Day picker show [M] [T] [W] ....
56296 * Creates a new Day Picker
56297 * @param {Object} config Configuration options
56299 Roo.form.DayPicker= function(config){
56300 Roo.form.DayPicker.superclass.constructor.call(this, config);
56304 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
56306 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56308 focusClass : undefined,
56310 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56312 fieldClass: "x-form-field",
56315 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56316 * {tag: "input", type: "checkbox", autocomplete: "off"})
56318 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56321 actionMode : 'viewEl',
56325 inputType : 'hidden',
56328 inputElement: false, // real input element?
56329 basedOn: false, // ????
56331 isFormField: true, // not sure where this is needed!!!!
56333 onResize : function(){
56334 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56335 if(!this.boxLabel){
56336 this.el.alignTo(this.wrap, 'c-c');
56340 initEvents : function(){
56341 Roo.form.Checkbox.superclass.initEvents.call(this);
56342 this.el.on("click", this.onClick, this);
56343 this.el.on("change", this.onClick, this);
56347 getResizeEl : function(){
56351 getPositionEl : function(){
56357 onRender : function(ct, position){
56358 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56360 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56362 var r1 = '<table><tr>';
56363 var r2 = '<tr class="x-form-daypick-icons">';
56364 for (var i=0; i < 7; i++) {
56365 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56366 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
56369 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56370 viewEl.select('img').on('click', this.onClick, this);
56371 this.viewEl = viewEl;
56374 // this will not work on Chrome!!!
56375 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
56376 this.el.on('propertychange', this.setFromHidden, this); //ie
56384 initValue : Roo.emptyFn,
56387 * Returns the checked state of the checkbox.
56388 * @return {Boolean} True if checked, else false
56390 getValue : function(){
56391 return this.el.dom.value;
56396 onClick : function(e){
56397 //this.setChecked(!this.checked);
56398 Roo.get(e.target).toggleClass('x-menu-item-checked');
56399 this.refreshValue();
56400 //if(this.el.dom.checked != this.checked){
56401 // this.setValue(this.el.dom.checked);
56406 refreshValue : function()
56409 this.viewEl.select('img',true).each(function(e,i,n) {
56410 val += e.is(".x-menu-item-checked") ? String(n) : '';
56412 this.setValue(val, true);
56416 * Sets the checked state of the checkbox.
56417 * On is always based on a string comparison between inputValue and the param.
56418 * @param {Boolean/String} value - the value to set
56419 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56421 setValue : function(v,suppressEvent){
56422 if (!this.el.dom) {
56425 var old = this.el.dom.value ;
56426 this.el.dom.value = v;
56427 if (suppressEvent) {
56431 // update display..
56432 this.viewEl.select('img',true).each(function(e,i,n) {
56434 var on = e.is(".x-menu-item-checked");
56435 var newv = v.indexOf(String(n)) > -1;
56437 e.toggleClass('x-menu-item-checked');
56443 this.fireEvent('change', this, v, old);
56448 // handle setting of hidden value by some other method!!?!?
56449 setFromHidden: function()
56454 //console.log("SET FROM HIDDEN");
56455 //alert('setFrom hidden');
56456 this.setValue(this.el.dom.value);
56459 onDestroy : function()
56462 Roo.get(this.viewEl).remove();
56465 Roo.form.DayPicker.superclass.onDestroy.call(this);
56469 * RooJS Library 1.1.1
56470 * Copyright(c) 2008-2011 Alan Knowles
56477 * @class Roo.form.ComboCheck
56478 * @extends Roo.form.ComboBox
56479 * A combobox for multiple select items.
56481 * FIXME - could do with a reset button..
56484 * Create a new ComboCheck
56485 * @param {Object} config Configuration options
56487 Roo.form.ComboCheck = function(config){
56488 Roo.form.ComboCheck.superclass.constructor.call(this, config);
56489 // should verify some data...
56491 // hiddenName = required..
56492 // displayField = required
56493 // valudField == required
56494 var req= [ 'hiddenName', 'displayField', 'valueField' ];
56496 Roo.each(req, function(e) {
56497 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56498 throw "Roo.form.ComboCheck : missing value for: " + e;
56505 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56510 selectedClass: 'x-menu-item-checked',
56513 onRender : function(ct, position){
56519 var cls = 'x-combo-list';
56522 this.tpl = new Roo.Template({
56523 html : '<div class="'+cls+'-item x-menu-check-item">' +
56524 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
56525 '<span>{' + this.displayField + '}</span>' +
56532 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56533 this.view.singleSelect = false;
56534 this.view.multiSelect = true;
56535 this.view.toggleSelect = true;
56536 this.pageTb.add(new Roo.Toolbar.Fill(), {
56539 handler: function()
56546 onViewOver : function(e, t){
56552 onViewClick : function(doFocus,index){
56556 select: function () {
56557 //Roo.log("SELECT CALLED");
56560 selectByValue : function(xv, scrollIntoView){
56561 var ar = this.getValueArray();
56564 Roo.each(ar, function(v) {
56565 if(v === undefined || v === null){
56568 var r = this.findRecord(this.valueField, v);
56570 sels.push(this.store.indexOf(r))
56574 this.view.select(sels);
56580 onSelect : function(record, index){
56581 // Roo.log("onselect Called");
56582 // this is only called by the clear button now..
56583 this.view.clearSelections();
56584 this.setValue('[]');
56585 if (this.value != this.valueBefore) {
56586 this.fireEvent('change', this, this.value, this.valueBefore);
56587 this.valueBefore = this.value;
56590 getValueArray : function()
56595 //Roo.log(this.value);
56596 if (typeof(this.value) == 'undefined') {
56599 var ar = Roo.decode(this.value);
56600 return ar instanceof Array ? ar : []; //?? valid?
56603 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
56608 expand : function ()
56611 Roo.form.ComboCheck.superclass.expand.call(this);
56612 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56613 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56618 collapse : function(){
56619 Roo.form.ComboCheck.superclass.collapse.call(this);
56620 var sl = this.view.getSelectedIndexes();
56621 var st = this.store;
56625 Roo.each(sl, function(i) {
56627 nv.push(r.get(this.valueField));
56629 this.setValue(Roo.encode(nv));
56630 if (this.value != this.valueBefore) {
56632 this.fireEvent('change', this, this.value, this.valueBefore);
56633 this.valueBefore = this.value;
56638 setValue : function(v){
56642 var vals = this.getValueArray();
56644 Roo.each(vals, function(k) {
56645 var r = this.findRecord(this.valueField, k);
56647 tv.push(r.data[this.displayField]);
56648 }else if(this.valueNotFoundText !== undefined){
56649 tv.push( this.valueNotFoundText );
56654 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56655 this.hiddenField.value = v;
56661 * Ext JS Library 1.1.1
56662 * Copyright(c) 2006-2007, Ext JS, LLC.
56664 * Originally Released Under LGPL - original licence link has changed is not relivant.
56667 * <script type="text/javascript">
56671 * @class Roo.form.Signature
56672 * @extends Roo.form.Field
56676 * @param {Object} config Configuration options
56679 Roo.form.Signature = function(config){
56680 Roo.form.Signature.superclass.constructor.call(this, config);
56682 this.addEvents({// not in used??
56685 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56686 * @param {Roo.form.Signature} combo This combo box
56691 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56692 * @param {Roo.form.ComboBox} combo This combo box
56693 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56699 Roo.extend(Roo.form.Signature, Roo.form.Field, {
56701 * @cfg {Object} labels Label to use when rendering a form.
56705 * confirm : "Confirm"
56710 confirm : "Confirm"
56713 * @cfg {Number} width The signature panel width (defaults to 300)
56717 * @cfg {Number} height The signature panel height (defaults to 100)
56721 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56723 allowBlank : false,
56726 // {Object} signPanel The signature SVG panel element (defaults to {})
56728 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56729 isMouseDown : false,
56730 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56731 isConfirmed : false,
56732 // {String} signatureTmp SVG mapping string (defaults to empty string)
56736 defaultAutoCreate : { // modified by initCompnoent..
56742 onRender : function(ct, position){
56744 Roo.form.Signature.superclass.onRender.call(this, ct, position);
56746 this.wrap = this.el.wrap({
56747 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
56750 this.createToolbar(this);
56751 this.signPanel = this.wrap.createChild({
56753 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
56757 this.svgID = Roo.id();
56758 this.svgEl = this.signPanel.createChild({
56759 xmlns : 'http://www.w3.org/2000/svg',
56761 id : this.svgID + "-svg",
56763 height: this.height,
56764 viewBox: '0 0 '+this.width+' '+this.height,
56768 id: this.svgID + "-svg-r",
56770 height: this.height,
56775 id: this.svgID + "-svg-l",
56777 y1: (this.height*0.8), // start set the line in 80% of height
56778 x2: this.width, // end
56779 y2: (this.height*0.8), // end set the line in 80% of height
56781 'stroke-width': "1",
56782 'stroke-dasharray': "3",
56783 'shape-rendering': "crispEdges",
56784 'pointer-events': "none"
56788 id: this.svgID + "-svg-p",
56790 'stroke-width': "3",
56792 'pointer-events': 'none'
56797 this.svgBox = this.svgEl.dom.getScreenCTM();
56799 createSVG : function(){
56800 var svg = this.signPanel;
56801 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
56804 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
56805 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
56806 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
56807 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
56808 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
56809 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
56810 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
56813 isTouchEvent : function(e){
56814 return e.type.match(/^touch/);
56816 getCoords : function (e) {
56817 var pt = this.svgEl.dom.createSVGPoint();
56820 if (this.isTouchEvent(e)) {
56821 pt.x = e.targetTouches[0].clientX;
56822 pt.y = e.targetTouches[0].clientY;
56824 var a = this.svgEl.dom.getScreenCTM();
56825 var b = a.inverse();
56826 var mx = pt.matrixTransform(b);
56827 return mx.x + ',' + mx.y;
56829 //mouse event headler
56830 down : function (e) {
56831 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
56832 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
56834 this.isMouseDown = true;
56836 e.preventDefault();
56838 move : function (e) {
56839 if (this.isMouseDown) {
56840 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
56841 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
56844 e.preventDefault();
56846 up : function (e) {
56847 this.isMouseDown = false;
56848 var sp = this.signatureTmp.split(' ');
56851 if(!sp[sp.length-2].match(/^L/)){
56855 this.signatureTmp = sp.join(" ");
56858 if(this.getValue() != this.signatureTmp){
56859 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56860 this.isConfirmed = false;
56862 e.preventDefault();
56866 * Protected method that will not generally be called directly. It
56867 * is called when the editor creates its toolbar. Override this method if you need to
56868 * add custom toolbar buttons.
56869 * @param {HtmlEditor} editor
56871 createToolbar : function(editor){
56872 function btn(id, toggle, handler){
56873 var xid = fid + '-'+ id ;
56877 cls : 'x-btn-icon x-edit-'+id,
56878 enableToggle:toggle !== false,
56879 scope: editor, // was editor...
56880 handler:handler||editor.relayBtnCmd,
56881 clickEvent:'mousedown',
56882 tooltip: etb.buttonTips[id] || undefined, ///tips ???
56888 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
56892 cls : ' x-signature-btn x-signature-'+id,
56893 scope: editor, // was editor...
56894 handler: this.reset,
56895 clickEvent:'mousedown',
56896 text: this.labels.clear
56903 cls : ' x-signature-btn x-signature-'+id,
56904 scope: editor, // was editor...
56905 handler: this.confirmHandler,
56906 clickEvent:'mousedown',
56907 text: this.labels.confirm
56914 * when user is clicked confirm then show this image.....
56916 * @return {String} Image Data URI
56918 getImageDataURI : function(){
56919 var svg = this.svgEl.dom.parentNode.innerHTML;
56920 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
56925 * @return {Boolean} this.isConfirmed
56927 getConfirmed : function(){
56928 return this.isConfirmed;
56932 * @return {Number} this.width
56934 getWidth : function(){
56939 * @return {Number} this.height
56941 getHeight : function(){
56942 return this.height;
56945 getSignature : function(){
56946 return this.signatureTmp;
56949 reset : function(){
56950 this.signatureTmp = '';
56951 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56952 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
56953 this.isConfirmed = false;
56954 Roo.form.Signature.superclass.reset.call(this);
56956 setSignature : function(s){
56957 this.signatureTmp = s;
56958 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56959 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
56961 this.isConfirmed = false;
56962 Roo.form.Signature.superclass.reset.call(this);
56965 // Roo.log(this.signPanel.dom.contentWindow.up())
56968 setConfirmed : function(){
56972 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
56975 confirmHandler : function(){
56976 if(!this.getSignature()){
56980 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
56981 this.setValue(this.getSignature());
56982 this.isConfirmed = true;
56984 this.fireEvent('confirm', this);
56987 // Subclasses should provide the validation implementation by overriding this
56988 validateValue : function(value){
56989 if(this.allowBlank){
56993 if(this.isConfirmed){
57000 * Ext JS Library 1.1.1
57001 * Copyright(c) 2006-2007, Ext JS, LLC.
57003 * Originally Released Under LGPL - original licence link has changed is not relivant.
57006 * <script type="text/javascript">
57011 * @class Roo.form.ComboBox
57012 * @extends Roo.form.TriggerField
57013 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57015 * Create a new ComboBox.
57016 * @param {Object} config Configuration options
57018 Roo.form.Select = function(config){
57019 Roo.form.Select.superclass.constructor.call(this, config);
57023 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57025 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57028 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57029 * rendering into an Roo.Editor, defaults to false)
57032 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57033 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57036 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57039 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57040 * the dropdown list (defaults to undefined, with no header element)
57044 * @cfg {String/Roo.Template} tpl The template to use to render the output
57048 defaultAutoCreate : {tag: "select" },
57050 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57052 listWidth: undefined,
57054 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57055 * mode = 'remote' or 'text' if mode = 'local')
57057 displayField: undefined,
57059 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57060 * mode = 'remote' or 'value' if mode = 'local').
57061 * Note: use of a valueField requires the user make a selection
57062 * in order for a value to be mapped.
57064 valueField: undefined,
57068 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57069 * field's data value (defaults to the underlying DOM element's name)
57071 hiddenName: undefined,
57073 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57077 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57079 selectedClass: 'x-combo-selected',
57081 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
57082 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57083 * which displays a downward arrow icon).
57085 triggerClass : 'x-form-arrow-trigger',
57087 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57091 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57092 * anchor positions (defaults to 'tl-bl')
57094 listAlign: 'tl-bl?',
57096 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57100 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
57101 * query specified by the allQuery config option (defaults to 'query')
57103 triggerAction: 'query',
57105 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57106 * (defaults to 4, does not apply if editable = false)
57110 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57111 * delay (typeAheadDelay) if it matches a known value (defaults to false)
57115 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57116 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57120 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57121 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
57125 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
57126 * when editable = true (defaults to false)
57128 selectOnFocus:false,
57130 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57132 queryParam: 'query',
57134 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
57135 * when mode = 'remote' (defaults to 'Loading...')
57137 loadingText: 'Loading...',
57139 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57143 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57147 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57148 * traditional select (defaults to true)
57152 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57156 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57160 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57161 * listWidth has a higher value)
57165 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57166 * allow the user to set arbitrary text into the field (defaults to false)
57168 forceSelection:false,
57170 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57171 * if typeAhead = true (defaults to 250)
57173 typeAheadDelay : 250,
57175 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57176 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57178 valueNotFoundText : undefined,
57181 * @cfg {String} defaultValue The value displayed after loading the store.
57186 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57188 blockFocus : false,
57191 * @cfg {Boolean} disableClear Disable showing of clear button.
57193 disableClear : false,
57195 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
57197 alwaysQuery : false,
57203 // element that contains real text value.. (when hidden is used..)
57206 onRender : function(ct, position){
57207 Roo.form.Field.prototype.onRender.call(this, ct, position);
57210 this.store.on('beforeload', this.onBeforeLoad, this);
57211 this.store.on('load', this.onLoad, this);
57212 this.store.on('loadexception', this.onLoadException, this);
57213 this.store.load({});
57221 initEvents : function(){
57222 //Roo.form.ComboBox.superclass.initEvents.call(this);
57226 onDestroy : function(){
57229 this.store.un('beforeload', this.onBeforeLoad, this);
57230 this.store.un('load', this.onLoad, this);
57231 this.store.un('loadexception', this.onLoadException, this);
57233 //Roo.form.ComboBox.superclass.onDestroy.call(this);
57237 fireKey : function(e){
57238 if(e.isNavKeyPress() && !this.list.isVisible()){
57239 this.fireEvent("specialkey", this, e);
57244 onResize: function(w, h){
57252 * Allow or prevent the user from directly editing the field text. If false is passed,
57253 * the user will only be able to select from the items defined in the dropdown list. This method
57254 * is the runtime equivalent of setting the 'editable' config option at config time.
57255 * @param {Boolean} value True to allow the user to directly edit the field text
57257 setEditable : function(value){
57262 onBeforeLoad : function(){
57264 Roo.log("Select before load");
57267 this.innerList.update(this.loadingText ?
57268 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57269 //this.restrictHeight();
57270 this.selectedIndex = -1;
57274 onLoad : function(){
57277 var dom = this.el.dom;
57278 dom.innerHTML = '';
57279 var od = dom.ownerDocument;
57281 if (this.emptyText) {
57282 var op = od.createElement('option');
57283 op.setAttribute('value', '');
57284 op.innerHTML = String.format('{0}', this.emptyText);
57285 dom.appendChild(op);
57287 if(this.store.getCount() > 0){
57289 var vf = this.valueField;
57290 var df = this.displayField;
57291 this.store.data.each(function(r) {
57292 // which colmsn to use... testing - cdoe / title..
57293 var op = od.createElement('option');
57294 op.setAttribute('value', r.data[vf]);
57295 op.innerHTML = String.format('{0}', r.data[df]);
57296 dom.appendChild(op);
57298 if (typeof(this.defaultValue != 'undefined')) {
57299 this.setValue(this.defaultValue);
57304 //this.onEmptyResults();
57309 onLoadException : function()
57311 dom.innerHTML = '';
57313 Roo.log("Select on load exception");
57317 Roo.log(this.store.reader.jsonData);
57318 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57319 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57325 onTypeAhead : function(){
57330 onSelect : function(record, index){
57331 Roo.log('on select?');
57333 if(this.fireEvent('beforeselect', this, record, index) !== false){
57334 this.setFromData(index > -1 ? record.data : false);
57336 this.fireEvent('select', this, record, index);
57341 * Returns the currently selected field value or empty string if no value is set.
57342 * @return {String} value The selected value
57344 getValue : function(){
57345 var dom = this.el.dom;
57346 this.value = dom.options[dom.selectedIndex].value;
57352 * Clears any text/value currently set in the field
57354 clearValue : function(){
57356 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57361 * Sets the specified value into the field. If the value finds a match, the corresponding record text
57362 * will be displayed in the field. If the value does not match the data value of an existing item,
57363 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57364 * Otherwise the field will be blank (although the value will still be set).
57365 * @param {String} value The value to match
57367 setValue : function(v){
57368 var d = this.el.dom;
57369 for (var i =0; i < d.options.length;i++) {
57370 if (v == d.options[i].value) {
57371 d.selectedIndex = i;
57379 * @property {Object} the last set data for the element
57384 * Sets the value of the field based on a object which is related to the record format for the store.
57385 * @param {Object} value the value to set as. or false on reset?
57387 setFromData : function(o){
57388 Roo.log('setfrom data?');
57394 reset : function(){
57398 findRecord : function(prop, value){
57403 if(this.store.getCount() > 0){
57404 this.store.each(function(r){
57405 if(r.data[prop] == value){
57415 getName: function()
57417 // returns hidden if it's set..
57418 if (!this.rendered) {return ''};
57419 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
57427 onEmptyResults : function(){
57428 Roo.log('empty results');
57433 * Returns true if the dropdown list is expanded, else false.
57435 isExpanded : function(){
57440 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57441 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57442 * @param {String} value The data value of the item to select
57443 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57444 * selected item if it is not currently in view (defaults to true)
57445 * @return {Boolean} True if the value matched an item in the list, else false
57447 selectByValue : function(v, scrollIntoView){
57448 Roo.log('select By Value');
57451 if(v !== undefined && v !== null){
57452 var r = this.findRecord(this.valueField || this.displayField, v);
57454 this.select(this.store.indexOf(r), scrollIntoView);
57462 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57463 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57464 * @param {Number} index The zero-based index of the list item to select
57465 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57466 * selected item if it is not currently in view (defaults to true)
57468 select : function(index, scrollIntoView){
57469 Roo.log('select ');
57472 this.selectedIndex = index;
57473 this.view.select(index);
57474 if(scrollIntoView !== false){
57475 var el = this.view.getNode(index);
57477 this.innerList.scrollChildIntoView(el, false);
57485 validateBlur : function(){
57492 initQuery : function(){
57493 this.doQuery(this.getRawValue());
57497 doForce : function(){
57498 if(this.el.dom.value.length > 0){
57499 this.el.dom.value =
57500 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57506 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
57507 * query allowing the query action to be canceled if needed.
57508 * @param {String} query The SQL query to execute
57509 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57510 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
57511 * saved in the current store (defaults to false)
57513 doQuery : function(q, forceAll){
57515 Roo.log('doQuery?');
57516 if(q === undefined || q === null){
57521 forceAll: forceAll,
57525 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57529 forceAll = qe.forceAll;
57530 if(forceAll === true || (q.length >= this.minChars)){
57531 if(this.lastQuery != q || this.alwaysQuery){
57532 this.lastQuery = q;
57533 if(this.mode == 'local'){
57534 this.selectedIndex = -1;
57536 this.store.clearFilter();
57538 this.store.filter(this.displayField, q);
57542 this.store.baseParams[this.queryParam] = q;
57544 params: this.getParams(q)
57549 this.selectedIndex = -1;
57556 getParams : function(q){
57558 //p[this.queryParam] = q;
57561 p.limit = this.pageSize;
57567 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57569 collapse : function(){
57574 collapseIf : function(e){
57579 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57581 expand : function(){
57589 * @cfg {Boolean} grow
57593 * @cfg {Number} growMin
57597 * @cfg {Number} growMax
57605 setWidth : function()
57609 getResizeEl : function(){
57612 });//<script type="text/javasscript">
57616 * @class Roo.DDView
57617 * A DnD enabled version of Roo.View.
57618 * @param {Element/String} container The Element in which to create the View.
57619 * @param {String} tpl The template string used to create the markup for each element of the View
57620 * @param {Object} config The configuration properties. These include all the config options of
57621 * {@link Roo.View} plus some specific to this class.<br>
57623 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57624 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57626 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57627 .x-view-drag-insert-above {
57628 border-top:1px dotted #3366cc;
57630 .x-view-drag-insert-below {
57631 border-bottom:1px dotted #3366cc;
57637 Roo.DDView = function(container, tpl, config) {
57638 Roo.DDView.superclass.constructor.apply(this, arguments);
57639 this.getEl().setStyle("outline", "0px none");
57640 this.getEl().unselectable();
57641 if (this.dragGroup) {
57642 this.setDraggable(this.dragGroup.split(","));
57644 if (this.dropGroup) {
57645 this.setDroppable(this.dropGroup.split(","));
57647 if (this.deletable) {
57648 this.setDeletable();
57650 this.isDirtyFlag = false;
57656 Roo.extend(Roo.DDView, Roo.View, {
57657 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57658 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57659 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57660 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57664 reset: Roo.emptyFn,
57666 clearInvalid: Roo.form.Field.prototype.clearInvalid,
57668 validate: function() {
57672 destroy: function() {
57673 this.purgeListeners();
57674 this.getEl.removeAllListeners();
57675 this.getEl().remove();
57676 if (this.dragZone) {
57677 if (this.dragZone.destroy) {
57678 this.dragZone.destroy();
57681 if (this.dropZone) {
57682 if (this.dropZone.destroy) {
57683 this.dropZone.destroy();
57688 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57689 getName: function() {
57693 /** Loads the View from a JSON string representing the Records to put into the Store. */
57694 setValue: function(v) {
57696 throw "DDView.setValue(). DDView must be constructed with a valid Store";
57699 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57700 this.store.proxy = new Roo.data.MemoryProxy(data);
57704 /** @return {String} a parenthesised list of the ids of the Records in the View. */
57705 getValue: function() {
57707 this.store.each(function(rec) {
57708 result += rec.id + ',';
57710 return result.substr(0, result.length - 1) + ')';
57713 getIds: function() {
57714 var i = 0, result = new Array(this.store.getCount());
57715 this.store.each(function(rec) {
57716 result[i++] = rec.id;
57721 isDirty: function() {
57722 return this.isDirtyFlag;
57726 * Part of the Roo.dd.DropZone interface. If no target node is found, the
57727 * whole Element becomes the target, and this causes the drop gesture to append.
57729 getTargetFromEvent : function(e) {
57730 var target = e.getTarget();
57731 while ((target !== null) && (target.parentNode != this.el.dom)) {
57732 target = target.parentNode;
57735 target = this.el.dom.lastChild || this.el.dom;
57741 * Create the drag data which consists of an object which has the property "ddel" as
57742 * the drag proxy element.
57744 getDragData : function(e) {
57745 var target = this.findItemFromChild(e.getTarget());
57747 this.handleSelection(e);
57748 var selNodes = this.getSelectedNodes();
57751 copy: this.copy || (this.allowCopy && e.ctrlKey),
57755 var selectedIndices = this.getSelectedIndexes();
57756 for (var i = 0; i < selectedIndices.length; i++) {
57757 dragData.records.push(this.store.getAt(selectedIndices[i]));
57759 if (selNodes.length == 1) {
57760 dragData.ddel = target.cloneNode(true); // the div element
57762 var div = document.createElement('div'); // create the multi element drag "ghost"
57763 div.className = 'multi-proxy';
57764 for (var i = 0, len = selNodes.length; i < len; i++) {
57765 div.appendChild(selNodes[i].cloneNode(true));
57767 dragData.ddel = div;
57769 //console.log(dragData)
57770 //console.log(dragData.ddel.innerHTML)
57773 //console.log('nodragData')
57777 /** Specify to which ddGroup items in this DDView may be dragged. */
57778 setDraggable: function(ddGroup) {
57779 if (ddGroup instanceof Array) {
57780 Roo.each(ddGroup, this.setDraggable, this);
57783 if (this.dragZone) {
57784 this.dragZone.addToGroup(ddGroup);
57786 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
57787 containerScroll: true,
57791 // Draggability implies selection. DragZone's mousedown selects the element.
57792 if (!this.multiSelect) { this.singleSelect = true; }
57794 // Wire the DragZone's handlers up to methods in *this*
57795 this.dragZone.getDragData = this.getDragData.createDelegate(this);
57799 /** Specify from which ddGroup this DDView accepts drops. */
57800 setDroppable: function(ddGroup) {
57801 if (ddGroup instanceof Array) {
57802 Roo.each(ddGroup, this.setDroppable, this);
57805 if (this.dropZone) {
57806 this.dropZone.addToGroup(ddGroup);
57808 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
57809 containerScroll: true,
57813 // Wire the DropZone's handlers up to methods in *this*
57814 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
57815 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
57816 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
57817 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
57818 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
57822 /** Decide whether to drop above or below a View node. */
57823 getDropPoint : function(e, n, dd){
57824 if (n == this.el.dom) { return "above"; }
57825 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
57826 var c = t + (b - t) / 2;
57827 var y = Roo.lib.Event.getPageY(e);
57835 onNodeEnter : function(n, dd, e, data){
57839 onNodeOver : function(n, dd, e, data){
57840 var pt = this.getDropPoint(e, n, dd);
57841 // set the insert point style on the target node
57842 var dragElClass = this.dropNotAllowed;
57845 if (pt == "above"){
57846 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
57847 targetElClass = "x-view-drag-insert-above";
57849 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
57850 targetElClass = "x-view-drag-insert-below";
57852 if (this.lastInsertClass != targetElClass){
57853 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
57854 this.lastInsertClass = targetElClass;
57857 return dragElClass;
57860 onNodeOut : function(n, dd, e, data){
57861 this.removeDropIndicators(n);
57864 onNodeDrop : function(n, dd, e, data){
57865 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
57868 var pt = this.getDropPoint(e, n, dd);
57869 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
57870 if (pt == "below") { insertAt++; }
57871 for (var i = 0; i < data.records.length; i++) {
57872 var r = data.records[i];
57873 var dup = this.store.getById(r.id);
57874 if (dup && (dd != this.dragZone)) {
57875 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
57878 this.store.insert(insertAt++, r.copy());
57880 data.source.isDirtyFlag = true;
57882 this.store.insert(insertAt++, r);
57884 this.isDirtyFlag = true;
57887 this.dragZone.cachedTarget = null;
57891 removeDropIndicators : function(n){
57893 Roo.fly(n).removeClass([
57894 "x-view-drag-insert-above",
57895 "x-view-drag-insert-below"]);
57896 this.lastInsertClass = "_noclass";
57901 * Utility method. Add a delete option to the DDView's context menu.
57902 * @param {String} imageUrl The URL of the "delete" icon image.
57904 setDeletable: function(imageUrl) {
57905 if (!this.singleSelect && !this.multiSelect) {
57906 this.singleSelect = true;
57908 var c = this.getContextMenu();
57909 this.contextMenu.on("itemclick", function(item) {
57912 this.remove(this.getSelectedIndexes());
57916 this.contextMenu.add({
57923 /** Return the context menu for this DDView. */
57924 getContextMenu: function() {
57925 if (!this.contextMenu) {
57926 // Create the View's context menu
57927 this.contextMenu = new Roo.menu.Menu({
57928 id: this.id + "-contextmenu"
57930 this.el.on("contextmenu", this.showContextMenu, this);
57932 return this.contextMenu;
57935 disableContextMenu: function() {
57936 if (this.contextMenu) {
57937 this.el.un("contextmenu", this.showContextMenu, this);
57941 showContextMenu: function(e, item) {
57942 item = this.findItemFromChild(e.getTarget());
57945 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
57946 this.contextMenu.showAt(e.getXY());
57951 * Remove {@link Roo.data.Record}s at the specified indices.
57952 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
57954 remove: function(selectedIndices) {
57955 selectedIndices = [].concat(selectedIndices);
57956 for (var i = 0; i < selectedIndices.length; i++) {
57957 var rec = this.store.getAt(selectedIndices[i]);
57958 this.store.remove(rec);
57963 * Double click fires the event, but also, if this is draggable, and there is only one other
57964 * related DropZone, it transfers the selected node.
57966 onDblClick : function(e){
57967 var item = this.findItemFromChild(e.getTarget());
57969 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
57972 if (this.dragGroup) {
57973 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
57974 while (targets.indexOf(this.dropZone) > -1) {
57975 targets.remove(this.dropZone);
57977 if (targets.length == 1) {
57978 this.dragZone.cachedTarget = null;
57979 var el = Roo.get(targets[0].getEl());
57980 var box = el.getBox(true);
57981 targets[0].onNodeDrop(el.dom, {
57983 xy: [box.x, box.y + box.height - 1]
57984 }, null, this.getDragData(e));
57990 handleSelection: function(e) {
57991 this.dragZone.cachedTarget = null;
57992 var item = this.findItemFromChild(e.getTarget());
57994 this.clearSelections(true);
57997 if (item && (this.multiSelect || this.singleSelect)){
57998 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
57999 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58000 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58001 this.unselect(item);
58003 this.select(item, this.multiSelect && e.ctrlKey);
58004 this.lastSelection = item;
58009 onItemClick : function(item, index, e){
58010 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58016 unselect : function(nodeInfo, suppressEvent){
58017 var node = this.getNode(nodeInfo);
58018 if(node && this.isSelected(node)){
58019 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58020 Roo.fly(node).removeClass(this.selectedClass);
58021 this.selections.remove(node);
58022 if(!suppressEvent){
58023 this.fireEvent("selectionchange", this, this.selections);
58031 * Ext JS Library 1.1.1
58032 * Copyright(c) 2006-2007, Ext JS, LLC.
58034 * Originally Released Under LGPL - original licence link has changed is not relivant.
58037 * <script type="text/javascript">
58041 * @class Roo.LayoutManager
58042 * @extends Roo.util.Observable
58043 * Base class for layout managers.
58045 Roo.LayoutManager = function(container, config){
58046 Roo.LayoutManager.superclass.constructor.call(this);
58047 this.el = Roo.get(container);
58048 // ie scrollbar fix
58049 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58050 document.body.scroll = "no";
58051 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58052 this.el.position('relative');
58054 this.id = this.el.id;
58055 this.el.addClass("x-layout-container");
58056 /** false to disable window resize monitoring @type Boolean */
58057 this.monitorWindowResize = true;
58062 * Fires when a layout is performed.
58063 * @param {Roo.LayoutManager} this
58067 * @event regionresized
58068 * Fires when the user resizes a region.
58069 * @param {Roo.LayoutRegion} region The resized region
58070 * @param {Number} newSize The new size (width for east/west, height for north/south)
58072 "regionresized" : true,
58074 * @event regioncollapsed
58075 * Fires when a region is collapsed.
58076 * @param {Roo.LayoutRegion} region The collapsed region
58078 "regioncollapsed" : true,
58080 * @event regionexpanded
58081 * Fires when a region is expanded.
58082 * @param {Roo.LayoutRegion} region The expanded region
58084 "regionexpanded" : true
58086 this.updating = false;
58087 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58090 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58092 * Returns true if this layout is currently being updated
58093 * @return {Boolean}
58095 isUpdating : function(){
58096 return this.updating;
58100 * Suspend the LayoutManager from doing auto-layouts while
58101 * making multiple add or remove calls
58103 beginUpdate : function(){
58104 this.updating = true;
58108 * Restore auto-layouts and optionally disable the manager from performing a layout
58109 * @param {Boolean} noLayout true to disable a layout update
58111 endUpdate : function(noLayout){
58112 this.updating = false;
58118 layout: function(){
58122 onRegionResized : function(region, newSize){
58123 this.fireEvent("regionresized", region, newSize);
58127 onRegionCollapsed : function(region){
58128 this.fireEvent("regioncollapsed", region);
58131 onRegionExpanded : function(region){
58132 this.fireEvent("regionexpanded", region);
58136 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58137 * performs box-model adjustments.
58138 * @return {Object} The size as an object {width: (the width), height: (the height)}
58140 getViewSize : function(){
58142 if(this.el.dom != document.body){
58143 size = this.el.getSize();
58145 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58147 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58148 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58153 * Returns the Element this layout is bound to.
58154 * @return {Roo.Element}
58156 getEl : function(){
58161 * Returns the specified region.
58162 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58163 * @return {Roo.LayoutRegion}
58165 getRegion : function(target){
58166 return this.regions[target.toLowerCase()];
58169 onWindowResize : function(){
58170 if(this.monitorWindowResize){
58176 * Ext JS Library 1.1.1
58177 * Copyright(c) 2006-2007, Ext JS, LLC.
58179 * Originally Released Under LGPL - original licence link has changed is not relivant.
58182 * <script type="text/javascript">
58185 * @class Roo.BorderLayout
58186 * @extends Roo.LayoutManager
58187 * @children Roo.ContentPanel
58188 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58189 * please see: <br><br>
58190 * <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>
58191 * <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>
58194 var layout = new Roo.BorderLayout(document.body, {
58228 preferredTabWidth: 150
58233 var CP = Roo.ContentPanel;
58235 layout.beginUpdate();
58236 layout.add("north", new CP("north", "North"));
58237 layout.add("south", new CP("south", {title: "South", closable: true}));
58238 layout.add("west", new CP("west", {title: "West"}));
58239 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58240 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58241 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58242 layout.getRegion("center").showPanel("center1");
58243 layout.endUpdate();
58246 <b>The container the layout is rendered into can be either the body element or any other element.
58247 If it is not the body element, the container needs to either be an absolute positioned element,
58248 or you will need to add "position:relative" to the css of the container. You will also need to specify
58249 the container size if it is not the body element.</b>
58252 * Create a new BorderLayout
58253 * @param {String/HTMLElement/Element} container The container this layout is bound to
58254 * @param {Object} config Configuration options
58256 Roo.BorderLayout = function(container, config){
58257 config = config || {};
58258 Roo.BorderLayout.superclass.constructor.call(this, container, config);
58259 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58260 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58261 var target = this.factory.validRegions[i];
58262 if(config[target]){
58263 this.addRegion(target, config[target]);
58268 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58271 * @cfg {Roo.LayoutRegion} east
58274 * @cfg {Roo.LayoutRegion} west
58277 * @cfg {Roo.LayoutRegion} north
58280 * @cfg {Roo.LayoutRegion} south
58283 * @cfg {Roo.LayoutRegion} center
58286 * Creates and adds a new region if it doesn't already exist.
58287 * @param {String} target The target region key (north, south, east, west or center).
58288 * @param {Object} config The regions config object
58289 * @return {BorderLayoutRegion} The new region
58291 addRegion : function(target, config){
58292 if(!this.regions[target]){
58293 var r = this.factory.create(target, this, config);
58294 this.bindRegion(target, r);
58296 return this.regions[target];
58300 bindRegion : function(name, r){
58301 this.regions[name] = r;
58302 r.on("visibilitychange", this.layout, this);
58303 r.on("paneladded", this.layout, this);
58304 r.on("panelremoved", this.layout, this);
58305 r.on("invalidated", this.layout, this);
58306 r.on("resized", this.onRegionResized, this);
58307 r.on("collapsed", this.onRegionCollapsed, this);
58308 r.on("expanded", this.onRegionExpanded, this);
58312 * Performs a layout update.
58314 layout : function(){
58315 if(this.updating) {
58318 var size = this.getViewSize();
58319 var w = size.width;
58320 var h = size.height;
58325 //var x = 0, y = 0;
58327 var rs = this.regions;
58328 var north = rs["north"];
58329 var south = rs["south"];
58330 var west = rs["west"];
58331 var east = rs["east"];
58332 var center = rs["center"];
58333 //if(this.hideOnLayout){ // not supported anymore
58334 //c.el.setStyle("display", "none");
58336 if(north && north.isVisible()){
58337 var b = north.getBox();
58338 var m = north.getMargins();
58339 b.width = w - (m.left+m.right);
58342 centerY = b.height + b.y + m.bottom;
58343 centerH -= centerY;
58344 north.updateBox(this.safeBox(b));
58346 if(south && south.isVisible()){
58347 var b = south.getBox();
58348 var m = south.getMargins();
58349 b.width = w - (m.left+m.right);
58351 var totalHeight = (b.height + m.top + m.bottom);
58352 b.y = h - totalHeight + m.top;
58353 centerH -= totalHeight;
58354 south.updateBox(this.safeBox(b));
58356 if(west && west.isVisible()){
58357 var b = west.getBox();
58358 var m = west.getMargins();
58359 b.height = centerH - (m.top+m.bottom);
58361 b.y = centerY + m.top;
58362 var totalWidth = (b.width + m.left + m.right);
58363 centerX += totalWidth;
58364 centerW -= totalWidth;
58365 west.updateBox(this.safeBox(b));
58367 if(east && east.isVisible()){
58368 var b = east.getBox();
58369 var m = east.getMargins();
58370 b.height = centerH - (m.top+m.bottom);
58371 var totalWidth = (b.width + m.left + m.right);
58372 b.x = w - totalWidth + m.left;
58373 b.y = centerY + m.top;
58374 centerW -= totalWidth;
58375 east.updateBox(this.safeBox(b));
58378 var m = center.getMargins();
58380 x: centerX + m.left,
58381 y: centerY + m.top,
58382 width: centerW - (m.left+m.right),
58383 height: centerH - (m.top+m.bottom)
58385 //if(this.hideOnLayout){
58386 //center.el.setStyle("display", "block");
58388 center.updateBox(this.safeBox(centerBox));
58391 this.fireEvent("layout", this);
58395 safeBox : function(box){
58396 box.width = Math.max(0, box.width);
58397 box.height = Math.max(0, box.height);
58402 * Adds a ContentPanel (or subclass) to this layout.
58403 * @param {String} target The target region key (north, south, east, west or center).
58404 * @param {Roo.ContentPanel} panel The panel to add
58405 * @return {Roo.ContentPanel} The added panel
58407 add : function(target, panel){
58409 target = target.toLowerCase();
58410 return this.regions[target].add(panel);
58414 * Remove a ContentPanel (or subclass) to this layout.
58415 * @param {String} target The target region key (north, south, east, west or center).
58416 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58417 * @return {Roo.ContentPanel} The removed panel
58419 remove : function(target, panel){
58420 target = target.toLowerCase();
58421 return this.regions[target].remove(panel);
58425 * Searches all regions for a panel with the specified id
58426 * @param {String} panelId
58427 * @return {Roo.ContentPanel} The panel or null if it wasn't found
58429 findPanel : function(panelId){
58430 var rs = this.regions;
58431 for(var target in rs){
58432 if(typeof rs[target] != "function"){
58433 var p = rs[target].getPanel(panelId);
58443 * Searches all regions for a panel with the specified id and activates (shows) it.
58444 * @param {String/ContentPanel} panelId The panels id or the panel itself
58445 * @return {Roo.ContentPanel} The shown panel or null
58447 showPanel : function(panelId) {
58448 var rs = this.regions;
58449 for(var target in rs){
58450 var r = rs[target];
58451 if(typeof r != "function"){
58452 if(r.hasPanel(panelId)){
58453 return r.showPanel(panelId);
58461 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58462 * @param {Roo.state.Provider} provider (optional) An alternate state provider
58464 restoreState : function(provider){
58466 provider = Roo.state.Manager;
58468 var sm = new Roo.LayoutStateManager();
58469 sm.init(this, provider);
58473 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
58474 * object should contain properties for each region to add ContentPanels to, and each property's value should be
58475 * a valid ContentPanel config object. Example:
58477 // Create the main layout
58478 var layout = new Roo.BorderLayout('main-ct', {
58489 // Create and add multiple ContentPanels at once via configs
58492 id: 'source-files',
58494 title:'Ext Source Files',
58507 * @param {Object} regions An object containing ContentPanel configs by region name
58509 batchAdd : function(regions){
58510 this.beginUpdate();
58511 for(var rname in regions){
58512 var lr = this.regions[rname];
58514 this.addTypedPanels(lr, regions[rname]);
58521 addTypedPanels : function(lr, ps){
58522 if(typeof ps == 'string'){
58523 lr.add(new Roo.ContentPanel(ps));
58525 else if(ps instanceof Array){
58526 for(var i =0, len = ps.length; i < len; i++){
58527 this.addTypedPanels(lr, ps[i]);
58530 else if(!ps.events){ // raw config?
58532 delete ps.el; // prevent conflict
58533 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58535 else { // panel object assumed!
58540 * Adds a xtype elements to the layout.
58544 xtype : 'ContentPanel',
58551 xtype : 'NestedLayoutPanel',
58557 items : [ ... list of content panels or nested layout panels.. ]
58561 * @param {Object} cfg Xtype definition of item to add.
58563 addxtype : function(cfg)
58565 // basically accepts a pannel...
58566 // can accept a layout region..!?!?
58567 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58569 if (!cfg.xtype.match(/Panel$/)) {
58574 if (typeof(cfg.region) == 'undefined') {
58575 Roo.log("Failed to add Panel, region was not set");
58579 var region = cfg.region;
58585 xitems = cfg.items;
58592 case 'ContentPanel': // ContentPanel (el, cfg)
58593 case 'ScrollPanel': // ContentPanel (el, cfg)
58595 if(cfg.autoCreate) {
58596 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58598 var el = this.el.createChild();
58599 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58602 this.add(region, ret);
58606 case 'TreePanel': // our new panel!
58607 cfg.el = this.el.createChild();
58608 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58609 this.add(region, ret);
58612 case 'NestedLayoutPanel':
58613 // create a new Layout (which is a Border Layout...
58614 var el = this.el.createChild();
58615 var clayout = cfg.layout;
58617 clayout.items = clayout.items || [];
58618 // replace this exitems with the clayout ones..
58619 xitems = clayout.items;
58622 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58623 cfg.background = false;
58625 var layout = new Roo.BorderLayout(el, clayout);
58627 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58628 //console.log('adding nested layout panel ' + cfg.toSource());
58629 this.add(region, ret);
58630 nb = {}; /// find first...
58635 // needs grid and region
58637 //var el = this.getRegion(region).el.createChild();
58638 var el = this.el.createChild();
58639 // create the grid first...
58641 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58643 if (region == 'center' && this.active ) {
58644 cfg.background = false;
58646 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58648 this.add(region, ret);
58649 if (cfg.background) {
58650 ret.on('activate', function(gp) {
58651 if (!gp.grid.rendered) {
58666 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58668 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58669 this.add(region, ret);
58672 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58676 // GridPanel (grid, cfg)
58679 this.beginUpdate();
58683 Roo.each(xitems, function(i) {
58684 region = nb && i.region ? i.region : false;
58686 var add = ret.addxtype(i);
58689 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58690 if (!i.background) {
58691 abn[region] = nb[region] ;
58698 // make the last non-background panel active..
58699 //if (nb) { Roo.log(abn); }
58702 for(var r in abn) {
58703 region = this.getRegion(r);
58705 // tried using nb[r], but it does not work..
58707 region.showPanel(abn[r]);
58718 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58719 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
58720 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58721 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
58724 var CP = Roo.ContentPanel;
58726 var layout = Roo.BorderLayout.create({
58730 panels: [new CP("north", "North")]
58739 panels: [new CP("west", {title: "West"})]
58748 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
58757 panels: [new CP("south", {title: "South", closable: true})]
58764 preferredTabWidth: 150,
58766 new CP("center1", {title: "Close Me", closable: true}),
58767 new CP("center2", {title: "Center Panel", closable: false})
58772 layout.getRegion("center").showPanel("center1");
58777 Roo.BorderLayout.create = function(config, targetEl){
58778 var layout = new Roo.BorderLayout(targetEl || document.body, config);
58779 layout.beginUpdate();
58780 var regions = Roo.BorderLayout.RegionFactory.validRegions;
58781 for(var j = 0, jlen = regions.length; j < jlen; j++){
58782 var lr = regions[j];
58783 if(layout.regions[lr] && config[lr].panels){
58784 var r = layout.regions[lr];
58785 var ps = config[lr].panels;
58786 layout.addTypedPanels(r, ps);
58789 layout.endUpdate();
58794 Roo.BorderLayout.RegionFactory = {
58796 validRegions : ["north","south","east","west","center"],
58799 create : function(target, mgr, config){
58800 target = target.toLowerCase();
58801 if(config.lightweight || config.basic){
58802 return new Roo.BasicLayoutRegion(mgr, config, target);
58806 return new Roo.NorthLayoutRegion(mgr, config);
58808 return new Roo.SouthLayoutRegion(mgr, config);
58810 return new Roo.EastLayoutRegion(mgr, config);
58812 return new Roo.WestLayoutRegion(mgr, config);
58814 return new Roo.CenterLayoutRegion(mgr, config);
58816 throw 'Layout region "'+target+'" not supported.';
58820 * Ext JS Library 1.1.1
58821 * Copyright(c) 2006-2007, Ext JS, LLC.
58823 * Originally Released Under LGPL - original licence link has changed is not relivant.
58826 * <script type="text/javascript">
58830 * @class Roo.BasicLayoutRegion
58831 * @extends Roo.util.Observable
58832 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
58833 * and does not have a titlebar, tabs or any other features. All it does is size and position
58834 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
58836 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
58838 this.position = pos;
58841 * @scope Roo.BasicLayoutRegion
58845 * @event beforeremove
58846 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
58847 * @param {Roo.LayoutRegion} this
58848 * @param {Roo.ContentPanel} panel The panel
58849 * @param {Object} e The cancel event object
58851 "beforeremove" : true,
58853 * @event invalidated
58854 * Fires when the layout for this region is changed.
58855 * @param {Roo.LayoutRegion} this
58857 "invalidated" : true,
58859 * @event visibilitychange
58860 * Fires when this region is shown or hidden
58861 * @param {Roo.LayoutRegion} this
58862 * @param {Boolean} visibility true or false
58864 "visibilitychange" : true,
58866 * @event paneladded
58867 * Fires when a panel is added.
58868 * @param {Roo.LayoutRegion} this
58869 * @param {Roo.ContentPanel} panel The panel
58871 "paneladded" : true,
58873 * @event panelremoved
58874 * Fires when a panel is removed.
58875 * @param {Roo.LayoutRegion} this
58876 * @param {Roo.ContentPanel} panel The panel
58878 "panelremoved" : true,
58880 * @event beforecollapse
58881 * Fires when this region before collapse.
58882 * @param {Roo.LayoutRegion} this
58884 "beforecollapse" : true,
58887 * Fires when this region is collapsed.
58888 * @param {Roo.LayoutRegion} this
58890 "collapsed" : true,
58893 * Fires when this region is expanded.
58894 * @param {Roo.LayoutRegion} this
58899 * Fires when this region is slid into view.
58900 * @param {Roo.LayoutRegion} this
58902 "slideshow" : true,
58905 * Fires when this region slides out of view.
58906 * @param {Roo.LayoutRegion} this
58908 "slidehide" : true,
58910 * @event panelactivated
58911 * Fires when a panel is activated.
58912 * @param {Roo.LayoutRegion} this
58913 * @param {Roo.ContentPanel} panel The activated panel
58915 "panelactivated" : true,
58918 * Fires when the user resizes this region.
58919 * @param {Roo.LayoutRegion} this
58920 * @param {Number} newSize The new size (width for east/west, height for north/south)
58924 /** A collection of panels in this region. @type Roo.util.MixedCollection */
58925 this.panels = new Roo.util.MixedCollection();
58926 this.panels.getKey = this.getPanelId.createDelegate(this);
58928 this.activePanel = null;
58929 // ensure listeners are added...
58931 if (config.listeners || config.events) {
58932 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
58933 listeners : config.listeners || {},
58934 events : config.events || {}
58938 if(skipConfig !== true){
58939 this.applyConfig(config);
58943 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
58944 getPanelId : function(p){
58948 applyConfig : function(config){
58949 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
58950 this.config = config;
58955 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
58956 * the width, for horizontal (north, south) the height.
58957 * @param {Number} newSize The new width or height
58959 resizeTo : function(newSize){
58960 var el = this.el ? this.el :
58961 (this.activePanel ? this.activePanel.getEl() : null);
58963 switch(this.position){
58966 el.setWidth(newSize);
58967 this.fireEvent("resized", this, newSize);
58971 el.setHeight(newSize);
58972 this.fireEvent("resized", this, newSize);
58978 getBox : function(){
58979 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
58982 getMargins : function(){
58983 return this.margins;
58986 updateBox : function(box){
58988 var el = this.activePanel.getEl();
58989 el.dom.style.left = box.x + "px";
58990 el.dom.style.top = box.y + "px";
58991 this.activePanel.setSize(box.width, box.height);
58995 * Returns the container element for this region.
58996 * @return {Roo.Element}
58998 getEl : function(){
58999 return this.activePanel;
59003 * Returns true if this region is currently visible.
59004 * @return {Boolean}
59006 isVisible : function(){
59007 return this.activePanel ? true : false;
59010 setActivePanel : function(panel){
59011 panel = this.getPanel(panel);
59012 if(this.activePanel && this.activePanel != panel){
59013 this.activePanel.setActiveState(false);
59014 this.activePanel.getEl().setLeftTop(-10000,-10000);
59016 this.activePanel = panel;
59017 panel.setActiveState(true);
59019 panel.setSize(this.box.width, this.box.height);
59021 this.fireEvent("panelactivated", this, panel);
59022 this.fireEvent("invalidated");
59026 * Show the specified panel.
59027 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59028 * @return {Roo.ContentPanel} The shown panel or null
59030 showPanel : function(panel){
59031 if(panel = this.getPanel(panel)){
59032 this.setActivePanel(panel);
59038 * Get the active panel for this region.
59039 * @return {Roo.ContentPanel} The active panel or null
59041 getActivePanel : function(){
59042 return this.activePanel;
59046 * Add the passed ContentPanel(s)
59047 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59048 * @return {Roo.ContentPanel} The panel added (if only one was added)
59050 add : function(panel){
59051 if(arguments.length > 1){
59052 for(var i = 0, len = arguments.length; i < len; i++) {
59053 this.add(arguments[i]);
59057 if(this.hasPanel(panel)){
59058 this.showPanel(panel);
59061 var el = panel.getEl();
59062 if(el.dom.parentNode != this.mgr.el.dom){
59063 this.mgr.el.dom.appendChild(el.dom);
59065 if(panel.setRegion){
59066 panel.setRegion(this);
59068 this.panels.add(panel);
59069 el.setStyle("position", "absolute");
59070 if(!panel.background){
59071 this.setActivePanel(panel);
59072 if(this.config.initialSize && this.panels.getCount()==1){
59073 this.resizeTo(this.config.initialSize);
59076 this.fireEvent("paneladded", this, panel);
59081 * Returns true if the panel is in this region.
59082 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59083 * @return {Boolean}
59085 hasPanel : function(panel){
59086 if(typeof panel == "object"){ // must be panel obj
59087 panel = panel.getId();
59089 return this.getPanel(panel) ? true : false;
59093 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59094 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59095 * @param {Boolean} preservePanel Overrides the config preservePanel option
59096 * @return {Roo.ContentPanel} The panel that was removed
59098 remove : function(panel, preservePanel){
59099 panel = this.getPanel(panel);
59104 this.fireEvent("beforeremove", this, panel, e);
59105 if(e.cancel === true){
59108 var panelId = panel.getId();
59109 this.panels.removeKey(panelId);
59114 * Returns the panel specified or null if it's not in this region.
59115 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59116 * @return {Roo.ContentPanel}
59118 getPanel : function(id){
59119 if(typeof id == "object"){ // must be panel obj
59122 return this.panels.get(id);
59126 * Returns this regions position (north/south/east/west/center).
59129 getPosition: function(){
59130 return this.position;
59134 * Ext JS Library 1.1.1
59135 * Copyright(c) 2006-2007, Ext JS, LLC.
59137 * Originally Released Under LGPL - original licence link has changed is not relivant.
59140 * <script type="text/javascript">
59144 * @class Roo.LayoutRegion
59145 * @extends Roo.BasicLayoutRegion
59146 * This class represents a region in a layout manager.
59147 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
59148 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
59149 * @cfg {Boolean} floatable False to disable floating (defaults to true)
59150 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59151 * @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})
59152 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
59153 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
59154 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
59155 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
59156 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
59157 * @cfg {String} title The title for the region (overrides panel titles)
59158 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
59159 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59160 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
59161 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59162 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
59163 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59164 * the space available, similar to FireFox 1.5 tabs (defaults to false)
59165 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
59166 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
59167 * @cfg {Boolean} showPin True to show a pin button
59168 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
59169 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
59170 * @cfg {Boolean} disableTabTips True to disable tab tooltips
59171 * @cfg {Number} width For East/West panels
59172 * @cfg {Number} height For North/South panels
59173 * @cfg {Boolean} split To show the splitter
59174 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
59176 Roo.LayoutRegion = function(mgr, config, pos){
59177 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59178 var dh = Roo.DomHelper;
59179 /** This region's container element
59180 * @type Roo.Element */
59181 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59182 /** This region's title element
59183 * @type Roo.Element */
59185 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59186 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
59187 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59189 this.titleEl.enableDisplayMode();
59190 /** This region's title text element
59191 * @type HTMLElement */
59192 this.titleTextEl = this.titleEl.dom.firstChild;
59193 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59194 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59195 this.closeBtn.enableDisplayMode();
59196 this.closeBtn.on("click", this.closeClicked, this);
59197 this.closeBtn.hide();
59199 this.createBody(config);
59200 this.visible = true;
59201 this.collapsed = false;
59203 if(config.hideWhenEmpty){
59205 this.on("paneladded", this.validateVisibility, this);
59206 this.on("panelremoved", this.validateVisibility, this);
59208 this.applyConfig(config);
59211 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59213 createBody : function(){
59214 /** This region's body element
59215 * @type Roo.Element */
59216 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59219 applyConfig : function(c){
59220 if(c.collapsible && this.position != "center" && !this.collapsedEl){
59221 var dh = Roo.DomHelper;
59222 if(c.titlebar !== false){
59223 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59224 this.collapseBtn.on("click", this.collapse, this);
59225 this.collapseBtn.enableDisplayMode();
59227 if(c.showPin === true || this.showPin){
59228 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59229 this.stickBtn.enableDisplayMode();
59230 this.stickBtn.on("click", this.expand, this);
59231 this.stickBtn.hide();
59234 /** This region's collapsed element
59235 * @type Roo.Element */
59236 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59237 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59239 if(c.floatable !== false){
59240 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59241 this.collapsedEl.on("click", this.collapseClick, this);
59244 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59245 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59246 id: "message", unselectable: "on", style:{"float":"left"}});
59247 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59249 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59250 this.expandBtn.on("click", this.expand, this);
59252 if(this.collapseBtn){
59253 this.collapseBtn.setVisible(c.collapsible == true);
59255 this.cmargins = c.cmargins || this.cmargins ||
59256 (this.position == "west" || this.position == "east" ?
59257 {top: 0, left: 2, right:2, bottom: 0} :
59258 {top: 2, left: 0, right:0, bottom: 2});
59259 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59260 this.bottomTabs = c.tabPosition != "top";
59261 this.autoScroll = c.autoScroll || false;
59262 if(this.autoScroll){
59263 this.bodyEl.setStyle("overflow", "auto");
59265 this.bodyEl.setStyle("overflow", "hidden");
59267 //if(c.titlebar !== false){
59268 if((!c.titlebar && !c.title) || c.titlebar === false){
59269 this.titleEl.hide();
59271 this.titleEl.show();
59273 this.titleTextEl.innerHTML = c.title;
59277 this.duration = c.duration || .30;
59278 this.slideDuration = c.slideDuration || .45;
59281 this.collapse(true);
59288 * Returns true if this region is currently visible.
59289 * @return {Boolean}
59291 isVisible : function(){
59292 return this.visible;
59296 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59297 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
59299 setCollapsedTitle : function(title){
59300 title = title || " ";
59301 if(this.collapsedTitleTextEl){
59302 this.collapsedTitleTextEl.innerHTML = title;
59306 getBox : function(){
59308 if(!this.collapsed){
59309 b = this.el.getBox(false, true);
59311 b = this.collapsedEl.getBox(false, true);
59316 getMargins : function(){
59317 return this.collapsed ? this.cmargins : this.margins;
59320 highlight : function(){
59321 this.el.addClass("x-layout-panel-dragover");
59324 unhighlight : function(){
59325 this.el.removeClass("x-layout-panel-dragover");
59328 updateBox : function(box){
59330 if(!this.collapsed){
59331 this.el.dom.style.left = box.x + "px";
59332 this.el.dom.style.top = box.y + "px";
59333 this.updateBody(box.width, box.height);
59335 this.collapsedEl.dom.style.left = box.x + "px";
59336 this.collapsedEl.dom.style.top = box.y + "px";
59337 this.collapsedEl.setSize(box.width, box.height);
59340 this.tabs.autoSizeTabs();
59344 updateBody : function(w, h){
59346 this.el.setWidth(w);
59347 w -= this.el.getBorderWidth("rl");
59348 if(this.config.adjustments){
59349 w += this.config.adjustments[0];
59353 this.el.setHeight(h);
59354 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59355 h -= this.el.getBorderWidth("tb");
59356 if(this.config.adjustments){
59357 h += this.config.adjustments[1];
59359 this.bodyEl.setHeight(h);
59361 h = this.tabs.syncHeight(h);
59364 if(this.panelSize){
59365 w = w !== null ? w : this.panelSize.width;
59366 h = h !== null ? h : this.panelSize.height;
59368 if(this.activePanel){
59369 var el = this.activePanel.getEl();
59370 w = w !== null ? w : el.getWidth();
59371 h = h !== null ? h : el.getHeight();
59372 this.panelSize = {width: w, height: h};
59373 this.activePanel.setSize(w, h);
59375 if(Roo.isIE && this.tabs){
59376 this.tabs.el.repaint();
59381 * Returns the container element for this region.
59382 * @return {Roo.Element}
59384 getEl : function(){
59389 * Hides this region.
59392 if(!this.collapsed){
59393 this.el.dom.style.left = "-2000px";
59396 this.collapsedEl.dom.style.left = "-2000px";
59397 this.collapsedEl.hide();
59399 this.visible = false;
59400 this.fireEvent("visibilitychange", this, false);
59404 * Shows this region if it was previously hidden.
59407 if(!this.collapsed){
59410 this.collapsedEl.show();
59412 this.visible = true;
59413 this.fireEvent("visibilitychange", this, true);
59416 closeClicked : function(){
59417 if(this.activePanel){
59418 this.remove(this.activePanel);
59422 collapseClick : function(e){
59424 e.stopPropagation();
59427 e.stopPropagation();
59433 * Collapses this region.
59434 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59436 collapse : function(skipAnim, skipCheck){
59437 if(this.collapsed) {
59441 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59443 this.collapsed = true;
59445 this.split.el.hide();
59447 if(this.config.animate && skipAnim !== true){
59448 this.fireEvent("invalidated", this);
59449 this.animateCollapse();
59451 this.el.setLocation(-20000,-20000);
59453 this.collapsedEl.show();
59454 this.fireEvent("collapsed", this);
59455 this.fireEvent("invalidated", this);
59461 animateCollapse : function(){
59466 * Expands this region if it was previously collapsed.
59467 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59468 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59470 expand : function(e, skipAnim){
59472 e.stopPropagation();
59474 if(!this.collapsed || this.el.hasActiveFx()) {
59478 this.afterSlideIn();
59481 this.collapsed = false;
59482 if(this.config.animate && skipAnim !== true){
59483 this.animateExpand();
59487 this.split.el.show();
59489 this.collapsedEl.setLocation(-2000,-2000);
59490 this.collapsedEl.hide();
59491 this.fireEvent("invalidated", this);
59492 this.fireEvent("expanded", this);
59496 animateExpand : function(){
59500 initTabs : function()
59502 this.bodyEl.setStyle("overflow", "hidden");
59503 var ts = new Roo.TabPanel(
59506 tabPosition: this.bottomTabs ? 'bottom' : 'top',
59507 disableTooltips: this.config.disableTabTips,
59508 toolbar : this.config.toolbar
59511 if(this.config.hideTabs){
59512 ts.stripWrap.setDisplayed(false);
59515 ts.resizeTabs = this.config.resizeTabs === true;
59516 ts.minTabWidth = this.config.minTabWidth || 40;
59517 ts.maxTabWidth = this.config.maxTabWidth || 250;
59518 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59519 ts.monitorResize = false;
59520 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59521 ts.bodyEl.addClass('x-layout-tabs-body');
59522 this.panels.each(this.initPanelAsTab, this);
59525 initPanelAsTab : function(panel){
59526 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59527 this.config.closeOnTab && panel.isClosable());
59528 if(panel.tabTip !== undefined){
59529 ti.setTooltip(panel.tabTip);
59531 ti.on("activate", function(){
59532 this.setActivePanel(panel);
59534 if(this.config.closeOnTab){
59535 ti.on("beforeclose", function(t, e){
59537 this.remove(panel);
59543 updatePanelTitle : function(panel, title){
59544 if(this.activePanel == panel){
59545 this.updateTitle(title);
59548 var ti = this.tabs.getTab(panel.getEl().id);
59550 if(panel.tabTip !== undefined){
59551 ti.setTooltip(panel.tabTip);
59556 updateTitle : function(title){
59557 if(this.titleTextEl && !this.config.title){
59558 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
59562 setActivePanel : function(panel){
59563 panel = this.getPanel(panel);
59564 if(this.activePanel && this.activePanel != panel){
59565 this.activePanel.setActiveState(false);
59567 this.activePanel = panel;
59568 panel.setActiveState(true);
59569 if(this.panelSize){
59570 panel.setSize(this.panelSize.width, this.panelSize.height);
59573 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59575 this.updateTitle(panel.getTitle());
59577 this.fireEvent("invalidated", this);
59579 this.fireEvent("panelactivated", this, panel);
59583 * Shows the specified panel.
59584 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59585 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59587 showPanel : function(panel)
59589 panel = this.getPanel(panel);
59592 var tab = this.tabs.getTab(panel.getEl().id);
59593 if(tab.isHidden()){
59594 this.tabs.unhideTab(tab.id);
59598 this.setActivePanel(panel);
59605 * Get the active panel for this region.
59606 * @return {Roo.ContentPanel} The active panel or null
59608 getActivePanel : function(){
59609 return this.activePanel;
59612 validateVisibility : function(){
59613 if(this.panels.getCount() < 1){
59614 this.updateTitle(" ");
59615 this.closeBtn.hide();
59618 if(!this.isVisible()){
59625 * Adds the passed ContentPanel(s) to this region.
59626 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59627 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59629 add : function(panel){
59630 if(arguments.length > 1){
59631 for(var i = 0, len = arguments.length; i < len; i++) {
59632 this.add(arguments[i]);
59636 if(this.hasPanel(panel)){
59637 this.showPanel(panel);
59640 panel.setRegion(this);
59641 this.panels.add(panel);
59642 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59643 this.bodyEl.dom.appendChild(panel.getEl().dom);
59644 if(panel.background !== true){
59645 this.setActivePanel(panel);
59647 this.fireEvent("paneladded", this, panel);
59653 this.initPanelAsTab(panel);
59655 if(panel.background !== true){
59656 this.tabs.activate(panel.getEl().id);
59658 this.fireEvent("paneladded", this, panel);
59663 * Hides the tab for the specified panel.
59664 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59666 hidePanel : function(panel){
59667 if(this.tabs && (panel = this.getPanel(panel))){
59668 this.tabs.hideTab(panel.getEl().id);
59673 * Unhides the tab for a previously hidden panel.
59674 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59676 unhidePanel : function(panel){
59677 if(this.tabs && (panel = this.getPanel(panel))){
59678 this.tabs.unhideTab(panel.getEl().id);
59682 clearPanels : function(){
59683 while(this.panels.getCount() > 0){
59684 this.remove(this.panels.first());
59689 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59690 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59691 * @param {Boolean} preservePanel Overrides the config preservePanel option
59692 * @return {Roo.ContentPanel} The panel that was removed
59694 remove : function(panel, preservePanel){
59695 panel = this.getPanel(panel);
59700 this.fireEvent("beforeremove", this, panel, e);
59701 if(e.cancel === true){
59704 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59705 var panelId = panel.getId();
59706 this.panels.removeKey(panelId);
59708 document.body.appendChild(panel.getEl().dom);
59711 this.tabs.removeTab(panel.getEl().id);
59712 }else if (!preservePanel){
59713 this.bodyEl.dom.removeChild(panel.getEl().dom);
59715 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59716 var p = this.panels.first();
59717 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59718 tempEl.appendChild(p.getEl().dom);
59719 this.bodyEl.update("");
59720 this.bodyEl.dom.appendChild(p.getEl().dom);
59722 this.updateTitle(p.getTitle());
59724 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59725 this.setActivePanel(p);
59727 panel.setRegion(null);
59728 if(this.activePanel == panel){
59729 this.activePanel = null;
59731 if(this.config.autoDestroy !== false && preservePanel !== true){
59732 try{panel.destroy();}catch(e){}
59734 this.fireEvent("panelremoved", this, panel);
59739 * Returns the TabPanel component used by this region
59740 * @return {Roo.TabPanel}
59742 getTabs : function(){
59746 createTool : function(parentEl, className){
59747 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
59748 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
59749 btn.addClassOnOver("x-layout-tools-button-over");
59754 * Ext JS Library 1.1.1
59755 * Copyright(c) 2006-2007, Ext JS, LLC.
59757 * Originally Released Under LGPL - original licence link has changed is not relivant.
59760 * <script type="text/javascript">
59766 * @class Roo.SplitLayoutRegion
59767 * @extends Roo.LayoutRegion
59768 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
59770 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
59771 this.cursor = cursor;
59772 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
59775 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
59776 splitTip : "Drag to resize.",
59777 collapsibleSplitTip : "Drag to resize. Double click to hide.",
59778 useSplitTips : false,
59780 applyConfig : function(config){
59781 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
59784 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
59785 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
59786 /** The SplitBar for this region
59787 * @type Roo.SplitBar */
59788 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
59789 this.split.on("moved", this.onSplitMove, this);
59790 this.split.useShim = config.useShim === true;
59791 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
59792 if(this.useSplitTips){
59793 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
59795 if(config.collapsible){
59796 this.split.el.on("dblclick", this.collapse, this);
59799 if(typeof config.minSize != "undefined"){
59800 this.split.minSize = config.minSize;
59802 if(typeof config.maxSize != "undefined"){
59803 this.split.maxSize = config.maxSize;
59805 if(config.hideWhenEmpty || config.hidden || config.collapsed){
59806 this.hideSplitter();
59811 getHMaxSize : function(){
59812 var cmax = this.config.maxSize || 10000;
59813 var center = this.mgr.getRegion("center");
59814 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
59817 getVMaxSize : function(){
59818 var cmax = this.config.maxSize || 10000;
59819 var center = this.mgr.getRegion("center");
59820 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
59823 onSplitMove : function(split, newSize){
59824 this.fireEvent("resized", this, newSize);
59828 * Returns the {@link Roo.SplitBar} for this region.
59829 * @return {Roo.SplitBar}
59831 getSplitBar : function(){
59836 this.hideSplitter();
59837 Roo.SplitLayoutRegion.superclass.hide.call(this);
59840 hideSplitter : function(){
59842 this.split.el.setLocation(-2000,-2000);
59843 this.split.el.hide();
59849 this.split.el.show();
59851 Roo.SplitLayoutRegion.superclass.show.call(this);
59854 beforeSlide: function(){
59855 if(Roo.isGecko){// firefox overflow auto bug workaround
59856 this.bodyEl.clip();
59858 this.tabs.bodyEl.clip();
59860 if(this.activePanel){
59861 this.activePanel.getEl().clip();
59863 if(this.activePanel.beforeSlide){
59864 this.activePanel.beforeSlide();
59870 afterSlide : function(){
59871 if(Roo.isGecko){// firefox overflow auto bug workaround
59872 this.bodyEl.unclip();
59874 this.tabs.bodyEl.unclip();
59876 if(this.activePanel){
59877 this.activePanel.getEl().unclip();
59878 if(this.activePanel.afterSlide){
59879 this.activePanel.afterSlide();
59885 initAutoHide : function(){
59886 if(this.autoHide !== false){
59887 if(!this.autoHideHd){
59888 var st = new Roo.util.DelayedTask(this.slideIn, this);
59889 this.autoHideHd = {
59890 "mouseout": function(e){
59891 if(!e.within(this.el, true)){
59895 "mouseover" : function(e){
59901 this.el.on(this.autoHideHd);
59905 clearAutoHide : function(){
59906 if(this.autoHide !== false){
59907 this.el.un("mouseout", this.autoHideHd.mouseout);
59908 this.el.un("mouseover", this.autoHideHd.mouseover);
59912 clearMonitor : function(){
59913 Roo.get(document).un("click", this.slideInIf, this);
59916 // these names are backwards but not changed for compat
59917 slideOut : function(){
59918 if(this.isSlid || this.el.hasActiveFx()){
59921 this.isSlid = true;
59922 if(this.collapseBtn){
59923 this.collapseBtn.hide();
59925 this.closeBtnState = this.closeBtn.getStyle('display');
59926 this.closeBtn.hide();
59928 this.stickBtn.show();
59931 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
59932 this.beforeSlide();
59933 this.el.setStyle("z-index", 10001);
59934 this.el.slideIn(this.getSlideAnchor(), {
59935 callback: function(){
59937 this.initAutoHide();
59938 Roo.get(document).on("click", this.slideInIf, this);
59939 this.fireEvent("slideshow", this);
59946 afterSlideIn : function(){
59947 this.clearAutoHide();
59948 this.isSlid = false;
59949 this.clearMonitor();
59950 this.el.setStyle("z-index", "");
59951 if(this.collapseBtn){
59952 this.collapseBtn.show();
59954 this.closeBtn.setStyle('display', this.closeBtnState);
59956 this.stickBtn.hide();
59958 this.fireEvent("slidehide", this);
59961 slideIn : function(cb){
59962 if(!this.isSlid || this.el.hasActiveFx()){
59966 this.isSlid = false;
59967 this.beforeSlide();
59968 this.el.slideOut(this.getSlideAnchor(), {
59969 callback: function(){
59970 this.el.setLeftTop(-10000, -10000);
59972 this.afterSlideIn();
59980 slideInIf : function(e){
59981 if(!e.within(this.el)){
59986 animateCollapse : function(){
59987 this.beforeSlide();
59988 this.el.setStyle("z-index", 20000);
59989 var anchor = this.getSlideAnchor();
59990 this.el.slideOut(anchor, {
59991 callback : function(){
59992 this.el.setStyle("z-index", "");
59993 this.collapsedEl.slideIn(anchor, {duration:.3});
59995 this.el.setLocation(-10000,-10000);
59997 this.fireEvent("collapsed", this);
60004 animateExpand : function(){
60005 this.beforeSlide();
60006 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60007 this.el.setStyle("z-index", 20000);
60008 this.collapsedEl.hide({
60011 this.el.slideIn(this.getSlideAnchor(), {
60012 callback : function(){
60013 this.el.setStyle("z-index", "");
60016 this.split.el.show();
60018 this.fireEvent("invalidated", this);
60019 this.fireEvent("expanded", this);
60047 getAnchor : function(){
60048 return this.anchors[this.position];
60051 getCollapseAnchor : function(){
60052 return this.canchors[this.position];
60055 getSlideAnchor : function(){
60056 return this.sanchors[this.position];
60059 getAlignAdj : function(){
60060 var cm = this.cmargins;
60061 switch(this.position){
60077 getExpandAdj : function(){
60078 var c = this.collapsedEl, cm = this.cmargins;
60079 switch(this.position){
60081 return [-(cm.right+c.getWidth()+cm.left), 0];
60084 return [cm.right+c.getWidth()+cm.left, 0];
60087 return [0, -(cm.top+cm.bottom+c.getHeight())];
60090 return [0, cm.top+cm.bottom+c.getHeight()];
60096 * Ext JS Library 1.1.1
60097 * Copyright(c) 2006-2007, Ext JS, LLC.
60099 * Originally Released Under LGPL - original licence link has changed is not relivant.
60102 * <script type="text/javascript">
60105 * These classes are private internal classes
60107 Roo.CenterLayoutRegion = function(mgr, config){
60108 Roo.LayoutRegion.call(this, mgr, config, "center");
60109 this.visible = true;
60110 this.minWidth = config.minWidth || 20;
60111 this.minHeight = config.minHeight || 20;
60114 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60116 // center panel can't be hidden
60120 // center panel can't be hidden
60123 getMinWidth: function(){
60124 return this.minWidth;
60127 getMinHeight: function(){
60128 return this.minHeight;
60133 Roo.NorthLayoutRegion = function(mgr, config){
60134 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60136 this.split.placement = Roo.SplitBar.TOP;
60137 this.split.orientation = Roo.SplitBar.VERTICAL;
60138 this.split.el.addClass("x-layout-split-v");
60140 var size = config.initialSize || config.height;
60141 if(typeof size != "undefined"){
60142 this.el.setHeight(size);
60145 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60146 orientation: Roo.SplitBar.VERTICAL,
60147 getBox : function(){
60148 if(this.collapsed){
60149 return this.collapsedEl.getBox();
60151 var box = this.el.getBox();
60153 box.height += this.split.el.getHeight();
60158 updateBox : function(box){
60159 if(this.split && !this.collapsed){
60160 box.height -= this.split.el.getHeight();
60161 this.split.el.setLeft(box.x);
60162 this.split.el.setTop(box.y+box.height);
60163 this.split.el.setWidth(box.width);
60165 if(this.collapsed){
60166 this.updateBody(box.width, null);
60168 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60172 Roo.SouthLayoutRegion = function(mgr, config){
60173 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60175 this.split.placement = Roo.SplitBar.BOTTOM;
60176 this.split.orientation = Roo.SplitBar.VERTICAL;
60177 this.split.el.addClass("x-layout-split-v");
60179 var size = config.initialSize || config.height;
60180 if(typeof size != "undefined"){
60181 this.el.setHeight(size);
60184 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60185 orientation: Roo.SplitBar.VERTICAL,
60186 getBox : function(){
60187 if(this.collapsed){
60188 return this.collapsedEl.getBox();
60190 var box = this.el.getBox();
60192 var sh = this.split.el.getHeight();
60199 updateBox : function(box){
60200 if(this.split && !this.collapsed){
60201 var sh = this.split.el.getHeight();
60204 this.split.el.setLeft(box.x);
60205 this.split.el.setTop(box.y-sh);
60206 this.split.el.setWidth(box.width);
60208 if(this.collapsed){
60209 this.updateBody(box.width, null);
60211 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60215 Roo.EastLayoutRegion = function(mgr, config){
60216 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60218 this.split.placement = Roo.SplitBar.RIGHT;
60219 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60220 this.split.el.addClass("x-layout-split-h");
60222 var size = config.initialSize || config.width;
60223 if(typeof size != "undefined"){
60224 this.el.setWidth(size);
60227 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60228 orientation: Roo.SplitBar.HORIZONTAL,
60229 getBox : function(){
60230 if(this.collapsed){
60231 return this.collapsedEl.getBox();
60233 var box = this.el.getBox();
60235 var sw = this.split.el.getWidth();
60242 updateBox : function(box){
60243 if(this.split && !this.collapsed){
60244 var sw = this.split.el.getWidth();
60246 this.split.el.setLeft(box.x);
60247 this.split.el.setTop(box.y);
60248 this.split.el.setHeight(box.height);
60251 if(this.collapsed){
60252 this.updateBody(null, box.height);
60254 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60258 Roo.WestLayoutRegion = function(mgr, config){
60259 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60261 this.split.placement = Roo.SplitBar.LEFT;
60262 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60263 this.split.el.addClass("x-layout-split-h");
60265 var size = config.initialSize || config.width;
60266 if(typeof size != "undefined"){
60267 this.el.setWidth(size);
60270 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60271 orientation: Roo.SplitBar.HORIZONTAL,
60272 getBox : function(){
60273 if(this.collapsed){
60274 return this.collapsedEl.getBox();
60276 var box = this.el.getBox();
60278 box.width += this.split.el.getWidth();
60283 updateBox : function(box){
60284 if(this.split && !this.collapsed){
60285 var sw = this.split.el.getWidth();
60287 this.split.el.setLeft(box.x+box.width);
60288 this.split.el.setTop(box.y);
60289 this.split.el.setHeight(box.height);
60291 if(this.collapsed){
60292 this.updateBody(null, box.height);
60294 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60299 * Ext JS Library 1.1.1
60300 * Copyright(c) 2006-2007, Ext JS, LLC.
60302 * Originally Released Under LGPL - original licence link has changed is not relivant.
60305 * <script type="text/javascript">
60310 * Private internal class for reading and applying state
60312 Roo.LayoutStateManager = function(layout){
60313 // default empty state
60322 Roo.LayoutStateManager.prototype = {
60323 init : function(layout, provider){
60324 this.provider = provider;
60325 var state = provider.get(layout.id+"-layout-state");
60327 var wasUpdating = layout.isUpdating();
60329 layout.beginUpdate();
60331 for(var key in state){
60332 if(typeof state[key] != "function"){
60333 var rstate = state[key];
60334 var r = layout.getRegion(key);
60337 r.resizeTo(rstate.size);
60339 if(rstate.collapsed == true){
60342 r.expand(null, true);
60348 layout.endUpdate();
60350 this.state = state;
60352 this.layout = layout;
60353 layout.on("regionresized", this.onRegionResized, this);
60354 layout.on("regioncollapsed", this.onRegionCollapsed, this);
60355 layout.on("regionexpanded", this.onRegionExpanded, this);
60358 storeState : function(){
60359 this.provider.set(this.layout.id+"-layout-state", this.state);
60362 onRegionResized : function(region, newSize){
60363 this.state[region.getPosition()].size = newSize;
60367 onRegionCollapsed : function(region){
60368 this.state[region.getPosition()].collapsed = true;
60372 onRegionExpanded : function(region){
60373 this.state[region.getPosition()].collapsed = false;
60378 * Ext JS Library 1.1.1
60379 * Copyright(c) 2006-2007, Ext JS, LLC.
60381 * Originally Released Under LGPL - original licence link has changed is not relivant.
60384 * <script type="text/javascript">
60387 * @class Roo.ContentPanel
60388 * @extends Roo.util.Observable
60389 * @children Roo.form.Form Roo.JsonView Roo.View
60390 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60391 * A basic ContentPanel element.
60392 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
60393 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
60394 * @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
60395 * @cfg {Boolean} closable True if the panel can be closed/removed
60396 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
60397 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60398 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
60399 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
60400 * @cfg {String} title The title for this panel
60401 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60402 * @cfg {String} url Calls {@link #setUrl} with this value
60403 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60404 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
60405 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
60406 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
60407 * @cfg {String} style Extra style to add to the content panel
60408 * @cfg {Roo.menu.Menu} menu popup menu
60411 * Create a new ContentPanel.
60412 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60413 * @param {String/Object} config A string to set only the title or a config object
60414 * @param {String} content (optional) Set the HTML content for this panel
60415 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60417 Roo.ContentPanel = function(el, config, content){
60421 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60425 if (config && config.parentLayout) {
60426 el = config.parentLayout.el.createChild();
60429 if(el.autoCreate){ // xtype is available if this is called from factory
60433 this.el = Roo.get(el);
60434 if(!this.el && config && config.autoCreate){
60435 if(typeof config.autoCreate == "object"){
60436 if(!config.autoCreate.id){
60437 config.autoCreate.id = config.id||el;
60439 this.el = Roo.DomHelper.append(document.body,
60440 config.autoCreate, true);
60442 this.el = Roo.DomHelper.append(document.body,
60443 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60448 this.closable = false;
60449 this.loaded = false;
60450 this.active = false;
60451 if(typeof config == "string"){
60452 this.title = config;
60454 Roo.apply(this, config);
60457 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60458 this.wrapEl = this.el.wrap();
60459 this.toolbar.container = this.el.insertSibling(false, 'before');
60460 this.toolbar = new Roo.Toolbar(this.toolbar);
60463 // xtype created footer. - not sure if will work as we normally have to render first..
60464 if (this.footer && !this.footer.el && this.footer.xtype) {
60465 if (!this.wrapEl) {
60466 this.wrapEl = this.el.wrap();
60469 this.footer.container = this.wrapEl.createChild();
60471 this.footer = Roo.factory(this.footer, Roo);
60476 this.resizeEl = Roo.get(this.resizeEl, true);
60478 this.resizeEl = this.el;
60480 // handle view.xtype
60488 * Fires when this panel is activated.
60489 * @param {Roo.ContentPanel} this
60493 * @event deactivate
60494 * Fires when this panel is activated.
60495 * @param {Roo.ContentPanel} this
60497 "deactivate" : true,
60501 * Fires when this panel is resized if fitToFrame is true.
60502 * @param {Roo.ContentPanel} this
60503 * @param {Number} width The width after any component adjustments
60504 * @param {Number} height The height after any component adjustments
60510 * Fires when this tab is created
60511 * @param {Roo.ContentPanel} this
60521 if(this.autoScroll){
60522 this.resizeEl.setStyle("overflow", "auto");
60524 // fix randome scrolling
60525 this.el.on('scroll', function() {
60526 Roo.log('fix random scolling');
60527 this.scrollTo('top',0);
60530 content = content || this.content;
60532 this.setContent(content);
60534 if(config && config.url){
60535 this.setUrl(this.url, this.params, this.loadOnce);
60540 Roo.ContentPanel.superclass.constructor.call(this);
60542 if (this.view && typeof(this.view.xtype) != 'undefined') {
60543 this.view.el = this.el.appendChild(document.createElement("div"));
60544 this.view = Roo.factory(this.view);
60545 this.view.render && this.view.render(false, '');
60549 this.fireEvent('render', this);
60552 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60554 setRegion : function(region){
60555 this.region = region;
60557 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60559 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60564 * Returns the toolbar for this Panel if one was configured.
60565 * @return {Roo.Toolbar}
60567 getToolbar : function(){
60568 return this.toolbar;
60571 setActiveState : function(active){
60572 this.active = active;
60574 this.fireEvent("deactivate", this);
60576 this.fireEvent("activate", this);
60580 * Updates this panel's element
60581 * @param {String} content The new content
60582 * @param {Boolean} loadScripts (optional) true to look for and process scripts
60584 setContent : function(content, loadScripts){
60585 this.el.update(content, loadScripts);
60588 ignoreResize : function(w, h){
60589 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60592 this.lastSize = {width: w, height: h};
60597 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60598 * @return {Roo.UpdateManager} The UpdateManager
60600 getUpdateManager : function(){
60601 return this.el.getUpdateManager();
60604 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60605 * @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:
60608 url: "your-url.php",
60609 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60610 callback: yourFunction,
60611 scope: yourObject, //(optional scope)
60614 text: "Loading...",
60619 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60620 * 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.
60621 * @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}
60622 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60623 * @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.
60624 * @return {Roo.ContentPanel} this
60627 var um = this.el.getUpdateManager();
60628 um.update.apply(um, arguments);
60634 * 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.
60635 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60636 * @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)
60637 * @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)
60638 * @return {Roo.UpdateManager} The UpdateManager
60640 setUrl : function(url, params, loadOnce){
60641 if(this.refreshDelegate){
60642 this.removeListener("activate", this.refreshDelegate);
60644 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60645 this.on("activate", this.refreshDelegate);
60646 return this.el.getUpdateManager();
60649 _handleRefresh : function(url, params, loadOnce){
60650 if(!loadOnce || !this.loaded){
60651 var updater = this.el.getUpdateManager();
60652 updater.update(url, params, this._setLoaded.createDelegate(this));
60656 _setLoaded : function(){
60657 this.loaded = true;
60661 * Returns this panel's id
60664 getId : function(){
60669 * Returns this panel's element - used by regiosn to add.
60670 * @return {Roo.Element}
60672 getEl : function(){
60673 return this.wrapEl || this.el;
60676 adjustForComponents : function(width, height)
60678 //Roo.log('adjustForComponents ');
60679 if(this.resizeEl != this.el){
60680 width -= this.el.getFrameWidth('lr');
60681 height -= this.el.getFrameWidth('tb');
60684 var te = this.toolbar.getEl();
60685 height -= te.getHeight();
60686 te.setWidth(width);
60689 var te = this.footer.getEl();
60690 //Roo.log("footer:" + te.getHeight());
60692 height -= te.getHeight();
60693 te.setWidth(width);
60697 if(this.adjustments){
60698 width += this.adjustments[0];
60699 height += this.adjustments[1];
60701 return {"width": width, "height": height};
60704 setSize : function(width, height){
60705 if(this.fitToFrame && !this.ignoreResize(width, height)){
60706 if(this.fitContainer && this.resizeEl != this.el){
60707 this.el.setSize(width, height);
60709 var size = this.adjustForComponents(width, height);
60710 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60711 this.fireEvent('resize', this, size.width, size.height);
60716 * Returns this panel's title
60719 getTitle : function(){
60724 * Set this panel's title
60725 * @param {String} title
60727 setTitle : function(title){
60728 this.title = title;
60730 this.region.updatePanelTitle(this, title);
60735 * Returns true is this panel was configured to be closable
60736 * @return {Boolean}
60738 isClosable : function(){
60739 return this.closable;
60742 beforeSlide : function(){
60744 this.resizeEl.clip();
60747 afterSlide : function(){
60749 this.resizeEl.unclip();
60753 * Force a content refresh from the URL specified in the {@link #setUrl} method.
60754 * Will fail silently if the {@link #setUrl} method has not been called.
60755 * This does not activate the panel, just updates its content.
60757 refresh : function(){
60758 if(this.refreshDelegate){
60759 this.loaded = false;
60760 this.refreshDelegate();
60765 * Destroys this panel
60767 destroy : function(){
60768 this.el.removeAllListeners();
60769 var tempEl = document.createElement("span");
60770 tempEl.appendChild(this.el.dom);
60771 tempEl.innerHTML = "";
60777 * form - if the content panel contains a form - this is a reference to it.
60778 * @type {Roo.form.Form}
60782 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
60783 * This contains a reference to it.
60789 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
60799 * @param {Object} cfg Xtype definition of item to add.
60802 addxtype : function(cfg) {
60804 if (cfg.xtype.match(/^Form$/)) {
60807 //if (this.footer) {
60808 // el = this.footer.container.insertSibling(false, 'before');
60810 el = this.el.createChild();
60813 this.form = new Roo.form.Form(cfg);
60816 if ( this.form.allItems.length) {
60817 this.form.render(el.dom);
60821 // should only have one of theses..
60822 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
60823 // views.. should not be just added - used named prop 'view''
60825 cfg.el = this.el.appendChild(document.createElement("div"));
60828 var ret = new Roo.factory(cfg);
60830 ret.render && ret.render(false, ''); // render blank..
60850 * @class Roo.GridPanel
60851 * @extends Roo.ContentPanel
60852 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60854 * Create a new GridPanel.
60855 * @cfg {Roo.grid.Grid} grid The grid for this panel
60857 Roo.GridPanel = function(grid, config){
60859 // universal ctor...
60860 if (typeof(grid.grid) != 'undefined') {
60862 grid = config.grid;
60864 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
60865 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
60867 this.wrapper.dom.appendChild(grid.getGridEl().dom);
60869 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
60872 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
60874 // xtype created footer. - not sure if will work as we normally have to render first..
60875 if (this.footer && !this.footer.el && this.footer.xtype) {
60877 this.footer.container = this.grid.getView().getFooterPanel(true);
60878 this.footer.dataSource = this.grid.dataSource;
60879 this.footer = Roo.factory(this.footer, Roo);
60883 grid.monitorWindowResize = false; // turn off autosizing
60884 grid.autoHeight = false;
60885 grid.autoWidth = false;
60887 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
60890 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
60891 getId : function(){
60892 return this.grid.id;
60896 * Returns the grid for this panel
60897 * @return {Roo.grid.Grid}
60899 getGrid : function(){
60903 setSize : function(width, height){
60904 if(!this.ignoreResize(width, height)){
60905 var grid = this.grid;
60906 var size = this.adjustForComponents(width, height);
60907 grid.getGridEl().setSize(size.width, size.height);
60912 beforeSlide : function(){
60913 this.grid.getView().scroller.clip();
60916 afterSlide : function(){
60917 this.grid.getView().scroller.unclip();
60920 destroy : function(){
60921 this.grid.destroy();
60923 Roo.GridPanel.superclass.destroy.call(this);
60929 * @class Roo.NestedLayoutPanel
60930 * @extends Roo.ContentPanel
60931 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60932 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
60936 * Create a new NestedLayoutPanel.
60939 * @param {Roo.BorderLayout} layout [required] The layout for this panel
60940 * @param {String/Object} config A string to set only the title or a config object
60942 Roo.NestedLayoutPanel = function(layout, config)
60944 // construct with only one argument..
60945 /* FIXME - implement nicer consturctors
60946 if (layout.layout) {
60948 layout = config.layout;
60949 delete config.layout;
60951 if (layout.xtype && !layout.getEl) {
60952 // then layout needs constructing..
60953 layout = Roo.factory(layout, Roo);
60958 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
60960 layout.monitorWindowResize = false; // turn off autosizing
60961 this.layout = layout;
60962 this.layout.getEl().addClass("x-layout-nested-layout");
60969 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
60973 setSize : function(width, height){
60974 if(!this.ignoreResize(width, height)){
60975 var size = this.adjustForComponents(width, height);
60976 var el = this.layout.getEl();
60977 el.setSize(size.width, size.height);
60978 var touch = el.dom.offsetWidth;
60979 this.layout.layout();
60980 // ie requires a double layout on the first pass
60981 if(Roo.isIE && !this.initialized){
60982 this.initialized = true;
60983 this.layout.layout();
60988 // activate all subpanels if not currently active..
60990 setActiveState : function(active){
60991 this.active = active;
60993 this.fireEvent("deactivate", this);
60997 this.fireEvent("activate", this);
60998 // not sure if this should happen before or after..
60999 if (!this.layout) {
61000 return; // should not happen..
61003 for (var r in this.layout.regions) {
61004 reg = this.layout.getRegion(r);
61005 if (reg.getActivePanel()) {
61006 //reg.showPanel(reg.getActivePanel()); // force it to activate..
61007 reg.setActivePanel(reg.getActivePanel());
61010 if (!reg.panels.length) {
61013 reg.showPanel(reg.getPanel(0));
61022 * Returns the nested BorderLayout for this panel
61023 * @return {Roo.BorderLayout}
61025 getLayout : function(){
61026 return this.layout;
61030 * Adds a xtype elements to the layout of the nested panel
61034 xtype : 'ContentPanel',
61041 xtype : 'NestedLayoutPanel',
61047 items : [ ... list of content panels or nested layout panels.. ]
61051 * @param {Object} cfg Xtype definition of item to add.
61053 addxtype : function(cfg) {
61054 return this.layout.addxtype(cfg);
61059 Roo.ScrollPanel = function(el, config, content){
61060 config = config || {};
61061 config.fitToFrame = true;
61062 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61064 this.el.dom.style.overflow = "hidden";
61065 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61066 this.el.removeClass("x-layout-inactive-content");
61067 this.el.on("mousewheel", this.onWheel, this);
61069 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
61070 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
61071 up.unselectable(); down.unselectable();
61072 up.on("click", this.scrollUp, this);
61073 down.on("click", this.scrollDown, this);
61074 up.addClassOnOver("x-scroller-btn-over");
61075 down.addClassOnOver("x-scroller-btn-over");
61076 up.addClassOnClick("x-scroller-btn-click");
61077 down.addClassOnClick("x-scroller-btn-click");
61078 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61080 this.resizeEl = this.el;
61081 this.el = wrap; this.up = up; this.down = down;
61084 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61086 wheelIncrement : 5,
61087 scrollUp : function(){
61088 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61091 scrollDown : function(){
61092 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61095 afterScroll : function(){
61096 var el = this.resizeEl;
61097 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61098 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61099 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61102 setSize : function(){
61103 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61104 this.afterScroll();
61107 onWheel : function(e){
61108 var d = e.getWheelDelta();
61109 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61110 this.afterScroll();
61114 setContent : function(content, loadScripts){
61115 this.resizeEl.update(content, loadScripts);
61123 * @class Roo.TreePanel
61124 * @extends Roo.ContentPanel
61125 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61126 * Treepanel component
61129 * Create a new TreePanel. - defaults to fit/scoll contents.
61130 * @param {String/Object} config A string to set only the panel's title, or a config object
61132 Roo.TreePanel = function(config){
61133 var el = config.el;
61134 var tree = config.tree;
61135 delete config.tree;
61136 delete config.el; // hopefull!
61138 // wrapper for IE7 strict & safari scroll issue
61140 var treeEl = el.createChild();
61141 config.resizeEl = treeEl;
61145 Roo.TreePanel.superclass.constructor.call(this, el, config);
61148 this.tree = new Roo.tree.TreePanel(treeEl , tree);
61149 //console.log(tree);
61150 this.on('activate', function()
61152 if (this.tree.rendered) {
61155 //console.log('render tree');
61156 this.tree.render();
61158 // this should not be needed.. - it's actually the 'el' that resizes?
61159 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61161 //this.on('resize', function (cp, w, h) {
61162 // this.tree.innerCt.setWidth(w);
61163 // this.tree.innerCt.setHeight(h);
61164 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
61171 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
61175 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61182 * Ext JS Library 1.1.1
61183 * Copyright(c) 2006-2007, Ext JS, LLC.
61185 * Originally Released Under LGPL - original licence link has changed is not relivant.
61188 * <script type="text/javascript">
61193 * @class Roo.ReaderLayout
61194 * @extends Roo.BorderLayout
61195 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
61196 * center region containing two nested regions (a top one for a list view and one for item preview below),
61197 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61198 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61199 * expedites the setup of the overall layout and regions for this common application style.
61202 var reader = new Roo.ReaderLayout();
61203 var CP = Roo.ContentPanel; // shortcut for adding
61205 reader.beginUpdate();
61206 reader.add("north", new CP("north", "North"));
61207 reader.add("west", new CP("west", {title: "West"}));
61208 reader.add("east", new CP("east", {title: "East"}));
61210 reader.regions.listView.add(new CP("listView", "List"));
61211 reader.regions.preview.add(new CP("preview", "Preview"));
61212 reader.endUpdate();
61215 * Create a new ReaderLayout
61216 * @param {Object} config Configuration options
61217 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61218 * document.body if omitted)
61220 Roo.ReaderLayout = function(config, renderTo){
61221 var c = config || {size:{}};
61222 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61223 north: c.north !== false ? Roo.apply({
61227 }, c.north) : false,
61228 west: c.west !== false ? Roo.apply({
61236 margins:{left:5,right:0,bottom:5,top:5},
61237 cmargins:{left:5,right:5,bottom:5,top:5}
61238 }, c.west) : false,
61239 east: c.east !== false ? Roo.apply({
61247 margins:{left:0,right:5,bottom:5,top:5},
61248 cmargins:{left:5,right:5,bottom:5,top:5}
61249 }, c.east) : false,
61250 center: Roo.apply({
61251 tabPosition: 'top',
61255 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61259 this.el.addClass('x-reader');
61261 this.beginUpdate();
61263 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61264 south: c.preview !== false ? Roo.apply({
61271 cmargins:{top:5,left:0, right:0, bottom:0}
61272 }, c.preview) : false,
61273 center: Roo.apply({
61279 this.add('center', new Roo.NestedLayoutPanel(inner,
61280 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61284 this.regions.preview = inner.getRegion('south');
61285 this.regions.listView = inner.getRegion('center');
61288 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61290 * Ext JS Library 1.1.1
61291 * Copyright(c) 2006-2007, Ext JS, LLC.
61293 * Originally Released Under LGPL - original licence link has changed is not relivant.
61296 * <script type="text/javascript">
61300 * @class Roo.grid.Grid
61301 * @extends Roo.util.Observable
61302 * This class represents the primary interface of a component based grid control.
61303 * <br><br>Usage:<pre><code>
61304 var grid = new Roo.grid.Grid("my-container-id", {
61307 selModel: mySelectionModel,
61308 autoSizeColumns: true,
61309 monitorWindowResize: false,
61310 trackMouseOver: true
61315 * <b>Common Problems:</b><br/>
61316 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61317 * element will correct this<br/>
61318 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61319 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61320 * are unpredictable.<br/>
61321 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61322 * grid to calculate dimensions/offsets.<br/>
61324 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61325 * The container MUST have some type of size defined for the grid to fill. The container will be
61326 * automatically set to position relative if it isn't already.
61327 * @param {Object} config A config object that sets properties on this grid.
61329 Roo.grid.Grid = function(container, config){
61330 // initialize the container
61331 this.container = Roo.get(container);
61332 this.container.update("");
61333 this.container.setStyle("overflow", "hidden");
61334 this.container.addClass('x-grid-container');
61336 this.id = this.container.id;
61338 Roo.apply(this, config);
61339 // check and correct shorthanded configs
61341 this.dataSource = this.ds;
61345 this.colModel = this.cm;
61349 this.selModel = this.sm;
61353 if (this.selModel) {
61354 this.selModel = Roo.factory(this.selModel, Roo.grid);
61355 this.sm = this.selModel;
61356 this.sm.xmodule = this.xmodule || false;
61358 if (typeof(this.colModel.config) == 'undefined') {
61359 this.colModel = new Roo.grid.ColumnModel(this.colModel);
61360 this.cm = this.colModel;
61361 this.cm.xmodule = this.xmodule || false;
61363 if (this.dataSource) {
61364 this.dataSource= Roo.factory(this.dataSource, Roo.data);
61365 this.ds = this.dataSource;
61366 this.ds.xmodule = this.xmodule || false;
61373 this.container.setWidth(this.width);
61377 this.container.setHeight(this.height);
61384 * The raw click event for the entire grid.
61385 * @param {Roo.EventObject} e
61390 * The raw dblclick event for the entire grid.
61391 * @param {Roo.EventObject} e
61395 * @event contextmenu
61396 * The raw contextmenu event for the entire grid.
61397 * @param {Roo.EventObject} e
61399 "contextmenu" : true,
61402 * The raw mousedown event for the entire grid.
61403 * @param {Roo.EventObject} e
61405 "mousedown" : true,
61408 * The raw mouseup event for the entire grid.
61409 * @param {Roo.EventObject} e
61414 * The raw mouseover event for the entire grid.
61415 * @param {Roo.EventObject} e
61417 "mouseover" : true,
61420 * The raw mouseout event for the entire grid.
61421 * @param {Roo.EventObject} e
61426 * The raw keypress event for the entire grid.
61427 * @param {Roo.EventObject} e
61432 * The raw keydown event for the entire grid.
61433 * @param {Roo.EventObject} e
61441 * Fires when a cell is clicked
61442 * @param {Grid} this
61443 * @param {Number} rowIndex
61444 * @param {Number} columnIndex
61445 * @param {Roo.EventObject} e
61447 "cellclick" : true,
61449 * @event celldblclick
61450 * Fires when a cell is double clicked
61451 * @param {Grid} this
61452 * @param {Number} rowIndex
61453 * @param {Number} columnIndex
61454 * @param {Roo.EventObject} e
61456 "celldblclick" : true,
61459 * Fires when a row is clicked
61460 * @param {Grid} this
61461 * @param {Number} rowIndex
61462 * @param {Roo.EventObject} e
61466 * @event rowdblclick
61467 * Fires when a row is double clicked
61468 * @param {Grid} this
61469 * @param {Number} rowIndex
61470 * @param {Roo.EventObject} e
61472 "rowdblclick" : true,
61474 * @event headerclick
61475 * Fires when a header is clicked
61476 * @param {Grid} this
61477 * @param {Number} columnIndex
61478 * @param {Roo.EventObject} e
61480 "headerclick" : true,
61482 * @event headerdblclick
61483 * Fires when a header cell is double clicked
61484 * @param {Grid} this
61485 * @param {Number} columnIndex
61486 * @param {Roo.EventObject} e
61488 "headerdblclick" : true,
61490 * @event rowcontextmenu
61491 * Fires when a row is right clicked
61492 * @param {Grid} this
61493 * @param {Number} rowIndex
61494 * @param {Roo.EventObject} e
61496 "rowcontextmenu" : true,
61498 * @event cellcontextmenu
61499 * Fires when a cell is right clicked
61500 * @param {Grid} this
61501 * @param {Number} rowIndex
61502 * @param {Number} cellIndex
61503 * @param {Roo.EventObject} e
61505 "cellcontextmenu" : true,
61507 * @event headercontextmenu
61508 * Fires when a header is right clicked
61509 * @param {Grid} this
61510 * @param {Number} columnIndex
61511 * @param {Roo.EventObject} e
61513 "headercontextmenu" : true,
61515 * @event bodyscroll
61516 * Fires when the body element is scrolled
61517 * @param {Number} scrollLeft
61518 * @param {Number} scrollTop
61520 "bodyscroll" : true,
61522 * @event columnresize
61523 * Fires when the user resizes a column
61524 * @param {Number} columnIndex
61525 * @param {Number} newSize
61527 "columnresize" : true,
61529 * @event columnmove
61530 * Fires when the user moves a column
61531 * @param {Number} oldIndex
61532 * @param {Number} newIndex
61534 "columnmove" : true,
61537 * Fires when row(s) start being dragged
61538 * @param {Grid} this
61539 * @param {Roo.GridDD} dd The drag drop object
61540 * @param {event} e The raw browser event
61542 "startdrag" : true,
61545 * Fires when a drag operation is complete
61546 * @param {Grid} this
61547 * @param {Roo.GridDD} dd The drag drop object
61548 * @param {event} e The raw browser event
61553 * Fires when dragged row(s) are dropped on a valid DD target
61554 * @param {Grid} this
61555 * @param {Roo.GridDD} dd The drag drop object
61556 * @param {String} targetId The target drag drop object
61557 * @param {event} e The raw browser event
61562 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61563 * @param {Grid} this
61564 * @param {Roo.GridDD} dd The drag drop object
61565 * @param {String} targetId The target drag drop object
61566 * @param {event} e The raw browser event
61571 * Fires when the dragged row(s) first cross another DD target while being dragged
61572 * @param {Grid} this
61573 * @param {Roo.GridDD} dd The drag drop object
61574 * @param {String} targetId The target drag drop object
61575 * @param {event} e The raw browser event
61577 "dragenter" : true,
61580 * Fires when the dragged row(s) leave another DD target while being dragged
61581 * @param {Grid} this
61582 * @param {Roo.GridDD} dd The drag drop object
61583 * @param {String} targetId The target drag drop object
61584 * @param {event} e The raw browser event
61589 * Fires when a row is rendered, so you can change add a style to it.
61590 * @param {GridView} gridview The grid view
61591 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
61597 * Fires when the grid is rendered
61598 * @param {Grid} grid
61603 Roo.grid.Grid.superclass.constructor.call(this);
61605 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61608 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61611 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
61614 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61617 * @cfg {Roo.data.Store} ds The data store for the grid
61620 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61623 * @cfg {String} ddGroup - drag drop group.
61626 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61630 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61632 minColumnWidth : 25,
61635 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61636 * <b>on initial render.</b> It is more efficient to explicitly size the columns
61637 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
61639 autoSizeColumns : false,
61642 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61644 autoSizeHeaders : true,
61647 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61649 monitorWindowResize : true,
61652 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61653 * rows measured to get a columns size. Default is 0 (all rows).
61655 maxRowsToMeasure : 0,
61658 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61660 trackMouseOver : true,
61663 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
61666 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
61670 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61672 enableDragDrop : false,
61675 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61677 enableColumnMove : true,
61680 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61682 enableColumnHide : true,
61685 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61687 enableRowHeightSync : false,
61690 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
61695 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61697 autoHeight : false,
61700 * @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.
61702 autoExpandColumn : false,
61705 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61708 autoExpandMin : 50,
61711 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61713 autoExpandMax : 1000,
61716 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61721 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61725 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61729 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
61731 sortColMenu : false,
61737 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
61738 * of a fixed width. Default is false.
61741 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
61746 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
61747 * %0 is replaced with the number of selected rows.
61749 ddText : "{0} selected row{1}",
61753 * Called once after all setup has been completed and the grid is ready to be rendered.
61754 * @return {Roo.grid.Grid} this
61756 render : function()
61758 var c = this.container;
61759 // try to detect autoHeight/width mode
61760 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
61761 this.autoHeight = true;
61763 var view = this.getView();
61766 c.on("click", this.onClick, this);
61767 c.on("dblclick", this.onDblClick, this);
61768 c.on("contextmenu", this.onContextMenu, this);
61769 c.on("keydown", this.onKeyDown, this);
61771 c.on("touchstart", this.onTouchStart, this);
61774 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
61776 this.getSelectionModel().init(this);
61781 this.loadMask = new Roo.LoadMask(this.container,
61782 Roo.apply({store:this.dataSource}, this.loadMask));
61786 if (this.toolbar && this.toolbar.xtype) {
61787 this.toolbar.container = this.getView().getHeaderPanel(true);
61788 this.toolbar = new Roo.Toolbar(this.toolbar);
61790 if (this.footer && this.footer.xtype) {
61791 this.footer.dataSource = this.getDataSource();
61792 this.footer.container = this.getView().getFooterPanel(true);
61793 this.footer = Roo.factory(this.footer, Roo);
61795 if (this.dropTarget && this.dropTarget.xtype) {
61796 delete this.dropTarget.xtype;
61797 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
61801 this.rendered = true;
61802 this.fireEvent('render', this);
61807 * Reconfigures the grid to use a different Store and Column Model.
61808 * The View will be bound to the new objects and refreshed.
61809 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
61810 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
61812 reconfigure : function(dataSource, colModel){
61814 this.loadMask.destroy();
61815 this.loadMask = new Roo.LoadMask(this.container,
61816 Roo.apply({store:dataSource}, this.loadMask));
61818 this.view.bind(dataSource, colModel);
61819 this.dataSource = dataSource;
61820 this.colModel = colModel;
61821 this.view.refresh(true);
61825 * Add's a column, default at the end..
61827 * @param {int} position to add (default end)
61828 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
61830 addColumns : function(pos, ar)
61833 for (var i =0;i< ar.length;i++) {
61835 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
61836 this.cm.lookup[cfg.id] = cfg;
61840 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
61841 pos = this.cm.config.length; //this.cm.config.push(cfg);
61843 pos = Math.max(0,pos);
61846 this.cm.config.splice.apply(this.cm.config, ar);
61850 this.view.generateRules(this.cm);
61851 this.view.refresh(true);
61859 onKeyDown : function(e){
61860 this.fireEvent("keydown", e);
61864 * Destroy this grid.
61865 * @param {Boolean} removeEl True to remove the element
61867 destroy : function(removeEl, keepListeners){
61869 this.loadMask.destroy();
61871 var c = this.container;
61872 c.removeAllListeners();
61873 this.view.destroy();
61874 this.colModel.purgeListeners();
61875 if(!keepListeners){
61876 this.purgeListeners();
61879 if(removeEl === true){
61885 processEvent : function(name, e){
61886 // does this fire select???
61887 //Roo.log('grid:processEvent ' + name);
61889 if (name != 'touchstart' ) {
61890 this.fireEvent(name, e);
61893 var t = e.getTarget();
61895 var header = v.findHeaderIndex(t);
61896 if(header !== false){
61897 var ename = name == 'touchstart' ? 'click' : name;
61899 this.fireEvent("header" + ename, this, header, e);
61901 var row = v.findRowIndex(t);
61902 var cell = v.findCellIndex(t);
61903 if (name == 'touchstart') {
61904 // first touch is always a click.
61905 // hopefull this happens after selection is updated.?
61908 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
61909 var cs = this.selModel.getSelectedCell();
61910 if (row == cs[0] && cell == cs[1]){
61914 if (typeof(this.selModel.getSelections) != 'undefined') {
61915 var cs = this.selModel.getSelections();
61916 var ds = this.dataSource;
61917 if (cs.length == 1 && ds.getAt(row) == cs[0]){
61928 this.fireEvent("row" + name, this, row, e);
61929 if(cell !== false){
61930 this.fireEvent("cell" + name, this, row, cell, e);
61937 onClick : function(e){
61938 this.processEvent("click", e);
61941 onTouchStart : function(e){
61942 this.processEvent("touchstart", e);
61946 onContextMenu : function(e, t){
61947 this.processEvent("contextmenu", e);
61951 onDblClick : function(e){
61952 this.processEvent("dblclick", e);
61956 walkCells : function(row, col, step, fn, scope){
61957 var cm = this.colModel, clen = cm.getColumnCount();
61958 var ds = this.dataSource, rlen = ds.getCount(), first = true;
61970 if(fn.call(scope || this, row, col, cm) === true){
61988 if(fn.call(scope || this, row, col, cm) === true){
62000 getSelections : function(){
62001 return this.selModel.getSelections();
62005 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62006 * but if manual update is required this method will initiate it.
62008 autoSize : function(){
62010 this.view.layout();
62011 if(this.view.adjustForScroll){
62012 this.view.adjustForScroll();
62018 * Returns the grid's underlying element.
62019 * @return {Element} The element
62021 getGridEl : function(){
62022 return this.container;
62025 // private for compatibility, overridden by editor grid
62026 stopEditing : function(){},
62029 * Returns the grid's SelectionModel.
62030 * @return {SelectionModel}
62032 getSelectionModel : function(){
62033 if(!this.selModel){
62034 this.selModel = new Roo.grid.RowSelectionModel();
62036 return this.selModel;
62040 * Returns the grid's DataSource.
62041 * @return {DataSource}
62043 getDataSource : function(){
62044 return this.dataSource;
62048 * Returns the grid's ColumnModel.
62049 * @return {ColumnModel}
62051 getColumnModel : function(){
62052 return this.colModel;
62056 * Returns the grid's GridView object.
62057 * @return {GridView}
62059 getView : function(){
62061 this.view = new Roo.grid.GridView(this.viewConfig);
62062 this.relayEvents(this.view, [
62063 "beforerowremoved", "beforerowsinserted",
62064 "beforerefresh", "rowremoved",
62065 "rowsinserted", "rowupdated" ,"refresh"
62071 * Called to get grid's drag proxy text, by default returns this.ddText.
62072 * Override this to put something different in the dragged text.
62075 getDragDropText : function(){
62076 var count = this.selModel.getCount();
62077 return String.format(this.ddText, count, count == 1 ? '' : 's');
62082 * Ext JS Library 1.1.1
62083 * Copyright(c) 2006-2007, Ext JS, LLC.
62085 * Originally Released Under LGPL - original licence link has changed is not relivant.
62088 * <script type="text/javascript">
62091 * @class Roo.grid.AbstractGridView
62092 * @extends Roo.util.Observable
62094 * Abstract base class for grid Views
62097 Roo.grid.AbstractGridView = function(){
62101 "beforerowremoved" : true,
62102 "beforerowsinserted" : true,
62103 "beforerefresh" : true,
62104 "rowremoved" : true,
62105 "rowsinserted" : true,
62106 "rowupdated" : true,
62109 Roo.grid.AbstractGridView.superclass.constructor.call(this);
62112 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62113 rowClass : "x-grid-row",
62114 cellClass : "x-grid-cell",
62115 tdClass : "x-grid-td",
62116 hdClass : "x-grid-hd",
62117 splitClass : "x-grid-hd-split",
62119 init: function(grid){
62121 var cid = this.grid.getGridEl().id;
62122 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62123 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62124 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62125 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62128 getColumnRenderers : function(){
62129 var renderers = [];
62130 var cm = this.grid.colModel;
62131 var colCount = cm.getColumnCount();
62132 for(var i = 0; i < colCount; i++){
62133 renderers[i] = cm.getRenderer(i);
62138 getColumnIds : function(){
62140 var cm = this.grid.colModel;
62141 var colCount = cm.getColumnCount();
62142 for(var i = 0; i < colCount; i++){
62143 ids[i] = cm.getColumnId(i);
62148 getDataIndexes : function(){
62149 if(!this.indexMap){
62150 this.indexMap = this.buildIndexMap();
62152 return this.indexMap.colToData;
62155 getColumnIndexByDataIndex : function(dataIndex){
62156 if(!this.indexMap){
62157 this.indexMap = this.buildIndexMap();
62159 return this.indexMap.dataToCol[dataIndex];
62163 * Set a css style for a column dynamically.
62164 * @param {Number} colIndex The index of the column
62165 * @param {String} name The css property name
62166 * @param {String} value The css value
62168 setCSSStyle : function(colIndex, name, value){
62169 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62170 Roo.util.CSS.updateRule(selector, name, value);
62173 generateRules : function(cm){
62174 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62175 Roo.util.CSS.removeStyleSheet(rulesId);
62176 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62177 var cid = cm.getColumnId(i);
62178 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62179 this.tdSelector, cid, " {\n}\n",
62180 this.hdSelector, cid, " {\n}\n",
62181 this.splitSelector, cid, " {\n}\n");
62183 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62187 * Ext JS Library 1.1.1
62188 * Copyright(c) 2006-2007, Ext JS, LLC.
62190 * Originally Released Under LGPL - original licence link has changed is not relivant.
62193 * <script type="text/javascript">
62197 // This is a support class used internally by the Grid components
62198 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62200 this.view = grid.getView();
62201 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62202 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62204 this.setHandleElId(Roo.id(hd));
62205 this.setOuterHandleElId(Roo.id(hd2));
62207 this.scroll = false;
62209 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62211 getDragData : function(e){
62212 var t = Roo.lib.Event.getTarget(e);
62213 var h = this.view.findHeaderCell(t);
62215 return {ddel: h.firstChild, header:h};
62220 onInitDrag : function(e){
62221 this.view.headersDisabled = true;
62222 var clone = this.dragData.ddel.cloneNode(true);
62223 clone.id = Roo.id();
62224 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62225 this.proxy.update(clone);
62229 afterValidDrop : function(){
62231 setTimeout(function(){
62232 v.headersDisabled = false;
62236 afterInvalidDrop : function(){
62238 setTimeout(function(){
62239 v.headersDisabled = false;
62245 * Ext JS Library 1.1.1
62246 * Copyright(c) 2006-2007, Ext JS, LLC.
62248 * Originally Released Under LGPL - original licence link has changed is not relivant.
62251 * <script type="text/javascript">
62254 // This is a support class used internally by the Grid components
62255 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62257 this.view = grid.getView();
62258 // split the proxies so they don't interfere with mouse events
62259 this.proxyTop = Roo.DomHelper.append(document.body, {
62260 cls:"col-move-top", html:" "
62262 this.proxyBottom = Roo.DomHelper.append(document.body, {
62263 cls:"col-move-bottom", html:" "
62265 this.proxyTop.hide = this.proxyBottom.hide = function(){
62266 this.setLeftTop(-100,-100);
62267 this.setStyle("visibility", "hidden");
62269 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62270 // temporarily disabled
62271 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62272 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62274 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62275 proxyOffsets : [-4, -9],
62276 fly: Roo.Element.fly,
62278 getTargetFromEvent : function(e){
62279 var t = Roo.lib.Event.getTarget(e);
62280 var cindex = this.view.findCellIndex(t);
62281 if(cindex !== false){
62282 return this.view.getHeaderCell(cindex);
62287 nextVisible : function(h){
62288 var v = this.view, cm = this.grid.colModel;
62291 if(!cm.isHidden(v.getCellIndex(h))){
62299 prevVisible : function(h){
62300 var v = this.view, cm = this.grid.colModel;
62303 if(!cm.isHidden(v.getCellIndex(h))){
62311 positionIndicator : function(h, n, e){
62312 var x = Roo.lib.Event.getPageX(e);
62313 var r = Roo.lib.Dom.getRegion(n.firstChild);
62314 var px, pt, py = r.top + this.proxyOffsets[1];
62315 if((r.right - x) <= (r.right-r.left)/2){
62316 px = r.right+this.view.borderWidth;
62322 var oldIndex = this.view.getCellIndex(h);
62323 var newIndex = this.view.getCellIndex(n);
62325 if(this.grid.colModel.isFixed(newIndex)){
62329 var locked = this.grid.colModel.isLocked(newIndex);
62334 if(oldIndex < newIndex){
62337 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62340 px += this.proxyOffsets[0];
62341 this.proxyTop.setLeftTop(px, py);
62342 this.proxyTop.show();
62343 if(!this.bottomOffset){
62344 this.bottomOffset = this.view.mainHd.getHeight();
62346 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62347 this.proxyBottom.show();
62351 onNodeEnter : function(n, dd, e, data){
62352 if(data.header != n){
62353 this.positionIndicator(data.header, n, e);
62357 onNodeOver : function(n, dd, e, data){
62358 var result = false;
62359 if(data.header != n){
62360 result = this.positionIndicator(data.header, n, e);
62363 this.proxyTop.hide();
62364 this.proxyBottom.hide();
62366 return result ? this.dropAllowed : this.dropNotAllowed;
62369 onNodeOut : function(n, dd, e, data){
62370 this.proxyTop.hide();
62371 this.proxyBottom.hide();
62374 onNodeDrop : function(n, dd, e, data){
62375 var h = data.header;
62377 var cm = this.grid.colModel;
62378 var x = Roo.lib.Event.getPageX(e);
62379 var r = Roo.lib.Dom.getRegion(n.firstChild);
62380 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62381 var oldIndex = this.view.getCellIndex(h);
62382 var newIndex = this.view.getCellIndex(n);
62383 var locked = cm.isLocked(newIndex);
62387 if(oldIndex < newIndex){
62390 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62393 cm.setLocked(oldIndex, locked, true);
62394 cm.moveColumn(oldIndex, newIndex);
62395 this.grid.fireEvent("columnmove", oldIndex, newIndex);
62403 * Ext JS Library 1.1.1
62404 * Copyright(c) 2006-2007, Ext JS, LLC.
62406 * Originally Released Under LGPL - original licence link has changed is not relivant.
62409 * <script type="text/javascript">
62413 * @class Roo.grid.GridView
62414 * @extends Roo.util.Observable
62417 * @param {Object} config
62419 Roo.grid.GridView = function(config){
62420 Roo.grid.GridView.superclass.constructor.call(this);
62423 Roo.apply(this, config);
62426 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62428 unselectable : 'unselectable="on"',
62429 unselectableCls : 'x-unselectable',
62432 rowClass : "x-grid-row",
62434 cellClass : "x-grid-col",
62436 tdClass : "x-grid-td",
62438 hdClass : "x-grid-hd",
62440 splitClass : "x-grid-split",
62442 sortClasses : ["sort-asc", "sort-desc"],
62444 enableMoveAnim : false,
62448 dh : Roo.DomHelper,
62450 fly : Roo.Element.fly,
62452 css : Roo.util.CSS,
62458 scrollIncrement : 22,
62460 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62462 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62464 bind : function(ds, cm){
62466 this.ds.un("load", this.onLoad, this);
62467 this.ds.un("datachanged", this.onDataChange, this);
62468 this.ds.un("add", this.onAdd, this);
62469 this.ds.un("remove", this.onRemove, this);
62470 this.ds.un("update", this.onUpdate, this);
62471 this.ds.un("clear", this.onClear, this);
62474 ds.on("load", this.onLoad, this);
62475 ds.on("datachanged", this.onDataChange, this);
62476 ds.on("add", this.onAdd, this);
62477 ds.on("remove", this.onRemove, this);
62478 ds.on("update", this.onUpdate, this);
62479 ds.on("clear", this.onClear, this);
62484 this.cm.un("widthchange", this.onColWidthChange, this);
62485 this.cm.un("headerchange", this.onHeaderChange, this);
62486 this.cm.un("hiddenchange", this.onHiddenChange, this);
62487 this.cm.un("columnmoved", this.onColumnMove, this);
62488 this.cm.un("columnlockchange", this.onColumnLock, this);
62491 this.generateRules(cm);
62492 cm.on("widthchange", this.onColWidthChange, this);
62493 cm.on("headerchange", this.onHeaderChange, this);
62494 cm.on("hiddenchange", this.onHiddenChange, this);
62495 cm.on("columnmoved", this.onColumnMove, this);
62496 cm.on("columnlockchange", this.onColumnLock, this);
62501 init: function(grid){
62502 Roo.grid.GridView.superclass.init.call(this, grid);
62504 this.bind(grid.dataSource, grid.colModel);
62506 grid.on("headerclick", this.handleHeaderClick, this);
62508 if(grid.trackMouseOver){
62509 grid.on("mouseover", this.onRowOver, this);
62510 grid.on("mouseout", this.onRowOut, this);
62512 grid.cancelTextSelection = function(){};
62513 this.gridId = grid.id;
62515 var tpls = this.templates || {};
62518 tpls.master = new Roo.Template(
62519 '<div class="x-grid" hidefocus="true">',
62520 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62521 '<div class="x-grid-topbar"></div>',
62522 '<div class="x-grid-scroller"><div></div></div>',
62523 '<div class="x-grid-locked">',
62524 '<div class="x-grid-header">{lockedHeader}</div>',
62525 '<div class="x-grid-body">{lockedBody}</div>',
62527 '<div class="x-grid-viewport">',
62528 '<div class="x-grid-header">{header}</div>',
62529 '<div class="x-grid-body">{body}</div>',
62531 '<div class="x-grid-bottombar"></div>',
62533 '<div class="x-grid-resize-proxy"> </div>',
62536 tpls.master.disableformats = true;
62540 tpls.header = new Roo.Template(
62541 '<table border="0" cellspacing="0" cellpadding="0">',
62542 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62545 tpls.header.disableformats = true;
62547 tpls.header.compile();
62550 tpls.hcell = new Roo.Template(
62551 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62552 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62555 tpls.hcell.disableFormats = true;
62557 tpls.hcell.compile();
62560 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62561 this.unselectableCls + '" ' + this.unselectable +'> </div>');
62562 tpls.hsplit.disableFormats = true;
62564 tpls.hsplit.compile();
62567 tpls.body = new Roo.Template(
62568 '<table border="0" cellspacing="0" cellpadding="0">',
62569 "<tbody>{rows}</tbody>",
62572 tpls.body.disableFormats = true;
62574 tpls.body.compile();
62577 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62578 tpls.row.disableFormats = true;
62580 tpls.row.compile();
62583 tpls.cell = new Roo.Template(
62584 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62585 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62586 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62589 tpls.cell.disableFormats = true;
62591 tpls.cell.compile();
62593 this.templates = tpls;
62596 // remap these for backwards compat
62597 onColWidthChange : function(){
62598 this.updateColumns.apply(this, arguments);
62600 onHeaderChange : function(){
62601 this.updateHeaders.apply(this, arguments);
62603 onHiddenChange : function(){
62604 this.handleHiddenChange.apply(this, arguments);
62606 onColumnMove : function(){
62607 this.handleColumnMove.apply(this, arguments);
62609 onColumnLock : function(){
62610 this.handleLockChange.apply(this, arguments);
62613 onDataChange : function(){
62615 this.updateHeaderSortState();
62618 onClear : function(){
62622 onUpdate : function(ds, record){
62623 this.refreshRow(record);
62626 refreshRow : function(record){
62627 var ds = this.ds, index;
62628 if(typeof record == 'number'){
62630 record = ds.getAt(index);
62632 index = ds.indexOf(record);
62634 this.insertRows(ds, index, index, true);
62635 this.onRemove(ds, record, index+1, true);
62636 this.syncRowHeights(index, index);
62638 this.fireEvent("rowupdated", this, index, record);
62641 onAdd : function(ds, records, index){
62642 this.insertRows(ds, index, index + (records.length-1));
62645 onRemove : function(ds, record, index, isUpdate){
62646 if(isUpdate !== true){
62647 this.fireEvent("beforerowremoved", this, index, record);
62649 var bt = this.getBodyTable(), lt = this.getLockedTable();
62650 if(bt.rows[index]){
62651 bt.firstChild.removeChild(bt.rows[index]);
62653 if(lt.rows[index]){
62654 lt.firstChild.removeChild(lt.rows[index]);
62656 if(isUpdate !== true){
62657 this.stripeRows(index);
62658 this.syncRowHeights(index, index);
62660 this.fireEvent("rowremoved", this, index, record);
62664 onLoad : function(){
62665 this.scrollToTop();
62669 * Scrolls the grid to the top
62671 scrollToTop : function(){
62673 this.scroller.dom.scrollTop = 0;
62679 * Gets a panel in the header of the grid that can be used for toolbars etc.
62680 * After modifying the contents of this panel a call to grid.autoSize() may be
62681 * required to register any changes in size.
62682 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62683 * @return Roo.Element
62685 getHeaderPanel : function(doShow){
62687 this.headerPanel.show();
62689 return this.headerPanel;
62693 * Gets a panel in the footer of the grid that can be used for toolbars etc.
62694 * After modifying the contents of this panel a call to grid.autoSize() may be
62695 * required to register any changes in size.
62696 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62697 * @return Roo.Element
62699 getFooterPanel : function(doShow){
62701 this.footerPanel.show();
62703 return this.footerPanel;
62706 initElements : function(){
62707 var E = Roo.Element;
62708 var el = this.grid.getGridEl().dom.firstChild;
62709 var cs = el.childNodes;
62711 this.el = new E(el);
62713 this.focusEl = new E(el.firstChild);
62714 this.focusEl.swallowEvent("click", true);
62716 this.headerPanel = new E(cs[1]);
62717 this.headerPanel.enableDisplayMode("block");
62719 this.scroller = new E(cs[2]);
62720 this.scrollSizer = new E(this.scroller.dom.firstChild);
62722 this.lockedWrap = new E(cs[3]);
62723 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62724 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62726 this.mainWrap = new E(cs[4]);
62727 this.mainHd = new E(this.mainWrap.dom.firstChild);
62728 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
62730 this.footerPanel = new E(cs[5]);
62731 this.footerPanel.enableDisplayMode("block");
62733 this.resizeProxy = new E(cs[6]);
62735 this.headerSelector = String.format(
62736 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
62737 this.lockedHd.id, this.mainHd.id
62740 this.splitterSelector = String.format(
62741 '#{0} div.x-grid-split, #{1} div.x-grid-split',
62742 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
62745 idToCssName : function(s)
62747 return s.replace(/[^a-z0-9]+/ig, '-');
62750 getHeaderCell : function(index){
62751 return Roo.DomQuery.select(this.headerSelector)[index];
62754 getHeaderCellMeasure : function(index){
62755 return this.getHeaderCell(index).firstChild;
62758 getHeaderCellText : function(index){
62759 return this.getHeaderCell(index).firstChild.firstChild;
62762 getLockedTable : function(){
62763 return this.lockedBody.dom.firstChild;
62766 getBodyTable : function(){
62767 return this.mainBody.dom.firstChild;
62770 getLockedRow : function(index){
62771 return this.getLockedTable().rows[index];
62774 getRow : function(index){
62775 return this.getBodyTable().rows[index];
62778 getRowComposite : function(index){
62780 this.rowEl = new Roo.CompositeElementLite();
62782 var els = [], lrow, mrow;
62783 if(lrow = this.getLockedRow(index)){
62786 if(mrow = this.getRow(index)){
62789 this.rowEl.elements = els;
62793 * Gets the 'td' of the cell
62795 * @param {Integer} rowIndex row to select
62796 * @param {Integer} colIndex column to select
62800 getCell : function(rowIndex, colIndex){
62801 var locked = this.cm.getLockedCount();
62803 if(colIndex < locked){
62804 source = this.lockedBody.dom.firstChild;
62806 source = this.mainBody.dom.firstChild;
62807 colIndex -= locked;
62809 return source.rows[rowIndex].childNodes[colIndex];
62812 getCellText : function(rowIndex, colIndex){
62813 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
62816 getCellBox : function(cell){
62817 var b = this.fly(cell).getBox();
62818 if(Roo.isOpera){ // opera fails to report the Y
62819 b.y = cell.offsetTop + this.mainBody.getY();
62824 getCellIndex : function(cell){
62825 var id = String(cell.className).match(this.cellRE);
62827 return parseInt(id[1], 10);
62832 findHeaderIndex : function(n){
62833 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62834 return r ? this.getCellIndex(r) : false;
62837 findHeaderCell : function(n){
62838 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62839 return r ? r : false;
62842 findRowIndex : function(n){
62846 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
62847 return r ? r.rowIndex : false;
62850 findCellIndex : function(node){
62851 var stop = this.el.dom;
62852 while(node && node != stop){
62853 if(this.findRE.test(node.className)){
62854 return this.getCellIndex(node);
62856 node = node.parentNode;
62861 getColumnId : function(index){
62862 return this.cm.getColumnId(index);
62865 getSplitters : function()
62867 if(this.splitterSelector){
62868 return Roo.DomQuery.select(this.splitterSelector);
62874 getSplitter : function(index){
62875 return this.getSplitters()[index];
62878 onRowOver : function(e, t){
62880 if((row = this.findRowIndex(t)) !== false){
62881 this.getRowComposite(row).addClass("x-grid-row-over");
62885 onRowOut : function(e, t){
62887 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
62888 this.getRowComposite(row).removeClass("x-grid-row-over");
62892 renderHeaders : function(){
62894 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
62895 var cb = [], lb = [], sb = [], lsb = [], p = {};
62896 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62897 p.cellId = "x-grid-hd-0-" + i;
62898 p.splitId = "x-grid-csplit-0-" + i;
62899 p.id = cm.getColumnId(i);
62900 p.value = cm.getColumnHeader(i) || "";
62901 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
62902 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
62903 if(!cm.isLocked(i)){
62904 cb[cb.length] = ct.apply(p);
62905 sb[sb.length] = st.apply(p);
62907 lb[lb.length] = ct.apply(p);
62908 lsb[lsb.length] = st.apply(p);
62911 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
62912 ht.apply({cells: cb.join(""), splits:sb.join("")})];
62915 updateHeaders : function(){
62916 var html = this.renderHeaders();
62917 this.lockedHd.update(html[0]);
62918 this.mainHd.update(html[1]);
62922 * Focuses the specified row.
62923 * @param {Number} row The row index
62925 focusRow : function(row)
62927 //Roo.log('GridView.focusRow');
62928 var x = this.scroller.dom.scrollLeft;
62929 this.focusCell(row, 0, false);
62930 this.scroller.dom.scrollLeft = x;
62934 * Focuses the specified cell.
62935 * @param {Number} row The row index
62936 * @param {Number} col The column index
62937 * @param {Boolean} hscroll false to disable horizontal scrolling
62939 focusCell : function(row, col, hscroll)
62941 //Roo.log('GridView.focusCell');
62942 var el = this.ensureVisible(row, col, hscroll);
62943 this.focusEl.alignTo(el, "tl-tl");
62945 this.focusEl.focus();
62947 this.focusEl.focus.defer(1, this.focusEl);
62952 * Scrolls the specified cell into view
62953 * @param {Number} row The row index
62954 * @param {Number} col The column index
62955 * @param {Boolean} hscroll false to disable horizontal scrolling
62957 ensureVisible : function(row, col, hscroll)
62959 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
62960 //return null; //disable for testing.
62961 if(typeof row != "number"){
62962 row = row.rowIndex;
62964 if(row < 0 && row >= this.ds.getCount()){
62967 col = (col !== undefined ? col : 0);
62968 var cm = this.grid.colModel;
62969 while(cm.isHidden(col)){
62973 var el = this.getCell(row, col);
62977 var c = this.scroller.dom;
62979 var ctop = parseInt(el.offsetTop, 10);
62980 var cleft = parseInt(el.offsetLeft, 10);
62981 var cbot = ctop + el.offsetHeight;
62982 var cright = cleft + el.offsetWidth;
62984 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
62985 var stop = parseInt(c.scrollTop, 10);
62986 var sleft = parseInt(c.scrollLeft, 10);
62987 var sbot = stop + ch;
62988 var sright = sleft + c.clientWidth;
62990 Roo.log('GridView.ensureVisible:' +
62992 ' c.clientHeight:' + c.clientHeight +
62993 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
63001 c.scrollTop = ctop;
63002 //Roo.log("set scrolltop to ctop DISABLE?");
63003 }else if(cbot > sbot){
63004 //Roo.log("set scrolltop to cbot-ch");
63005 c.scrollTop = cbot-ch;
63008 if(hscroll !== false){
63010 c.scrollLeft = cleft;
63011 }else if(cright > sright){
63012 c.scrollLeft = cright-c.clientWidth;
63019 updateColumns : function(){
63020 this.grid.stopEditing();
63021 var cm = this.grid.colModel, colIds = this.getColumnIds();
63022 //var totalWidth = cm.getTotalWidth();
63024 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63025 //if(cm.isHidden(i)) continue;
63026 var w = cm.getColumnWidth(i);
63027 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63028 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63030 this.updateSplitters();
63033 generateRules : function(cm){
63034 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63035 Roo.util.CSS.removeStyleSheet(rulesId);
63036 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63037 var cid = cm.getColumnId(i);
63039 if(cm.config[i].align){
63040 align = 'text-align:'+cm.config[i].align+';';
63043 if(cm.isHidden(i)){
63044 hidden = 'display:none;';
63046 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63048 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63049 this.hdSelector, cid, " {\n", align, width, "}\n",
63050 this.tdSelector, cid, " {\n",hidden,"\n}\n",
63051 this.splitSelector, cid, " {\n", hidden , "\n}\n");
63053 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63056 updateSplitters : function(){
63057 var cm = this.cm, s = this.getSplitters();
63058 if(s){ // splitters not created yet
63059 var pos = 0, locked = true;
63060 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63061 if(cm.isHidden(i)) {
63064 var w = cm.getColumnWidth(i); // make sure it's a number
63065 if(!cm.isLocked(i) && locked){
63070 s[i].style.left = (pos-this.splitOffset) + "px";
63075 handleHiddenChange : function(colModel, colIndex, hidden){
63077 this.hideColumn(colIndex);
63079 this.unhideColumn(colIndex);
63083 hideColumn : function(colIndex){
63084 var cid = this.getColumnId(colIndex);
63085 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63086 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63088 this.updateHeaders();
63090 this.updateSplitters();
63094 unhideColumn : function(colIndex){
63095 var cid = this.getColumnId(colIndex);
63096 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63097 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63100 this.updateHeaders();
63102 this.updateSplitters();
63106 insertRows : function(dm, firstRow, lastRow, isUpdate){
63107 if(firstRow == 0 && lastRow == dm.getCount()-1){
63111 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63113 var s = this.getScrollState();
63114 var markup = this.renderRows(firstRow, lastRow);
63115 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63116 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63117 this.restoreScroll(s);
63119 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63120 this.syncRowHeights(firstRow, lastRow);
63121 this.stripeRows(firstRow);
63127 bufferRows : function(markup, target, index){
63128 var before = null, trows = target.rows, tbody = target.tBodies[0];
63129 if(index < trows.length){
63130 before = trows[index];
63132 var b = document.createElement("div");
63133 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63134 var rows = b.firstChild.rows;
63135 for(var i = 0, len = rows.length; i < len; i++){
63137 tbody.insertBefore(rows[0], before);
63139 tbody.appendChild(rows[0]);
63146 deleteRows : function(dm, firstRow, lastRow){
63147 if(dm.getRowCount()<1){
63148 this.fireEvent("beforerefresh", this);
63149 this.mainBody.update("");
63150 this.lockedBody.update("");
63151 this.fireEvent("refresh", this);
63153 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63154 var bt = this.getBodyTable();
63155 var tbody = bt.firstChild;
63156 var rows = bt.rows;
63157 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63158 tbody.removeChild(rows[firstRow]);
63160 this.stripeRows(firstRow);
63161 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63165 updateRows : function(dataSource, firstRow, lastRow){
63166 var s = this.getScrollState();
63168 this.restoreScroll(s);
63171 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63175 this.updateHeaderSortState();
63178 getScrollState : function(){
63180 var sb = this.scroller.dom;
63181 return {left: sb.scrollLeft, top: sb.scrollTop};
63184 stripeRows : function(startRow){
63185 if(!this.grid.stripeRows || this.ds.getCount() < 1){
63188 startRow = startRow || 0;
63189 var rows = this.getBodyTable().rows;
63190 var lrows = this.getLockedTable().rows;
63191 var cls = ' x-grid-row-alt ';
63192 for(var i = startRow, len = rows.length; i < len; i++){
63193 var row = rows[i], lrow = lrows[i];
63194 var isAlt = ((i+1) % 2 == 0);
63195 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63196 if(isAlt == hasAlt){
63200 row.className += " x-grid-row-alt";
63202 row.className = row.className.replace("x-grid-row-alt", "");
63205 lrow.className = row.className;
63210 restoreScroll : function(state){
63211 //Roo.log('GridView.restoreScroll');
63212 var sb = this.scroller.dom;
63213 sb.scrollLeft = state.left;
63214 sb.scrollTop = state.top;
63218 syncScroll : function(){
63219 //Roo.log('GridView.syncScroll');
63220 var sb = this.scroller.dom;
63221 var sh = this.mainHd.dom;
63222 var bs = this.mainBody.dom;
63223 var lv = this.lockedBody.dom;
63224 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63225 lv.scrollTop = bs.scrollTop = sb.scrollTop;
63228 handleScroll : function(e){
63230 var sb = this.scroller.dom;
63231 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63235 handleWheel : function(e){
63236 var d = e.getWheelDelta();
63237 this.scroller.dom.scrollTop -= d*22;
63238 // set this here to prevent jumpy scrolling on large tables
63239 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63243 renderRows : function(startRow, endRow){
63244 // pull in all the crap needed to render rows
63245 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63246 var colCount = cm.getColumnCount();
63248 if(ds.getCount() < 1){
63252 // build a map for all the columns
63254 for(var i = 0; i < colCount; i++){
63255 var name = cm.getDataIndex(i);
63257 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63258 renderer : cm.getRenderer(i),
63259 id : cm.getColumnId(i),
63260 locked : cm.isLocked(i),
63261 has_editor : cm.isCellEditable(i)
63265 startRow = startRow || 0;
63266 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63268 // records to render
63269 var rs = ds.getRange(startRow, endRow);
63271 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63274 // As much as I hate to duplicate code, this was branched because FireFox really hates
63275 // [].join("") on strings. The performance difference was substantial enough to
63276 // branch this function
63277 doRender : Roo.isGecko ?
63278 function(cs, rs, ds, startRow, colCount, stripe){
63279 var ts = this.templates, ct = ts.cell, rt = ts.row;
63281 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63283 var hasListener = this.grid.hasListener('rowclass');
63285 for(var j = 0, len = rs.length; j < len; j++){
63286 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63287 for(var i = 0; i < colCount; i++){
63289 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63291 p.css = p.attr = "";
63292 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63293 if(p.value == undefined || p.value === "") {
63294 p.value = " ";
63297 p.css += ' x-grid-editable-cell';
63299 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63300 p.css += ' x-grid-dirty-cell';
63302 var markup = ct.apply(p);
63310 if(stripe && ((rowIndex+1) % 2 == 0)){
63311 alt.push("x-grid-row-alt")
63314 alt.push( " x-grid-dirty-row");
63317 if(this.getRowClass){
63318 alt.push(this.getRowClass(r, rowIndex));
63324 rowIndex : rowIndex,
63327 this.grid.fireEvent('rowclass', this, rowcfg);
63328 alt.push(rowcfg.rowClass);
63330 rp.alt = alt.join(" ");
63331 lbuf+= rt.apply(rp);
63333 buf+= rt.apply(rp);
63335 return [lbuf, buf];
63337 function(cs, rs, ds, startRow, colCount, stripe){
63338 var ts = this.templates, ct = ts.cell, rt = ts.row;
63340 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63341 var hasListener = this.grid.hasListener('rowclass');
63344 for(var j = 0, len = rs.length; j < len; j++){
63345 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63346 for(var i = 0; i < colCount; i++){
63348 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63350 p.css = p.attr = "";
63351 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63352 if(p.value == undefined || p.value === "") {
63353 p.value = " ";
63357 p.css += ' x-grid-editable-cell';
63359 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63360 p.css += ' x-grid-dirty-cell'
63363 var markup = ct.apply(p);
63365 cb[cb.length] = markup;
63367 lcb[lcb.length] = markup;
63371 if(stripe && ((rowIndex+1) % 2 == 0)){
63372 alt.push( "x-grid-row-alt");
63375 alt.push(" x-grid-dirty-row");
63378 if(this.getRowClass){
63379 alt.push( this.getRowClass(r, rowIndex));
63385 rowIndex : rowIndex,
63388 this.grid.fireEvent('rowclass', this, rowcfg);
63389 alt.push(rowcfg.rowClass);
63392 rp.alt = alt.join(" ");
63393 rp.cells = lcb.join("");
63394 lbuf[lbuf.length] = rt.apply(rp);
63395 rp.cells = cb.join("");
63396 buf[buf.length] = rt.apply(rp);
63398 return [lbuf.join(""), buf.join("")];
63401 renderBody : function(){
63402 var markup = this.renderRows();
63403 var bt = this.templates.body;
63404 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63408 * Refreshes the grid
63409 * @param {Boolean} headersToo
63411 refresh : function(headersToo){
63412 this.fireEvent("beforerefresh", this);
63413 this.grid.stopEditing();
63414 var result = this.renderBody();
63415 this.lockedBody.update(result[0]);
63416 this.mainBody.update(result[1]);
63417 if(headersToo === true){
63418 this.updateHeaders();
63419 this.updateColumns();
63420 this.updateSplitters();
63421 this.updateHeaderSortState();
63423 this.syncRowHeights();
63425 this.fireEvent("refresh", this);
63428 handleColumnMove : function(cm, oldIndex, newIndex){
63429 this.indexMap = null;
63430 var s = this.getScrollState();
63431 this.refresh(true);
63432 this.restoreScroll(s);
63433 this.afterMove(newIndex);
63436 afterMove : function(colIndex){
63437 if(this.enableMoveAnim && Roo.enableFx){
63438 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63440 // if multisort - fix sortOrder, and reload..
63441 if (this.grid.dataSource.multiSort) {
63442 // the we can call sort again..
63443 var dm = this.grid.dataSource;
63444 var cm = this.grid.colModel;
63446 for(var i = 0; i < cm.config.length; i++ ) {
63448 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63449 continue; // dont' bother, it's not in sort list or being set.
63452 so.push(cm.config[i].dataIndex);
63455 dm.load(dm.lastOptions);
63462 updateCell : function(dm, rowIndex, dataIndex){
63463 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63464 if(typeof colIndex == "undefined"){ // not present in grid
63467 var cm = this.grid.colModel;
63468 var cell = this.getCell(rowIndex, colIndex);
63469 var cellText = this.getCellText(rowIndex, colIndex);
63472 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63473 id : cm.getColumnId(colIndex),
63474 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63476 var renderer = cm.getRenderer(colIndex);
63477 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63478 if(typeof val == "undefined" || val === "") {
63481 cellText.innerHTML = val;
63482 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63483 this.syncRowHeights(rowIndex, rowIndex);
63486 calcColumnWidth : function(colIndex, maxRowsToMeasure){
63488 if(this.grid.autoSizeHeaders){
63489 var h = this.getHeaderCellMeasure(colIndex);
63490 maxWidth = Math.max(maxWidth, h.scrollWidth);
63493 if(this.cm.isLocked(colIndex)){
63494 tb = this.getLockedTable();
63497 tb = this.getBodyTable();
63498 index = colIndex - this.cm.getLockedCount();
63501 var rows = tb.rows;
63502 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63503 for(var i = 0; i < stopIndex; i++){
63504 var cell = rows[i].childNodes[index].firstChild;
63505 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63508 return maxWidth + /*margin for error in IE*/ 5;
63511 * Autofit a column to its content.
63512 * @param {Number} colIndex
63513 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63515 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63516 if(this.cm.isHidden(colIndex)){
63517 return; // can't calc a hidden column
63520 var cid = this.cm.getColumnId(colIndex);
63521 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63522 if(this.grid.autoSizeHeaders){
63523 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63526 var newWidth = this.calcColumnWidth(colIndex);
63527 this.cm.setColumnWidth(colIndex,
63528 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63529 if(!suppressEvent){
63530 this.grid.fireEvent("columnresize", colIndex, newWidth);
63535 * Autofits all columns to their content and then expands to fit any extra space in the grid
63537 autoSizeColumns : function(){
63538 var cm = this.grid.colModel;
63539 var colCount = cm.getColumnCount();
63540 for(var i = 0; i < colCount; i++){
63541 this.autoSizeColumn(i, true, true);
63543 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63546 this.updateColumns();
63552 * Autofits all columns to the grid's width proportionate with their current size
63553 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63555 fitColumns : function(reserveScrollSpace){
63556 var cm = this.grid.colModel;
63557 var colCount = cm.getColumnCount();
63561 for (i = 0; i < colCount; i++){
63562 if(!cm.isHidden(i) && !cm.isFixed(i)){
63563 w = cm.getColumnWidth(i);
63569 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63570 if(reserveScrollSpace){
63573 var frac = (avail - cm.getTotalWidth())/width;
63574 while (cols.length){
63577 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63579 this.updateColumns();
63583 onRowSelect : function(rowIndex){
63584 var row = this.getRowComposite(rowIndex);
63585 row.addClass("x-grid-row-selected");
63588 onRowDeselect : function(rowIndex){
63589 var row = this.getRowComposite(rowIndex);
63590 row.removeClass("x-grid-row-selected");
63593 onCellSelect : function(row, col){
63594 var cell = this.getCell(row, col);
63596 Roo.fly(cell).addClass("x-grid-cell-selected");
63600 onCellDeselect : function(row, col){
63601 var cell = this.getCell(row, col);
63603 Roo.fly(cell).removeClass("x-grid-cell-selected");
63607 updateHeaderSortState : function(){
63609 // sort state can be single { field: xxx, direction : yyy}
63610 // or { xxx=>ASC , yyy : DESC ..... }
63613 if (!this.ds.multiSort) {
63614 var state = this.ds.getSortState();
63618 mstate[state.field] = state.direction;
63619 // FIXME... - this is not used here.. but might be elsewhere..
63620 this.sortState = state;
63623 mstate = this.ds.sortToggle;
63625 //remove existing sort classes..
63627 var sc = this.sortClasses;
63628 var hds = this.el.select(this.headerSelector).removeClass(sc);
63630 for(var f in mstate) {
63632 var sortColumn = this.cm.findColumnIndex(f);
63634 if(sortColumn != -1){
63635 var sortDir = mstate[f];
63636 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63645 handleHeaderClick : function(g, index,e){
63647 Roo.log("header click");
63650 // touch events on header are handled by context
63651 this.handleHdCtx(g,index,e);
63656 if(this.headersDisabled){
63659 var dm = g.dataSource, cm = g.colModel;
63660 if(!cm.isSortable(index)){
63665 if (dm.multiSort) {
63666 // update the sortOrder
63668 for(var i = 0; i < cm.config.length; i++ ) {
63670 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63671 continue; // dont' bother, it's not in sort list or being set.
63674 so.push(cm.config[i].dataIndex);
63680 dm.sort(cm.getDataIndex(index));
63684 destroy : function(){
63686 this.colMenu.removeAll();
63687 Roo.menu.MenuMgr.unregister(this.colMenu);
63688 this.colMenu.getEl().remove();
63689 delete this.colMenu;
63692 this.hmenu.removeAll();
63693 Roo.menu.MenuMgr.unregister(this.hmenu);
63694 this.hmenu.getEl().remove();
63697 if(this.grid.enableColumnMove){
63698 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63700 for(var dd in dds){
63701 if(!dds[dd].config.isTarget && dds[dd].dragElId){
63702 var elid = dds[dd].dragElId;
63704 Roo.get(elid).remove();
63705 } else if(dds[dd].config.isTarget){
63706 dds[dd].proxyTop.remove();
63707 dds[dd].proxyBottom.remove();
63710 if(Roo.dd.DDM.locationCache[dd]){
63711 delete Roo.dd.DDM.locationCache[dd];
63714 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63717 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63718 this.bind(null, null);
63719 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63722 handleLockChange : function(){
63723 this.refresh(true);
63726 onDenyColumnLock : function(){
63730 onDenyColumnHide : function(){
63734 handleHdMenuClick : function(item){
63735 var index = this.hdCtxIndex;
63736 var cm = this.cm, ds = this.ds;
63739 ds.sort(cm.getDataIndex(index), "ASC");
63742 ds.sort(cm.getDataIndex(index), "DESC");
63745 var lc = cm.getLockedCount();
63746 if(cm.getColumnCount(true) <= lc+1){
63747 this.onDenyColumnLock();
63751 cm.setLocked(index, true, true);
63752 cm.moveColumn(index, lc);
63753 this.grid.fireEvent("columnmove", index, lc);
63755 cm.setLocked(index, true);
63759 var lc = cm.getLockedCount();
63760 if((lc-1) != index){
63761 cm.setLocked(index, false, true);
63762 cm.moveColumn(index, lc-1);
63763 this.grid.fireEvent("columnmove", index, lc-1);
63765 cm.setLocked(index, false);
63768 case 'wider': // used to expand cols on touch..
63770 var cw = cm.getColumnWidth(index);
63771 cw += (item.id == 'wider' ? 1 : -1) * 50;
63772 cw = Math.max(0, cw);
63773 cw = Math.min(cw,4000);
63774 cm.setColumnWidth(index, cw);
63778 index = cm.getIndexById(item.id.substr(4));
63780 if(item.checked && cm.getColumnCount(true) <= 1){
63781 this.onDenyColumnHide();
63784 cm.setHidden(index, item.checked);
63790 beforeColMenuShow : function(){
63791 var cm = this.cm, colCount = cm.getColumnCount();
63792 this.colMenu.removeAll();
63795 for(var i = 0; i < colCount; i++){
63797 id: "col-"+cm.getColumnId(i),
63798 text: cm.getColumnHeader(i),
63799 checked: !cm.isHidden(i),
63804 if (this.grid.sortColMenu) {
63805 items.sort(function(a,b) {
63806 if (a.text == b.text) {
63809 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
63813 for(var i = 0; i < colCount; i++){
63814 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
63818 handleHdCtx : function(g, index, e){
63820 var hd = this.getHeaderCell(index);
63821 this.hdCtxIndex = index;
63822 var ms = this.hmenu.items, cm = this.cm;
63823 ms.get("asc").setDisabled(!cm.isSortable(index));
63824 ms.get("desc").setDisabled(!cm.isSortable(index));
63825 if(this.grid.enableColLock !== false){
63826 ms.get("lock").setDisabled(cm.isLocked(index));
63827 ms.get("unlock").setDisabled(!cm.isLocked(index));
63829 this.hmenu.show(hd, "tl-bl");
63832 handleHdOver : function(e){
63833 var hd = this.findHeaderCell(e.getTarget());
63834 if(hd && !this.headersDisabled){
63835 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
63836 this.fly(hd).addClass("x-grid-hd-over");
63841 handleHdOut : function(e){
63842 var hd = this.findHeaderCell(e.getTarget());
63844 this.fly(hd).removeClass("x-grid-hd-over");
63848 handleSplitDblClick : function(e, t){
63849 var i = this.getCellIndex(t);
63850 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
63851 this.autoSizeColumn(i, true);
63856 render : function(){
63859 var colCount = cm.getColumnCount();
63861 if(this.grid.monitorWindowResize === true){
63862 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
63864 var header = this.renderHeaders();
63865 var body = this.templates.body.apply({rows:""});
63866 var html = this.templates.master.apply({
63869 lockedHeader: header[0],
63873 //this.updateColumns();
63875 this.grid.getGridEl().dom.innerHTML = html;
63877 this.initElements();
63879 // a kludge to fix the random scolling effect in webkit
63880 this.el.on("scroll", function() {
63881 this.el.dom.scrollTop=0; // hopefully not recursive..
63884 this.scroller.on("scroll", this.handleScroll, this);
63885 this.lockedBody.on("mousewheel", this.handleWheel, this);
63886 this.mainBody.on("mousewheel", this.handleWheel, this);
63888 this.mainHd.on("mouseover", this.handleHdOver, this);
63889 this.mainHd.on("mouseout", this.handleHdOut, this);
63890 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
63891 {delegate: "."+this.splitClass});
63893 this.lockedHd.on("mouseover", this.handleHdOver, this);
63894 this.lockedHd.on("mouseout", this.handleHdOut, this);
63895 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
63896 {delegate: "."+this.splitClass});
63898 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
63899 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63902 this.updateSplitters();
63904 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
63905 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63906 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63909 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
63910 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
63912 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
63913 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
63915 if(this.grid.enableColLock !== false){
63916 this.hmenu.add('-',
63917 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
63918 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
63922 this.hmenu.add('-',
63923 {id:"wider", text: this.columnsWiderText},
63924 {id:"narrow", text: this.columnsNarrowText }
63930 if(this.grid.enableColumnHide !== false){
63932 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
63933 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
63934 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
63936 this.hmenu.add('-',
63937 {id:"columns", text: this.columnsText, menu: this.colMenu}
63940 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
63942 this.grid.on("headercontextmenu", this.handleHdCtx, this);
63945 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
63946 this.dd = new Roo.grid.GridDragZone(this.grid, {
63947 ddGroup : this.grid.ddGroup || 'GridDD'
63953 for(var i = 0; i < colCount; i++){
63954 if(cm.isHidden(i)){
63955 this.hideColumn(i);
63957 if(cm.config[i].align){
63958 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
63959 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
63963 this.updateHeaderSortState();
63965 this.beforeInitialResize();
63968 // two part rendering gives faster view to the user
63969 this.renderPhase2.defer(1, this);
63972 renderPhase2 : function(){
63973 // render the rows now
63975 if(this.grid.autoSizeColumns){
63976 this.autoSizeColumns();
63980 beforeInitialResize : function(){
63984 onColumnSplitterMoved : function(i, w){
63985 this.userResized = true;
63986 var cm = this.grid.colModel;
63987 cm.setColumnWidth(i, w, true);
63988 var cid = cm.getColumnId(i);
63989 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63990 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63991 this.updateSplitters();
63993 this.grid.fireEvent("columnresize", i, w);
63996 syncRowHeights : function(startIndex, endIndex){
63997 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
63998 startIndex = startIndex || 0;
63999 var mrows = this.getBodyTable().rows;
64000 var lrows = this.getLockedTable().rows;
64001 var len = mrows.length-1;
64002 endIndex = Math.min(endIndex || len, len);
64003 for(var i = startIndex; i <= endIndex; i++){
64004 var m = mrows[i], l = lrows[i];
64005 var h = Math.max(m.offsetHeight, l.offsetHeight);
64006 m.style.height = l.style.height = h + "px";
64011 layout : function(initialRender, is2ndPass)
64014 var auto = g.autoHeight;
64015 var scrollOffset = 16;
64016 var c = g.getGridEl(), cm = this.cm,
64017 expandCol = g.autoExpandColumn,
64019 //c.beginMeasure();
64021 if(!c.dom.offsetWidth){ // display:none?
64023 this.lockedWrap.show();
64024 this.mainWrap.show();
64029 var hasLock = this.cm.isLocked(0);
64031 var tbh = this.headerPanel.getHeight();
64032 var bbh = this.footerPanel.getHeight();
64035 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64036 var newHeight = ch + c.getBorderWidth("tb");
64038 newHeight = Math.min(g.maxHeight, newHeight);
64040 c.setHeight(newHeight);
64044 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64047 var s = this.scroller;
64049 var csize = c.getSize(true);
64051 this.el.setSize(csize.width, csize.height);
64053 this.headerPanel.setWidth(csize.width);
64054 this.footerPanel.setWidth(csize.width);
64056 var hdHeight = this.mainHd.getHeight();
64057 var vw = csize.width;
64058 var vh = csize.height - (tbh + bbh);
64062 var bt = this.getBodyTable();
64064 if(cm.getLockedCount() == cm.config.length){
64065 bt = this.getLockedTable();
64068 var ltWidth = hasLock ?
64069 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64071 var scrollHeight = bt.offsetHeight;
64072 var scrollWidth = ltWidth + bt.offsetWidth;
64073 var vscroll = false, hscroll = false;
64075 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64077 var lw = this.lockedWrap, mw = this.mainWrap;
64078 var lb = this.lockedBody, mb = this.mainBody;
64080 setTimeout(function(){
64081 var t = s.dom.offsetTop;
64082 var w = s.dom.clientWidth,
64083 h = s.dom.clientHeight;
64086 lw.setSize(ltWidth, h);
64088 mw.setLeftTop(ltWidth, t);
64089 mw.setSize(w-ltWidth, h);
64091 lb.setHeight(h-hdHeight);
64092 mb.setHeight(h-hdHeight);
64094 if(is2ndPass !== true && !gv.userResized && expandCol){
64095 // high speed resize without full column calculation
64097 var ci = cm.getIndexById(expandCol);
64099 ci = cm.findColumnIndex(expandCol);
64101 ci = Math.max(0, ci); // make sure it's got at least the first col.
64102 var expandId = cm.getColumnId(ci);
64103 var tw = cm.getTotalWidth(false);
64104 var currentWidth = cm.getColumnWidth(ci);
64105 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64106 if(currentWidth != cw){
64107 cm.setColumnWidth(ci, cw, true);
64108 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64109 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64110 gv.updateSplitters();
64111 gv.layout(false, true);
64123 onWindowResize : function(){
64124 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64130 appendFooter : function(parentEl){
64134 sortAscText : "Sort Ascending",
64135 sortDescText : "Sort Descending",
64136 lockText : "Lock Column",
64137 unlockText : "Unlock Column",
64138 columnsText : "Columns",
64140 columnsWiderText : "Wider",
64141 columnsNarrowText : "Thinner"
64145 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64146 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64147 this.proxy.el.addClass('x-grid3-col-dd');
64150 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64151 handleMouseDown : function(e){
64155 callHandleMouseDown : function(e){
64156 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64161 * Ext JS Library 1.1.1
64162 * Copyright(c) 2006-2007, Ext JS, LLC.
64164 * Originally Released Under LGPL - original licence link has changed is not relivant.
64167 * <script type="text/javascript">
64170 * @extends Roo.dd.DDProxy
64171 * @class Roo.grid.SplitDragZone
64172 * Support for Column Header resizing
64174 * @param {Object} config
64177 // This is a support class used internally by the Grid components
64178 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64180 this.view = grid.getView();
64181 this.proxy = this.view.resizeProxy;
64182 Roo.grid.SplitDragZone.superclass.constructor.call(
64185 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64187 dragElId : Roo.id(this.proxy.dom),
64192 this.setHandleElId(Roo.id(hd));
64193 if (hd2 !== false) {
64194 this.setOuterHandleElId(Roo.id(hd2));
64197 this.scroll = false;
64199 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64200 fly: Roo.Element.fly,
64202 b4StartDrag : function(x, y){
64203 this.view.headersDisabled = true;
64204 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64205 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64207 this.proxy.setHeight(h);
64209 // for old system colWidth really stored the actual width?
64210 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64211 // which in reality did not work.. - it worked only for fixed sizes
64212 // for resizable we need to use actual sizes.
64213 var w = this.cm.getColumnWidth(this.cellIndex);
64214 if (!this.view.mainWrap) {
64216 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64221 // this was w-this.grid.minColumnWidth;
64222 // doesnt really make sense? - w = thie curren width or the rendered one?
64223 var minw = Math.max(w-this.grid.minColumnWidth, 0);
64224 this.resetConstraints();
64225 this.setXConstraint(minw, 1000);
64226 this.setYConstraint(0, 0);
64227 this.minX = x - minw;
64228 this.maxX = x + 1000;
64230 if (!this.view.mainWrap) { // this is Bootstrap code..
64231 this.getDragEl().style.display='block';
64234 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64238 handleMouseDown : function(e){
64239 ev = Roo.EventObject.setEvent(e);
64240 var t = this.fly(ev.getTarget());
64241 if(t.hasClass("x-grid-split")){
64242 this.cellIndex = this.view.getCellIndex(t.dom);
64243 this.split = t.dom;
64244 this.cm = this.grid.colModel;
64245 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64246 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64251 endDrag : function(e){
64252 this.view.headersDisabled = false;
64253 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64254 var diff = endX - this.startPos;
64256 var w = this.cm.getColumnWidth(this.cellIndex);
64257 if (!this.view.mainWrap) {
64260 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64263 autoOffset : function(){
64264 this.setDelta(0,0);
64268 * Ext JS Library 1.1.1
64269 * Copyright(c) 2006-2007, Ext JS, LLC.
64271 * Originally Released Under LGPL - original licence link has changed is not relivant.
64274 * <script type="text/javascript">
64278 // This is a support class used internally by the Grid components
64279 Roo.grid.GridDragZone = function(grid, config){
64280 this.view = grid.getView();
64281 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64282 if(this.view.lockedBody){
64283 this.setHandleElId(Roo.id(this.view.mainBody.dom));
64284 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64286 this.scroll = false;
64288 this.ddel = document.createElement('div');
64289 this.ddel.className = 'x-grid-dd-wrap';
64292 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64293 ddGroup : "GridDD",
64295 getDragData : function(e){
64296 var t = Roo.lib.Event.getTarget(e);
64297 var rowIndex = this.view.findRowIndex(t);
64298 var sm = this.grid.selModel;
64300 //Roo.log(rowIndex);
64302 if (sm.getSelectedCell) {
64303 // cell selection..
64304 if (!sm.getSelectedCell()) {
64307 if (rowIndex != sm.getSelectedCell()[0]) {
64312 if (sm.getSelections && sm.getSelections().length < 1) {
64317 // before it used to all dragging of unseleted... - now we dont do that.
64318 if(rowIndex !== false){
64323 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64325 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64328 if (e.hasModifier()){
64329 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64332 Roo.log("getDragData");
64337 rowIndex: rowIndex,
64338 selections: sm.getSelections ? sm.getSelections() : (
64339 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64346 onInitDrag : function(e){
64347 var data = this.dragData;
64348 this.ddel.innerHTML = this.grid.getDragDropText();
64349 this.proxy.update(this.ddel);
64350 // fire start drag?
64353 afterRepair : function(){
64354 this.dragging = false;
64357 getRepairXY : function(e, data){
64361 onEndDrag : function(data, e){
64365 onValidDrop : function(dd, e, id){
64370 beforeInvalidDrop : function(e, id){
64375 * Ext JS Library 1.1.1
64376 * Copyright(c) 2006-2007, Ext JS, LLC.
64378 * Originally Released Under LGPL - original licence link has changed is not relivant.
64381 * <script type="text/javascript">
64386 * @class Roo.grid.ColumnModel
64387 * @extends Roo.util.Observable
64388 * This is the default implementation of a ColumnModel used by the Grid. It defines
64389 * the columns in the grid.
64392 var colModel = new Roo.grid.ColumnModel([
64393 {header: "Ticker", width: 60, sortable: true, locked: true},
64394 {header: "Company Name", width: 150, sortable: true},
64395 {header: "Market Cap.", width: 100, sortable: true},
64396 {header: "$ Sales", width: 100, sortable: true, renderer: money},
64397 {header: "Employees", width: 100, sortable: true, resizable: false}
64402 * The config options listed for this class are options which may appear in each
64403 * individual column definition.
64404 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64406 * @param {Object} config An Array of column config objects. See this class's
64407 * config objects for details.
64409 Roo.grid.ColumnModel = function(config){
64411 * The config passed into the constructor
64413 this.config = []; //config;
64416 // if no id, create one
64417 // if the column does not have a dataIndex mapping,
64418 // map it to the order it is in the config
64419 for(var i = 0, len = config.length; i < len; i++){
64420 this.addColumn(config[i]);
64425 * The width of columns which have no width specified (defaults to 100)
64428 this.defaultWidth = 100;
64431 * Default sortable of columns which have no sortable specified (defaults to false)
64434 this.defaultSortable = false;
64438 * @event widthchange
64439 * Fires when the width of a column changes.
64440 * @param {ColumnModel} this
64441 * @param {Number} columnIndex The column index
64442 * @param {Number} newWidth The new width
64444 "widthchange": true,
64446 * @event headerchange
64447 * Fires when the text of a header changes.
64448 * @param {ColumnModel} this
64449 * @param {Number} columnIndex The column index
64450 * @param {Number} newText The new header text
64452 "headerchange": true,
64454 * @event hiddenchange
64455 * Fires when a column is hidden or "unhidden".
64456 * @param {ColumnModel} this
64457 * @param {Number} columnIndex The column index
64458 * @param {Boolean} hidden true if hidden, false otherwise
64460 "hiddenchange": true,
64462 * @event columnmoved
64463 * Fires when a column is moved.
64464 * @param {ColumnModel} this
64465 * @param {Number} oldIndex
64466 * @param {Number} newIndex
64468 "columnmoved" : true,
64470 * @event columlockchange
64471 * Fires when a column's locked state is changed
64472 * @param {ColumnModel} this
64473 * @param {Number} colIndex
64474 * @param {Boolean} locked true if locked
64476 "columnlockchange" : true
64478 Roo.grid.ColumnModel.superclass.constructor.call(this);
64480 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64482 * @cfg {String} header [required] The header text to display in the Grid view.
64485 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64488 * @cfg {String} smHeader Header at Bootsrap Small width
64491 * @cfg {String} mdHeader Header at Bootsrap Medium width
64494 * @cfg {String} lgHeader Header at Bootsrap Large width
64497 * @cfg {String} xlHeader Header at Bootsrap extra Large width
64500 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
64501 * {@link Roo.data.Record} definition from which to draw the column's value. If not
64502 * specified, the column's index is used as an index into the Record's data Array.
64505 * @cfg {Number} width The initial width in pixels of the column. Using this
64506 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64509 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
64510 * Defaults to the value of the {@link #defaultSortable} property.
64511 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64514 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
64517 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
64520 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
64523 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
64526 * @cfg {Function} renderer A function used to generate HTML markup for a cell
64527 * given the cell's data value. See {@link #setRenderer}. If not specified, the
64528 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64529 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64532 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
64535 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
64538 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
64541 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
64544 * @cfg {String} tooltip mouse over tooltip text
64547 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
64550 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
64553 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
64556 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
64559 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
64562 * Returns the id of the column at the specified index.
64563 * @param {Number} index The column index
64564 * @return {String} the id
64566 getColumnId : function(index){
64567 return this.config[index].id;
64571 * Returns the column for a specified id.
64572 * @param {String} id The column id
64573 * @return {Object} the column
64575 getColumnById : function(id){
64576 return this.lookup[id];
64581 * Returns the column Object for a specified dataIndex.
64582 * @param {String} dataIndex The column dataIndex
64583 * @return {Object|Boolean} the column or false if not found
64585 getColumnByDataIndex: function(dataIndex){
64586 var index = this.findColumnIndex(dataIndex);
64587 return index > -1 ? this.config[index] : false;
64591 * Returns the index for a specified column id.
64592 * @param {String} id The column id
64593 * @return {Number} the index, or -1 if not found
64595 getIndexById : function(id){
64596 for(var i = 0, len = this.config.length; i < len; i++){
64597 if(this.config[i].id == id){
64605 * Returns the index for a specified column dataIndex.
64606 * @param {String} dataIndex The column dataIndex
64607 * @return {Number} the index, or -1 if not found
64610 findColumnIndex : function(dataIndex){
64611 for(var i = 0, len = this.config.length; i < len; i++){
64612 if(this.config[i].dataIndex == dataIndex){
64620 moveColumn : function(oldIndex, newIndex){
64621 var c = this.config[oldIndex];
64622 this.config.splice(oldIndex, 1);
64623 this.config.splice(newIndex, 0, c);
64624 this.dataMap = null;
64625 this.fireEvent("columnmoved", this, oldIndex, newIndex);
64628 isLocked : function(colIndex){
64629 return this.config[colIndex].locked === true;
64632 setLocked : function(colIndex, value, suppressEvent){
64633 if(this.isLocked(colIndex) == value){
64636 this.config[colIndex].locked = value;
64637 if(!suppressEvent){
64638 this.fireEvent("columnlockchange", this, colIndex, value);
64642 getTotalLockedWidth : function(){
64643 var totalWidth = 0;
64644 for(var i = 0; i < this.config.length; i++){
64645 if(this.isLocked(i) && !this.isHidden(i)){
64646 this.totalWidth += this.getColumnWidth(i);
64652 getLockedCount : function(){
64653 for(var i = 0, len = this.config.length; i < len; i++){
64654 if(!this.isLocked(i)){
64659 return this.config.length;
64663 * Returns the number of columns.
64666 getColumnCount : function(visibleOnly){
64667 if(visibleOnly === true){
64669 for(var i = 0, len = this.config.length; i < len; i++){
64670 if(!this.isHidden(i)){
64676 return this.config.length;
64680 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64681 * @param {Function} fn
64682 * @param {Object} scope (optional)
64683 * @return {Array} result
64685 getColumnsBy : function(fn, scope){
64687 for(var i = 0, len = this.config.length; i < len; i++){
64688 var c = this.config[i];
64689 if(fn.call(scope||this, c, i) === true){
64697 * Returns true if the specified column is sortable.
64698 * @param {Number} col The column index
64699 * @return {Boolean}
64701 isSortable : function(col){
64702 if(typeof this.config[col].sortable == "undefined"){
64703 return this.defaultSortable;
64705 return this.config[col].sortable;
64709 * Returns the rendering (formatting) function defined for the column.
64710 * @param {Number} col The column index.
64711 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64713 getRenderer : function(col){
64714 if(!this.config[col].renderer){
64715 return Roo.grid.ColumnModel.defaultRenderer;
64717 return this.config[col].renderer;
64721 * Sets the rendering (formatting) function for a column.
64722 * @param {Number} col The column index
64723 * @param {Function} fn The function to use to process the cell's raw data
64724 * to return HTML markup for the grid view. The render function is called with
64725 * the following parameters:<ul>
64726 * <li>Data value.</li>
64727 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64728 * <li>css A CSS style string to apply to the table cell.</li>
64729 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
64730 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
64731 * <li>Row index</li>
64732 * <li>Column index</li>
64733 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
64735 setRenderer : function(col, fn){
64736 this.config[col].renderer = fn;
64740 * Returns the width for the specified column.
64741 * @param {Number} col The column index
64742 * @param (optional) {String} gridSize bootstrap width size.
64745 getColumnWidth : function(col, gridSize)
64747 var cfg = this.config[col];
64749 if (typeof(gridSize) == 'undefined') {
64750 return cfg.width * 1 || this.defaultWidth;
64752 if (gridSize === false) { // if we set it..
64753 return cfg.width || false;
64755 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
64757 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
64758 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
64761 return cfg[ sizes[i] ];
64768 * Sets the width for a column.
64769 * @param {Number} col The column index
64770 * @param {Number} width The new width
64772 setColumnWidth : function(col, width, suppressEvent){
64773 this.config[col].width = width;
64774 this.totalWidth = null;
64775 if(!suppressEvent){
64776 this.fireEvent("widthchange", this, col, width);
64781 * Returns the total width of all columns.
64782 * @param {Boolean} includeHidden True to include hidden column widths
64785 getTotalWidth : function(includeHidden){
64786 if(!this.totalWidth){
64787 this.totalWidth = 0;
64788 for(var i = 0, len = this.config.length; i < len; i++){
64789 if(includeHidden || !this.isHidden(i)){
64790 this.totalWidth += this.getColumnWidth(i);
64794 return this.totalWidth;
64798 * Returns the header for the specified column.
64799 * @param {Number} col The column index
64802 getColumnHeader : function(col){
64803 return this.config[col].header;
64807 * Sets the header for a column.
64808 * @param {Number} col The column index
64809 * @param {String} header The new header
64811 setColumnHeader : function(col, header){
64812 this.config[col].header = header;
64813 this.fireEvent("headerchange", this, col, header);
64817 * Returns the tooltip for the specified column.
64818 * @param {Number} col The column index
64821 getColumnTooltip : function(col){
64822 return this.config[col].tooltip;
64825 * Sets the tooltip for a column.
64826 * @param {Number} col The column index
64827 * @param {String} tooltip The new tooltip
64829 setColumnTooltip : function(col, tooltip){
64830 this.config[col].tooltip = tooltip;
64834 * Returns the dataIndex for the specified column.
64835 * @param {Number} col The column index
64838 getDataIndex : function(col){
64839 return this.config[col].dataIndex;
64843 * Sets the dataIndex for a column.
64844 * @param {Number} col The column index
64845 * @param {Number} dataIndex The new dataIndex
64847 setDataIndex : function(col, dataIndex){
64848 this.config[col].dataIndex = dataIndex;
64854 * Returns true if the cell is editable.
64855 * @param {Number} colIndex The column index
64856 * @param {Number} rowIndex The row index - this is nto actually used..?
64857 * @return {Boolean}
64859 isCellEditable : function(colIndex, rowIndex){
64860 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
64864 * Returns the editor defined for the cell/column.
64865 * return false or null to disable editing.
64866 * @param {Number} colIndex The column index
64867 * @param {Number} rowIndex The row index
64870 getCellEditor : function(colIndex, rowIndex){
64871 return this.config[colIndex].editor;
64875 * Sets if a column is editable.
64876 * @param {Number} col The column index
64877 * @param {Boolean} editable True if the column is editable
64879 setEditable : function(col, editable){
64880 this.config[col].editable = editable;
64885 * Returns true if the column is hidden.
64886 * @param {Number} colIndex The column index
64887 * @return {Boolean}
64889 isHidden : function(colIndex){
64890 return this.config[colIndex].hidden;
64895 * Returns true if the column width cannot be changed
64897 isFixed : function(colIndex){
64898 return this.config[colIndex].fixed;
64902 * Returns true if the column can be resized
64903 * @return {Boolean}
64905 isResizable : function(colIndex){
64906 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
64909 * Sets if a column is hidden.
64910 * @param {Number} colIndex The column index
64911 * @param {Boolean} hidden True if the column is hidden
64913 setHidden : function(colIndex, hidden){
64914 this.config[colIndex].hidden = hidden;
64915 this.totalWidth = null;
64916 this.fireEvent("hiddenchange", this, colIndex, hidden);
64920 * Sets the editor for a column.
64921 * @param {Number} col The column index
64922 * @param {Object} editor The editor object
64924 setEditor : function(col, editor){
64925 this.config[col].editor = editor;
64928 * Add a column (experimental...) - defaults to adding to the end..
64929 * @param {Object} config
64931 addColumn : function(c)
64934 var i = this.config.length;
64935 this.config[i] = c;
64937 if(typeof c.dataIndex == "undefined"){
64940 if(typeof c.renderer == "string"){
64941 c.renderer = Roo.util.Format[c.renderer];
64943 if(typeof c.id == "undefined"){
64946 if(c.editor && c.editor.xtype){
64947 c.editor = Roo.factory(c.editor, Roo.grid);
64949 if(c.editor && c.editor.isFormField){
64950 c.editor = new Roo.grid.GridEditor(c.editor);
64952 this.lookup[c.id] = c;
64957 Roo.grid.ColumnModel.defaultRenderer = function(value)
64959 if(typeof value == "object") {
64962 if(typeof value == "string" && value.length < 1){
64966 return String.format("{0}", value);
64969 // Alias for backwards compatibility
64970 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
64973 * Ext JS Library 1.1.1
64974 * Copyright(c) 2006-2007, Ext JS, LLC.
64976 * Originally Released Under LGPL - original licence link has changed is not relivant.
64979 * <script type="text/javascript">
64983 * @class Roo.grid.AbstractSelectionModel
64984 * @extends Roo.util.Observable
64986 * Abstract base class for grid SelectionModels. It provides the interface that should be
64987 * implemented by descendant classes. This class should not be directly instantiated.
64990 Roo.grid.AbstractSelectionModel = function(){
64991 this.locked = false;
64992 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
64995 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
64996 /** @ignore Called by the grid automatically. Do not call directly. */
64997 init : function(grid){
65003 * Locks the selections.
65006 this.locked = true;
65010 * Unlocks the selections.
65012 unlock : function(){
65013 this.locked = false;
65017 * Returns true if the selections are locked.
65018 * @return {Boolean}
65020 isLocked : function(){
65021 return this.locked;
65025 * Ext JS Library 1.1.1
65026 * Copyright(c) 2006-2007, Ext JS, LLC.
65028 * Originally Released Under LGPL - original licence link has changed is not relivant.
65031 * <script type="text/javascript">
65034 * @extends Roo.grid.AbstractSelectionModel
65035 * @class Roo.grid.RowSelectionModel
65036 * The default SelectionModel used by {@link Roo.grid.Grid}.
65037 * It supports multiple selections and keyboard selection/navigation.
65039 * @param {Object} config
65041 Roo.grid.RowSelectionModel = function(config){
65042 Roo.apply(this, config);
65043 this.selections = new Roo.util.MixedCollection(false, function(o){
65048 this.lastActive = false;
65052 * @event selectionchange
65053 * Fires when the selection changes
65054 * @param {SelectionModel} this
65056 "selectionchange" : true,
65058 * @event afterselectionchange
65059 * Fires after the selection changes (eg. by key press or clicking)
65060 * @param {SelectionModel} this
65062 "afterselectionchange" : true,
65064 * @event beforerowselect
65065 * Fires when a row is selected being selected, return false to cancel.
65066 * @param {SelectionModel} this
65067 * @param {Number} rowIndex The selected index
65068 * @param {Boolean} keepExisting False if other selections will be cleared
65070 "beforerowselect" : true,
65073 * Fires when a row is selected.
65074 * @param {SelectionModel} this
65075 * @param {Number} rowIndex The selected index
65076 * @param {Roo.data.Record} r The record
65078 "rowselect" : true,
65080 * @event rowdeselect
65081 * Fires when a row is deselected.
65082 * @param {SelectionModel} this
65083 * @param {Number} rowIndex The selected index
65085 "rowdeselect" : true
65087 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65088 this.locked = false;
65091 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
65093 * @cfg {Boolean} singleSelect
65094 * True to allow selection of only one row at a time (defaults to false)
65096 singleSelect : false,
65099 initEvents : function(){
65101 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65102 this.grid.on("mousedown", this.handleMouseDown, this);
65103 }else{ // allow click to work like normal
65104 this.grid.on("rowclick", this.handleDragableRowClick, this);
65106 // bootstrap does not have a view..
65107 var view = this.grid.view ? this.grid.view : this.grid;
65108 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65109 "up" : function(e){
65111 this.selectPrevious(e.shiftKey);
65112 }else if(this.last !== false && this.lastActive !== false){
65113 var last = this.last;
65114 this.selectRange(this.last, this.lastActive-1);
65115 view.focusRow(this.lastActive);
65116 if(last !== false){
65120 this.selectFirstRow();
65122 this.fireEvent("afterselectionchange", this);
65124 "down" : function(e){
65126 this.selectNext(e.shiftKey);
65127 }else if(this.last !== false && this.lastActive !== false){
65128 var last = this.last;
65129 this.selectRange(this.last, this.lastActive+1);
65130 view.focusRow(this.lastActive);
65131 if(last !== false){
65135 this.selectFirstRow();
65137 this.fireEvent("afterselectionchange", this);
65143 view.on("refresh", this.onRefresh, this);
65144 view.on("rowupdated", this.onRowUpdated, this);
65145 view.on("rowremoved", this.onRemove, this);
65149 onRefresh : function(){
65150 var ds = this.grid.ds, i, v = this.grid.view;
65151 var s = this.selections;
65152 s.each(function(r){
65153 if((i = ds.indexOfId(r.id)) != -1){
65155 s.add(ds.getAt(i)); // updating the selection relate data
65163 onRemove : function(v, index, r){
65164 this.selections.remove(r);
65168 onRowUpdated : function(v, index, r){
65169 if(this.isSelected(r)){
65170 v.onRowSelect(index);
65176 * @param {Array} records The records to select
65177 * @param {Boolean} keepExisting (optional) True to keep existing selections
65179 selectRecords : function(records, keepExisting){
65181 this.clearSelections();
65183 var ds = this.grid.ds;
65184 for(var i = 0, len = records.length; i < len; i++){
65185 this.selectRow(ds.indexOf(records[i]), true);
65190 * Gets the number of selected rows.
65193 getCount : function(){
65194 return this.selections.length;
65198 * Selects the first row in the grid.
65200 selectFirstRow : function(){
65205 * Select the last row.
65206 * @param {Boolean} keepExisting (optional) True to keep existing selections
65208 selectLastRow : function(keepExisting){
65209 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65213 * Selects the row immediately following the last selected row.
65214 * @param {Boolean} keepExisting (optional) True to keep existing selections
65216 selectNext : function(keepExisting){
65217 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65218 this.selectRow(this.last+1, keepExisting);
65219 var view = this.grid.view ? this.grid.view : this.grid;
65220 view.focusRow(this.last);
65225 * Selects the row that precedes the last selected row.
65226 * @param {Boolean} keepExisting (optional) True to keep existing selections
65228 selectPrevious : function(keepExisting){
65230 this.selectRow(this.last-1, keepExisting);
65231 var view = this.grid.view ? this.grid.view : this.grid;
65232 view.focusRow(this.last);
65237 * Returns the selected records
65238 * @return {Array} Array of selected records
65240 getSelections : function(){
65241 return [].concat(this.selections.items);
65245 * Returns the first selected record.
65248 getSelected : function(){
65249 return this.selections.itemAt(0);
65254 * Clears all selections.
65256 clearSelections : function(fast){
65261 var ds = this.grid.ds;
65262 var s = this.selections;
65263 s.each(function(r){
65264 this.deselectRow(ds.indexOfId(r.id));
65268 this.selections.clear();
65275 * Selects all rows.
65277 selectAll : function(){
65281 this.selections.clear();
65282 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65283 this.selectRow(i, true);
65288 * Returns True if there is a selection.
65289 * @return {Boolean}
65291 hasSelection : function(){
65292 return this.selections.length > 0;
65296 * Returns True if the specified row is selected.
65297 * @param {Number/Record} record The record or index of the record to check
65298 * @return {Boolean}
65300 isSelected : function(index){
65301 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65302 return (r && this.selections.key(r.id) ? true : false);
65306 * Returns True if the specified record id is selected.
65307 * @param {String} id The id of record to check
65308 * @return {Boolean}
65310 isIdSelected : function(id){
65311 return (this.selections.key(id) ? true : false);
65315 handleMouseDown : function(e, t)
65317 var view = this.grid.view ? this.grid.view : this.grid;
65319 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65322 if(e.shiftKey && this.last !== false){
65323 var last = this.last;
65324 this.selectRange(last, rowIndex, e.ctrlKey);
65325 this.last = last; // reset the last
65326 view.focusRow(rowIndex);
65328 var isSelected = this.isSelected(rowIndex);
65329 if(e.button !== 0 && isSelected){
65330 view.focusRow(rowIndex);
65331 }else if(e.ctrlKey && isSelected){
65332 this.deselectRow(rowIndex);
65333 }else if(!isSelected){
65334 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65335 view.focusRow(rowIndex);
65338 this.fireEvent("afterselectionchange", this);
65341 handleDragableRowClick : function(grid, rowIndex, e)
65343 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65344 this.selectRow(rowIndex, false);
65345 var view = this.grid.view ? this.grid.view : this.grid;
65346 view.focusRow(rowIndex);
65347 this.fireEvent("afterselectionchange", this);
65352 * Selects multiple rows.
65353 * @param {Array} rows Array of the indexes of the row to select
65354 * @param {Boolean} keepExisting (optional) True to keep existing selections
65356 selectRows : function(rows, keepExisting){
65358 this.clearSelections();
65360 for(var i = 0, len = rows.length; i < len; i++){
65361 this.selectRow(rows[i], true);
65366 * Selects a range of rows. All rows in between startRow and endRow are also selected.
65367 * @param {Number} startRow The index of the first row in the range
65368 * @param {Number} endRow The index of the last row in the range
65369 * @param {Boolean} keepExisting (optional) True to retain existing selections
65371 selectRange : function(startRow, endRow, keepExisting){
65376 this.clearSelections();
65378 if(startRow <= endRow){
65379 for(var i = startRow; i <= endRow; i++){
65380 this.selectRow(i, true);
65383 for(var i = startRow; i >= endRow; i--){
65384 this.selectRow(i, true);
65390 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65391 * @param {Number} startRow The index of the first row in the range
65392 * @param {Number} endRow The index of the last row in the range
65394 deselectRange : function(startRow, endRow, preventViewNotify){
65398 for(var i = startRow; i <= endRow; i++){
65399 this.deselectRow(i, preventViewNotify);
65405 * @param {Number} row The index of the row to select
65406 * @param {Boolean} keepExisting (optional) True to keep existing selections
65408 selectRow : function(index, keepExisting, preventViewNotify){
65409 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65412 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65413 if(!keepExisting || this.singleSelect){
65414 this.clearSelections();
65416 var r = this.grid.ds.getAt(index);
65417 this.selections.add(r);
65418 this.last = this.lastActive = index;
65419 if(!preventViewNotify){
65420 var view = this.grid.view ? this.grid.view : this.grid;
65421 view.onRowSelect(index);
65423 this.fireEvent("rowselect", this, index, r);
65424 this.fireEvent("selectionchange", this);
65430 * @param {Number} row The index of the row to deselect
65432 deselectRow : function(index, preventViewNotify){
65436 if(this.last == index){
65439 if(this.lastActive == index){
65440 this.lastActive = false;
65442 var r = this.grid.ds.getAt(index);
65443 this.selections.remove(r);
65444 if(!preventViewNotify){
65445 var view = this.grid.view ? this.grid.view : this.grid;
65446 view.onRowDeselect(index);
65448 this.fireEvent("rowdeselect", this, index);
65449 this.fireEvent("selectionchange", this);
65453 restoreLast : function(){
65455 this.last = this._last;
65460 acceptsNav : function(row, col, cm){
65461 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65465 onEditorKey : function(field, e){
65466 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65471 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65473 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65475 }else if(k == e.ENTER && !e.ctrlKey){
65479 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65481 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65483 }else if(k == e.ESC){
65487 g.startEditing(newCell[0], newCell[1]);
65492 * Ext JS Library 1.1.1
65493 * Copyright(c) 2006-2007, Ext JS, LLC.
65495 * Originally Released Under LGPL - original licence link has changed is not relivant.
65498 * <script type="text/javascript">
65501 * @class Roo.grid.CellSelectionModel
65502 * @extends Roo.grid.AbstractSelectionModel
65503 * This class provides the basic implementation for cell selection in a grid.
65505 * @param {Object} config The object containing the configuration of this model.
65506 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65508 Roo.grid.CellSelectionModel = function(config){
65509 Roo.apply(this, config);
65511 this.selection = null;
65515 * @event beforerowselect
65516 * Fires before a cell is selected.
65517 * @param {SelectionModel} this
65518 * @param {Number} rowIndex The selected row index
65519 * @param {Number} colIndex The selected cell index
65521 "beforecellselect" : true,
65523 * @event cellselect
65524 * Fires when a cell is selected.
65525 * @param {SelectionModel} this
65526 * @param {Number} rowIndex The selected row index
65527 * @param {Number} colIndex The selected cell index
65529 "cellselect" : true,
65531 * @event selectionchange
65532 * Fires when the active selection changes.
65533 * @param {SelectionModel} this
65534 * @param {Object} selection null for no selection or an object (o) with two properties
65536 <li>o.record: the record object for the row the selection is in</li>
65537 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65540 "selectionchange" : true,
65543 * Fires when the tab (or enter) was pressed on the last editable cell
65544 * You can use this to trigger add new row.
65545 * @param {SelectionModel} this
65549 * @event beforeeditnext
65550 * Fires before the next editable sell is made active
65551 * You can use this to skip to another cell or fire the tabend
65552 * if you set cell to false
65553 * @param {Object} eventdata object : { cell : [ row, col ] }
65555 "beforeeditnext" : true
65557 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65560 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
65562 enter_is_tab: false,
65565 initEvents : function(){
65566 this.grid.on("mousedown", this.handleMouseDown, this);
65567 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65568 var view = this.grid.view;
65569 view.on("refresh", this.onViewChange, this);
65570 view.on("rowupdated", this.onRowUpdated, this);
65571 view.on("beforerowremoved", this.clearSelections, this);
65572 view.on("beforerowsinserted", this.clearSelections, this);
65573 if(this.grid.isEditor){
65574 this.grid.on("beforeedit", this.beforeEdit, this);
65579 beforeEdit : function(e){
65580 this.select(e.row, e.column, false, true, e.record);
65584 onRowUpdated : function(v, index, r){
65585 if(this.selection && this.selection.record == r){
65586 v.onCellSelect(index, this.selection.cell[1]);
65591 onViewChange : function(){
65592 this.clearSelections(true);
65596 * Returns the currently selected cell,.
65597 * @return {Array} The selected cell (row, column) or null if none selected.
65599 getSelectedCell : function(){
65600 return this.selection ? this.selection.cell : null;
65604 * Clears all selections.
65605 * @param {Boolean} true to prevent the gridview from being notified about the change.
65607 clearSelections : function(preventNotify){
65608 var s = this.selection;
65610 if(preventNotify !== true){
65611 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65613 this.selection = null;
65614 this.fireEvent("selectionchange", this, null);
65619 * Returns true if there is a selection.
65620 * @return {Boolean}
65622 hasSelection : function(){
65623 return this.selection ? true : false;
65627 handleMouseDown : function(e, t){
65628 var v = this.grid.getView();
65629 if(this.isLocked()){
65632 var row = v.findRowIndex(t);
65633 var cell = v.findCellIndex(t);
65634 if(row !== false && cell !== false){
65635 this.select(row, cell);
65641 * @param {Number} rowIndex
65642 * @param {Number} collIndex
65644 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65645 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65646 this.clearSelections();
65647 r = r || this.grid.dataSource.getAt(rowIndex);
65650 cell : [rowIndex, colIndex]
65652 if(!preventViewNotify){
65653 var v = this.grid.getView();
65654 v.onCellSelect(rowIndex, colIndex);
65655 if(preventFocus !== true){
65656 v.focusCell(rowIndex, colIndex);
65659 this.fireEvent("cellselect", this, rowIndex, colIndex);
65660 this.fireEvent("selectionchange", this, this.selection);
65665 isSelectable : function(rowIndex, colIndex, cm){
65666 return !cm.isHidden(colIndex);
65670 handleKeyDown : function(e){
65671 //Roo.log('Cell Sel Model handleKeyDown');
65672 if(!e.isNavKeyPress()){
65675 var g = this.grid, s = this.selection;
65678 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
65680 this.select(cell[0], cell[1]);
65685 var walk = function(row, col, step){
65686 return g.walkCells(row, col, step, sm.isSelectable, sm);
65688 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65695 // handled by onEditorKey
65696 if (g.isEditor && g.editing) {
65700 newCell = walk(r, c-1, -1);
65702 newCell = walk(r, c+1, 1);
65707 newCell = walk(r+1, c, 1);
65711 newCell = walk(r-1, c, -1);
65715 newCell = walk(r, c+1, 1);
65719 newCell = walk(r, c-1, -1);
65724 if(g.isEditor && !g.editing){
65725 g.startEditing(r, c);
65734 this.select(newCell[0], newCell[1]);
65740 acceptsNav : function(row, col, cm){
65741 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65745 * @param {Number} field (not used) - as it's normally used as a listener
65746 * @param {Number} e - event - fake it by using
65748 * var e = Roo.EventObjectImpl.prototype;
65749 * e.keyCode = e.TAB
65753 onEditorKey : function(field, e){
65755 var k = e.getKey(),
65758 ed = g.activeEditor,
65760 ///Roo.log('onEditorKey' + k);
65763 if (this.enter_is_tab && k == e.ENTER) {
65769 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65771 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65777 } else if(k == e.ENTER && !e.ctrlKey){
65780 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65782 } else if(k == e.ESC){
65787 var ecall = { cell : newCell, forward : forward };
65788 this.fireEvent('beforeeditnext', ecall );
65789 newCell = ecall.cell;
65790 forward = ecall.forward;
65794 //Roo.log('next cell after edit');
65795 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
65796 } else if (forward) {
65797 // tabbed past last
65798 this.fireEvent.defer(100, this, ['tabend',this]);
65803 * Ext JS Library 1.1.1
65804 * Copyright(c) 2006-2007, Ext JS, LLC.
65806 * Originally Released Under LGPL - original licence link has changed is not relivant.
65809 * <script type="text/javascript">
65813 * @class Roo.grid.EditorGrid
65814 * @extends Roo.grid.Grid
65815 * Class for creating and editable grid.
65816 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
65817 * The container MUST have some type of size defined for the grid to fill. The container will be
65818 * automatically set to position relative if it isn't already.
65819 * @param {Object} dataSource The data model to bind to
65820 * @param {Object} colModel The column model with info about this grid's columns
65822 Roo.grid.EditorGrid = function(container, config){
65823 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
65824 this.getGridEl().addClass("xedit-grid");
65826 if(!this.selModel){
65827 this.selModel = new Roo.grid.CellSelectionModel();
65830 this.activeEditor = null;
65834 * @event beforeedit
65835 * Fires before cell editing is triggered. The edit event object has the following properties <br />
65836 * <ul style="padding:5px;padding-left:16px;">
65837 * <li>grid - This grid</li>
65838 * <li>record - The record being edited</li>
65839 * <li>field - The field name being edited</li>
65840 * <li>value - The value for the field being edited.</li>
65841 * <li>row - The grid row index</li>
65842 * <li>column - The grid column index</li>
65843 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65845 * @param {Object} e An edit event (see above for description)
65847 "beforeedit" : true,
65850 * Fires after a cell is edited. <br />
65851 * <ul style="padding:5px;padding-left:16px;">
65852 * <li>grid - This grid</li>
65853 * <li>record - The record being edited</li>
65854 * <li>field - The field name being edited</li>
65855 * <li>value - The value being set</li>
65856 * <li>originalValue - The original value for the field, before the edit.</li>
65857 * <li>row - The grid row index</li>
65858 * <li>column - The grid column index</li>
65860 * @param {Object} e An edit event (see above for description)
65862 "afteredit" : true,
65864 * @event validateedit
65865 * Fires after a cell is edited, but before the value is set in the record.
65866 * You can use this to modify the value being set in the field, Return false
65867 * to cancel the change. The edit event object has the following properties <br />
65868 * <ul style="padding:5px;padding-left:16px;">
65869 * <li>editor - This editor</li>
65870 * <li>grid - This grid</li>
65871 * <li>record - The record being edited</li>
65872 * <li>field - The field name being edited</li>
65873 * <li>value - The value being set</li>
65874 * <li>originalValue - The original value for the field, before the edit.</li>
65875 * <li>row - The grid row index</li>
65876 * <li>column - The grid column index</li>
65877 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65879 * @param {Object} e An edit event (see above for description)
65881 "validateedit" : true
65883 this.on("bodyscroll", this.stopEditing, this);
65884 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
65887 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
65889 * @cfg {Number} clicksToEdit
65890 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
65897 trackMouseOver: false, // causes very odd FF errors
65899 onCellDblClick : function(g, row, col){
65900 this.startEditing(row, col);
65903 onEditComplete : function(ed, value, startValue){
65904 this.editing = false;
65905 this.activeEditor = null;
65906 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
65908 var field = this.colModel.getDataIndex(ed.col);
65913 originalValue: startValue,
65920 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
65923 if(String(value) !== String(startValue)){
65925 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
65926 r.set(field, e.value);
65927 // if we are dealing with a combo box..
65928 // then we also set the 'name' colum to be the displayField
65929 if (ed.field.displayField && ed.field.name) {
65930 r.set(ed.field.name, ed.field.el.dom.value);
65933 delete e.cancel; //?? why!!!
65934 this.fireEvent("afteredit", e);
65937 this.fireEvent("afteredit", e); // always fire it!
65939 this.view.focusCell(ed.row, ed.col);
65943 * Starts editing the specified for the specified row/column
65944 * @param {Number} rowIndex
65945 * @param {Number} colIndex
65947 startEditing : function(row, col){
65948 this.stopEditing();
65949 if(this.colModel.isCellEditable(col, row)){
65950 this.view.ensureVisible(row, col, true);
65952 var r = this.dataSource.getAt(row);
65953 var field = this.colModel.getDataIndex(col);
65954 var cell = Roo.get(this.view.getCell(row,col));
65959 value: r.data[field],
65964 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
65965 this.editing = true;
65966 var ed = this.colModel.getCellEditor(col, row);
65972 ed.render(ed.parentEl || document.body);
65978 (function(){ // complex but required for focus issues in safari, ie and opera
65982 ed.on("complete", this.onEditComplete, this, {single: true});
65983 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
65984 this.activeEditor = ed;
65985 var v = r.data[field];
65986 ed.startEdit(this.view.getCell(row, col), v);
65987 // combo's with 'displayField and name set
65988 if (ed.field.displayField && ed.field.name) {
65989 ed.field.el.dom.value = r.data[ed.field.name];
65993 }).defer(50, this);
65999 * Stops any active editing
66001 stopEditing : function(){
66002 if(this.activeEditor){
66003 this.activeEditor.completeEdit();
66005 this.activeEditor = null;
66009 * Called to get grid's drag proxy text, by default returns this.ddText.
66012 getDragDropText : function(){
66013 var count = this.selModel.getSelectedCell() ? 1 : 0;
66014 return String.format(this.ddText, count, count == 1 ? '' : 's');
66019 * Ext JS Library 1.1.1
66020 * Copyright(c) 2006-2007, Ext JS, LLC.
66022 * Originally Released Under LGPL - original licence link has changed is not relivant.
66025 * <script type="text/javascript">
66028 // private - not really -- you end up using it !
66029 // This is a support class used internally by the Grid components
66032 * @class Roo.grid.GridEditor
66033 * @extends Roo.Editor
66034 * Class for creating and editable grid elements.
66035 * @param {Object} config any settings (must include field)
66037 Roo.grid.GridEditor = function(field, config){
66038 if (!config && field.field) {
66040 field = Roo.factory(config.field, Roo.form);
66042 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66043 field.monitorTab = false;
66046 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66049 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66052 alignment: "tl-tl",
66055 cls: "x-small-editor x-grid-editor",
66060 * Ext JS Library 1.1.1
66061 * Copyright(c) 2006-2007, Ext JS, LLC.
66063 * Originally Released Under LGPL - original licence link has changed is not relivant.
66066 * <script type="text/javascript">
66071 Roo.grid.PropertyRecord = Roo.data.Record.create([
66072 {name:'name',type:'string'}, 'value'
66076 Roo.grid.PropertyStore = function(grid, source){
66078 this.store = new Roo.data.Store({
66079 recordType : Roo.grid.PropertyRecord
66081 this.store.on('update', this.onUpdate, this);
66083 this.setSource(source);
66085 Roo.grid.PropertyStore.superclass.constructor.call(this);
66090 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66091 setSource : function(o){
66093 this.store.removeAll();
66096 if(this.isEditableValue(o[k])){
66097 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66100 this.store.loadRecords({records: data}, {}, true);
66103 onUpdate : function(ds, record, type){
66104 if(type == Roo.data.Record.EDIT){
66105 var v = record.data['value'];
66106 var oldValue = record.modified['value'];
66107 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66108 this.source[record.id] = v;
66110 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66117 getProperty : function(row){
66118 return this.store.getAt(row);
66121 isEditableValue: function(val){
66122 if(val && val instanceof Date){
66124 }else if(typeof val == 'object' || typeof val == 'function'){
66130 setValue : function(prop, value){
66131 this.source[prop] = value;
66132 this.store.getById(prop).set('value', value);
66135 getSource : function(){
66136 return this.source;
66140 Roo.grid.PropertyColumnModel = function(grid, store){
66143 g.PropertyColumnModel.superclass.constructor.call(this, [
66144 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66145 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66147 this.store = store;
66148 this.bselect = Roo.DomHelper.append(document.body, {
66149 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66150 {tag: 'option', value: 'true', html: 'true'},
66151 {tag: 'option', value: 'false', html: 'false'}
66154 Roo.id(this.bselect);
66157 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66158 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66159 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66160 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66161 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66163 this.renderCellDelegate = this.renderCell.createDelegate(this);
66164 this.renderPropDelegate = this.renderProp.createDelegate(this);
66167 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66171 valueText : 'Value',
66173 dateFormat : 'm/j/Y',
66176 renderDate : function(dateVal){
66177 return dateVal.dateFormat(this.dateFormat);
66180 renderBool : function(bVal){
66181 return bVal ? 'true' : 'false';
66184 isCellEditable : function(colIndex, rowIndex){
66185 return colIndex == 1;
66188 getRenderer : function(col){
66190 this.renderCellDelegate : this.renderPropDelegate;
66193 renderProp : function(v){
66194 return this.getPropertyName(v);
66197 renderCell : function(val){
66199 if(val instanceof Date){
66200 rv = this.renderDate(val);
66201 }else if(typeof val == 'boolean'){
66202 rv = this.renderBool(val);
66204 return Roo.util.Format.htmlEncode(rv);
66207 getPropertyName : function(name){
66208 var pn = this.grid.propertyNames;
66209 return pn && pn[name] ? pn[name] : name;
66212 getCellEditor : function(colIndex, rowIndex){
66213 var p = this.store.getProperty(rowIndex);
66214 var n = p.data['name'], val = p.data['value'];
66216 if(typeof(this.grid.customEditors[n]) == 'string'){
66217 return this.editors[this.grid.customEditors[n]];
66219 if(typeof(this.grid.customEditors[n]) != 'undefined'){
66220 return this.grid.customEditors[n];
66222 if(val instanceof Date){
66223 return this.editors['date'];
66224 }else if(typeof val == 'number'){
66225 return this.editors['number'];
66226 }else if(typeof val == 'boolean'){
66227 return this.editors['boolean'];
66229 return this.editors['string'];
66235 * @class Roo.grid.PropertyGrid
66236 * @extends Roo.grid.EditorGrid
66237 * This class represents the interface of a component based property grid control.
66238 * <br><br>Usage:<pre><code>
66239 var grid = new Roo.grid.PropertyGrid("my-container-id", {
66247 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66248 * The container MUST have some type of size defined for the grid to fill. The container will be
66249 * automatically set to position relative if it isn't already.
66250 * @param {Object} config A config object that sets properties on this grid.
66252 Roo.grid.PropertyGrid = function(container, config){
66253 config = config || {};
66254 var store = new Roo.grid.PropertyStore(this);
66255 this.store = store;
66256 var cm = new Roo.grid.PropertyColumnModel(this, store);
66257 store.store.sort('name', 'ASC');
66258 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66261 enableColLock:false,
66262 enableColumnMove:false,
66264 trackMouseOver: false,
66267 this.getGridEl().addClass('x-props-grid');
66268 this.lastEditRow = null;
66269 this.on('columnresize', this.onColumnResize, this);
66272 * @event beforepropertychange
66273 * Fires before a property changes (return false to stop?)
66274 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66275 * @param {String} id Record Id
66276 * @param {String} newval New Value
66277 * @param {String} oldval Old Value
66279 "beforepropertychange": true,
66281 * @event propertychange
66282 * Fires after a property changes
66283 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66284 * @param {String} id Record Id
66285 * @param {String} newval New Value
66286 * @param {String} oldval Old Value
66288 "propertychange": true
66290 this.customEditors = this.customEditors || {};
66292 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66295 * @cfg {Object} customEditors map of colnames=> custom editors.
66296 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66297 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66298 * false disables editing of the field.
66302 * @cfg {Object} propertyNames map of property Names to their displayed value
66305 render : function(){
66306 Roo.grid.PropertyGrid.superclass.render.call(this);
66307 this.autoSize.defer(100, this);
66310 autoSize : function(){
66311 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66313 this.view.fitColumns();
66317 onColumnResize : function(){
66318 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66322 * Sets the data for the Grid
66323 * accepts a Key => Value object of all the elements avaiable.
66324 * @param {Object} data to appear in grid.
66326 setSource : function(source){
66327 this.store.setSource(source);
66331 * Gets all the data from the grid.
66332 * @return {Object} data data stored in grid
66334 getSource : function(){
66335 return this.store.getSource();
66344 * @class Roo.grid.Calendar
66345 * @extends Roo.grid.Grid
66346 * This class extends the Grid to provide a calendar widget
66347 * <br><br>Usage:<pre><code>
66348 var grid = new Roo.grid.Calendar("my-container-id", {
66351 selModel: mySelectionModel,
66352 autoSizeColumns: true,
66353 monitorWindowResize: false,
66354 trackMouseOver: true
66355 eventstore : real data store..
66361 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66362 * The container MUST have some type of size defined for the grid to fill. The container will be
66363 * automatically set to position relative if it isn't already.
66364 * @param {Object} config A config object that sets properties on this grid.
66366 Roo.grid.Calendar = function(container, config){
66367 // initialize the container
66368 this.container = Roo.get(container);
66369 this.container.update("");
66370 this.container.setStyle("overflow", "hidden");
66371 this.container.addClass('x-grid-container');
66373 this.id = this.container.id;
66375 Roo.apply(this, config);
66376 // check and correct shorthanded configs
66380 for (var r = 0;r < 6;r++) {
66383 for (var c =0;c < 7;c++) {
66387 if (this.eventStore) {
66388 this.eventStore= Roo.factory(this.eventStore, Roo.data);
66389 this.eventStore.on('load',this.onLoad, this);
66390 this.eventStore.on('beforeload',this.clearEvents, this);
66394 this.dataSource = new Roo.data.Store({
66395 proxy: new Roo.data.MemoryProxy(rows),
66396 reader: new Roo.data.ArrayReader({}, [
66397 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66400 this.dataSource.load();
66401 this.ds = this.dataSource;
66402 this.ds.xmodule = this.xmodule || false;
66405 var cellRender = function(v,x,r)
66407 return String.format(
66408 '<div class="fc-day fc-widget-content"><div>' +
66409 '<div class="fc-event-container"></div>' +
66410 '<div class="fc-day-number">{0}</div>'+
66412 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66413 '</div></div>', v);
66418 this.colModel = new Roo.grid.ColumnModel( [
66420 xtype: 'ColumnModel',
66422 dataIndex : 'weekday0',
66424 renderer : cellRender
66427 xtype: 'ColumnModel',
66429 dataIndex : 'weekday1',
66431 renderer : cellRender
66434 xtype: 'ColumnModel',
66436 dataIndex : 'weekday2',
66437 header : 'Tuesday',
66438 renderer : cellRender
66441 xtype: 'ColumnModel',
66443 dataIndex : 'weekday3',
66444 header : 'Wednesday',
66445 renderer : cellRender
66448 xtype: 'ColumnModel',
66450 dataIndex : 'weekday4',
66451 header : 'Thursday',
66452 renderer : cellRender
66455 xtype: 'ColumnModel',
66457 dataIndex : 'weekday5',
66459 renderer : cellRender
66462 xtype: 'ColumnModel',
66464 dataIndex : 'weekday6',
66465 header : 'Saturday',
66466 renderer : cellRender
66469 this.cm = this.colModel;
66470 this.cm.xmodule = this.xmodule || false;
66474 //this.selModel = new Roo.grid.CellSelectionModel();
66475 //this.sm = this.selModel;
66476 //this.selModel.init(this);
66480 this.container.setWidth(this.width);
66484 this.container.setHeight(this.height);
66491 * The raw click event for the entire grid.
66492 * @param {Roo.EventObject} e
66497 * The raw dblclick event for the entire grid.
66498 * @param {Roo.EventObject} e
66502 * @event contextmenu
66503 * The raw contextmenu event for the entire grid.
66504 * @param {Roo.EventObject} e
66506 "contextmenu" : true,
66509 * The raw mousedown event for the entire grid.
66510 * @param {Roo.EventObject} e
66512 "mousedown" : true,
66515 * The raw mouseup event for the entire grid.
66516 * @param {Roo.EventObject} e
66521 * The raw mouseover event for the entire grid.
66522 * @param {Roo.EventObject} e
66524 "mouseover" : true,
66527 * The raw mouseout event for the entire grid.
66528 * @param {Roo.EventObject} e
66533 * The raw keypress event for the entire grid.
66534 * @param {Roo.EventObject} e
66539 * The raw keydown event for the entire grid.
66540 * @param {Roo.EventObject} e
66548 * Fires when a cell is clicked
66549 * @param {Grid} this
66550 * @param {Number} rowIndex
66551 * @param {Number} columnIndex
66552 * @param {Roo.EventObject} e
66554 "cellclick" : true,
66556 * @event celldblclick
66557 * Fires when a cell is double clicked
66558 * @param {Grid} this
66559 * @param {Number} rowIndex
66560 * @param {Number} columnIndex
66561 * @param {Roo.EventObject} e
66563 "celldblclick" : true,
66566 * Fires when a row is clicked
66567 * @param {Grid} this
66568 * @param {Number} rowIndex
66569 * @param {Roo.EventObject} e
66573 * @event rowdblclick
66574 * Fires when a row is double clicked
66575 * @param {Grid} this
66576 * @param {Number} rowIndex
66577 * @param {Roo.EventObject} e
66579 "rowdblclick" : true,
66581 * @event headerclick
66582 * Fires when a header is clicked
66583 * @param {Grid} this
66584 * @param {Number} columnIndex
66585 * @param {Roo.EventObject} e
66587 "headerclick" : true,
66589 * @event headerdblclick
66590 * Fires when a header cell is double clicked
66591 * @param {Grid} this
66592 * @param {Number} columnIndex
66593 * @param {Roo.EventObject} e
66595 "headerdblclick" : true,
66597 * @event rowcontextmenu
66598 * Fires when a row is right clicked
66599 * @param {Grid} this
66600 * @param {Number} rowIndex
66601 * @param {Roo.EventObject} e
66603 "rowcontextmenu" : true,
66605 * @event cellcontextmenu
66606 * Fires when a cell is right clicked
66607 * @param {Grid} this
66608 * @param {Number} rowIndex
66609 * @param {Number} cellIndex
66610 * @param {Roo.EventObject} e
66612 "cellcontextmenu" : true,
66614 * @event headercontextmenu
66615 * Fires when a header is right clicked
66616 * @param {Grid} this
66617 * @param {Number} columnIndex
66618 * @param {Roo.EventObject} e
66620 "headercontextmenu" : true,
66622 * @event bodyscroll
66623 * Fires when the body element is scrolled
66624 * @param {Number} scrollLeft
66625 * @param {Number} scrollTop
66627 "bodyscroll" : true,
66629 * @event columnresize
66630 * Fires when the user resizes a column
66631 * @param {Number} columnIndex
66632 * @param {Number} newSize
66634 "columnresize" : true,
66636 * @event columnmove
66637 * Fires when the user moves a column
66638 * @param {Number} oldIndex
66639 * @param {Number} newIndex
66641 "columnmove" : true,
66644 * Fires when row(s) start being dragged
66645 * @param {Grid} this
66646 * @param {Roo.GridDD} dd The drag drop object
66647 * @param {event} e The raw browser event
66649 "startdrag" : true,
66652 * Fires when a drag operation is complete
66653 * @param {Grid} this
66654 * @param {Roo.GridDD} dd The drag drop object
66655 * @param {event} e The raw browser event
66660 * Fires when dragged row(s) are dropped on a valid DD target
66661 * @param {Grid} this
66662 * @param {Roo.GridDD} dd The drag drop object
66663 * @param {String} targetId The target drag drop object
66664 * @param {event} e The raw browser event
66669 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66670 * @param {Grid} this
66671 * @param {Roo.GridDD} dd The drag drop object
66672 * @param {String} targetId The target drag drop object
66673 * @param {event} e The raw browser event
66678 * Fires when the dragged row(s) first cross another DD target while being dragged
66679 * @param {Grid} this
66680 * @param {Roo.GridDD} dd The drag drop object
66681 * @param {String} targetId The target drag drop object
66682 * @param {event} e The raw browser event
66684 "dragenter" : true,
66687 * Fires when the dragged row(s) leave another DD target while being dragged
66688 * @param {Grid} this
66689 * @param {Roo.GridDD} dd The drag drop object
66690 * @param {String} targetId The target drag drop object
66691 * @param {event} e The raw browser event
66696 * Fires when a row is rendered, so you can change add a style to it.
66697 * @param {GridView} gridview The grid view
66698 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
66704 * Fires when the grid is rendered
66705 * @param {Grid} grid
66710 * Fires when a date is selected
66711 * @param {DatePicker} this
66712 * @param {Date} date The selected date
66716 * @event monthchange
66717 * Fires when the displayed month changes
66718 * @param {DatePicker} this
66719 * @param {Date} date The selected month
66721 'monthchange': true,
66723 * @event evententer
66724 * Fires when mouse over an event
66725 * @param {Calendar} this
66726 * @param {event} Event
66728 'evententer': true,
66730 * @event eventleave
66731 * Fires when the mouse leaves an
66732 * @param {Calendar} this
66735 'eventleave': true,
66737 * @event eventclick
66738 * Fires when the mouse click an
66739 * @param {Calendar} this
66742 'eventclick': true,
66744 * @event eventrender
66745 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
66746 * @param {Calendar} this
66747 * @param {data} data to be modified
66749 'eventrender': true
66753 Roo.grid.Grid.superclass.constructor.call(this);
66754 this.on('render', function() {
66755 this.view.el.addClass('x-grid-cal');
66757 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
66761 if (!Roo.grid.Calendar.style) {
66762 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
66765 '.x-grid-cal .x-grid-col' : {
66766 height: 'auto !important',
66767 'vertical-align': 'top'
66769 '.x-grid-cal .fc-event-hori' : {
66780 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
66782 * @cfg {Store} eventStore The store that loads events.
66787 activeDate : false,
66790 monitorWindowResize : false,
66793 resizeColumns : function() {
66794 var col = (this.view.el.getWidth() / 7) - 3;
66795 // loop through cols, and setWidth
66796 for(var i =0 ; i < 7 ; i++){
66797 this.cm.setColumnWidth(i, col);
66800 setDate :function(date) {
66802 Roo.log('setDate?');
66804 this.resizeColumns();
66805 var vd = this.activeDate;
66806 this.activeDate = date;
66807 // if(vd && this.el){
66808 // var t = date.getTime();
66809 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
66810 // Roo.log('using add remove');
66812 // this.fireEvent('monthchange', this, date);
66814 // this.cells.removeClass("fc-state-highlight");
66815 // this.cells.each(function(c){
66816 // if(c.dateValue == t){
66817 // c.addClass("fc-state-highlight");
66818 // setTimeout(function(){
66819 // try{c.dom.firstChild.focus();}catch(e){}
66829 var days = date.getDaysInMonth();
66831 var firstOfMonth = date.getFirstDateOfMonth();
66832 var startingPos = firstOfMonth.getDay()-this.startDay;
66834 if(startingPos < this.startDay){
66838 var pm = date.add(Date.MONTH, -1);
66839 var prevStart = pm.getDaysInMonth()-startingPos;
66843 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66845 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
66846 //this.cells.addClassOnOver('fc-state-hover');
66848 var cells = this.cells.elements;
66849 var textEls = this.textNodes;
66851 //Roo.each(cells, function(cell){
66852 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
66855 days += startingPos;
66857 // convert everything to numbers so it's fast
66858 var day = 86400000;
66859 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
66862 //Roo.log(prevStart);
66864 var today = new Date().clearTime().getTime();
66865 var sel = date.clearTime().getTime();
66866 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
66867 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
66868 var ddMatch = this.disabledDatesRE;
66869 var ddText = this.disabledDatesText;
66870 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
66871 var ddaysText = this.disabledDaysText;
66872 var format = this.format;
66874 var setCellClass = function(cal, cell){
66876 //Roo.log('set Cell Class');
66878 var t = d.getTime();
66883 cell.dateValue = t;
66885 cell.className += " fc-today";
66886 cell.className += " fc-state-highlight";
66887 cell.title = cal.todayText;
66890 // disable highlight in other month..
66891 cell.className += " fc-state-highlight";
66896 //cell.className = " fc-state-disabled";
66897 cell.title = cal.minText;
66901 //cell.className = " fc-state-disabled";
66902 cell.title = cal.maxText;
66906 if(ddays.indexOf(d.getDay()) != -1){
66907 // cell.title = ddaysText;
66908 // cell.className = " fc-state-disabled";
66911 if(ddMatch && format){
66912 var fvalue = d.dateFormat(format);
66913 if(ddMatch.test(fvalue)){
66914 cell.title = ddText.replace("%0", fvalue);
66915 cell.className = " fc-state-disabled";
66919 if (!cell.initialClassName) {
66920 cell.initialClassName = cell.dom.className;
66923 cell.dom.className = cell.initialClassName + ' ' + cell.className;
66928 for(; i < startingPos; i++) {
66929 cells[i].dayName = (++prevStart);
66930 Roo.log(textEls[i]);
66931 d.setDate(d.getDate()+1);
66933 //cells[i].className = "fc-past fc-other-month";
66934 setCellClass(this, cells[i]);
66939 for(; i < days; i++){
66940 intDay = i - startingPos + 1;
66941 cells[i].dayName = (intDay);
66942 d.setDate(d.getDate()+1);
66944 cells[i].className = ''; // "x-date-active";
66945 setCellClass(this, cells[i]);
66949 for(; i < 42; i++) {
66950 //textEls[i].innerHTML = (++extraDays);
66952 d.setDate(d.getDate()+1);
66953 cells[i].dayName = (++extraDays);
66954 cells[i].className = "fc-future fc-other-month";
66955 setCellClass(this, cells[i]);
66958 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
66960 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
66962 // this will cause all the cells to mis
66965 for (var r = 0;r < 6;r++) {
66966 for (var c =0;c < 7;c++) {
66967 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
66971 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66972 for(i=0;i<cells.length;i++) {
66974 this.cells.elements[i].dayName = cells[i].dayName ;
66975 this.cells.elements[i].className = cells[i].className;
66976 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
66977 this.cells.elements[i].title = cells[i].title ;
66978 this.cells.elements[i].dateValue = cells[i].dateValue ;
66984 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
66985 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
66987 ////if(totalRows != 6){
66988 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
66989 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
66992 this.fireEvent('monthchange', this, date);
66997 * Returns the grid's SelectionModel.
66998 * @return {SelectionModel}
67000 getSelectionModel : function(){
67001 if(!this.selModel){
67002 this.selModel = new Roo.grid.CellSelectionModel();
67004 return this.selModel;
67008 this.eventStore.load()
67014 findCell : function(dt) {
67015 dt = dt.clearTime().getTime();
67017 this.cells.each(function(c){
67018 //Roo.log("check " +c.dateValue + '?=' + dt);
67019 if(c.dateValue == dt){
67029 findCells : function(rec) {
67030 var s = rec.data.start_dt.clone().clearTime().getTime();
67032 var e= rec.data.end_dt.clone().clearTime().getTime();
67035 this.cells.each(function(c){
67036 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67038 if(c.dateValue > e){
67041 if(c.dateValue < s){
67050 findBestRow: function(cells)
67054 for (var i =0 ; i < cells.length;i++) {
67055 ret = Math.max(cells[i].rows || 0,ret);
67062 addItem : function(rec)
67064 // look for vertical location slot in
67065 var cells = this.findCells(rec);
67067 rec.row = this.findBestRow(cells);
67069 // work out the location.
67073 for(var i =0; i < cells.length; i++) {
67081 if (crow.start.getY() == cells[i].getY()) {
67083 crow.end = cells[i];
67099 for (var i = 0; i < cells.length;i++) {
67100 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67107 clearEvents: function() {
67109 if (!this.eventStore.getCount()) {
67112 // reset number of rows in cells.
67113 Roo.each(this.cells.elements, function(c){
67117 this.eventStore.each(function(e) {
67118 this.clearEvent(e);
67123 clearEvent : function(ev)
67126 Roo.each(ev.els, function(el) {
67127 el.un('mouseenter' ,this.onEventEnter, this);
67128 el.un('mouseleave' ,this.onEventLeave, this);
67136 renderEvent : function(ev,ctr) {
67138 ctr = this.view.el.select('.fc-event-container',true).first();
67142 this.clearEvent(ev);
67148 var cells = ev.cells;
67149 var rows = ev.rows;
67150 this.fireEvent('eventrender', this, ev);
67152 for(var i =0; i < rows.length; i++) {
67156 cls += ' fc-event-start';
67158 if ((i+1) == rows.length) {
67159 cls += ' fc-event-end';
67162 //Roo.log(ev.data);
67163 // how many rows should it span..
67164 var cg = this.eventTmpl.append(ctr,Roo.apply({
67167 }, ev.data) , true);
67170 cg.on('mouseenter' ,this.onEventEnter, this, ev);
67171 cg.on('mouseleave' ,this.onEventLeave, this, ev);
67172 cg.on('click', this.onEventClick, this, ev);
67176 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67177 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67180 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
67181 cg.setWidth(ebox.right - sbox.x -2);
67185 renderEvents: function()
67187 // first make sure there is enough space..
67189 if (!this.eventTmpl) {
67190 this.eventTmpl = new Roo.Template(
67191 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
67192 '<div class="fc-event-inner">' +
67193 '<span class="fc-event-time">{time}</span>' +
67194 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67196 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
67204 this.cells.each(function(c) {
67205 //Roo.log(c.select('.fc-day-content div',true).first());
67206 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67209 var ctr = this.view.el.select('.fc-event-container',true).first();
67212 this.eventStore.each(function(ev){
67214 this.renderEvent(ev);
67218 this.view.layout();
67222 onEventEnter: function (e, el,event,d) {
67223 this.fireEvent('evententer', this, el, event);
67226 onEventLeave: function (e, el,event,d) {
67227 this.fireEvent('eventleave', this, el, event);
67230 onEventClick: function (e, el,event,d) {
67231 this.fireEvent('eventclick', this, el, event);
67234 onMonthChange: function () {
67238 onLoad: function () {
67240 //Roo.log('calendar onload');
67242 if(this.eventStore.getCount() > 0){
67246 this.eventStore.each(function(d){
67251 if (typeof(add.end_dt) == 'undefined') {
67252 Roo.log("Missing End time in calendar data: ");
67256 if (typeof(add.start_dt) == 'undefined') {
67257 Roo.log("Missing Start time in calendar data: ");
67261 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67262 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67263 add.id = add.id || d.id;
67264 add.title = add.title || '??';
67272 this.renderEvents();
67282 render : function ()
67286 if (!this.view.el.hasClass('course-timesheet')) {
67287 this.view.el.addClass('course-timesheet');
67289 if (this.tsStyle) {
67294 Roo.log(_this.grid.view.el.getWidth());
67297 this.tsStyle = Roo.util.CSS.createStyleSheet({
67298 '.course-timesheet .x-grid-row' : {
67301 '.x-grid-row td' : {
67302 'vertical-align' : 0
67304 '.course-edit-link' : {
67306 'text-overflow' : 'ellipsis',
67307 'overflow' : 'hidden',
67308 'white-space' : 'nowrap',
67309 'cursor' : 'pointer'
67314 '.de-act-sup-link' : {
67315 'color' : 'purple',
67316 'text-decoration' : 'line-through'
67320 'text-decoration' : 'line-through'
67322 '.course-timesheet .course-highlight' : {
67323 'border-top-style': 'dashed !important',
67324 'border-bottom-bottom': 'dashed !important'
67326 '.course-timesheet .course-item' : {
67327 'font-family' : 'tahoma, arial, helvetica',
67328 'font-size' : '11px',
67329 'overflow' : 'hidden',
67330 'padding-left' : '10px',
67331 'padding-right' : '10px',
67332 'padding-top' : '10px'
67340 monitorWindowResize : false,
67341 cellrenderer : function(v,x,r)
67346 xtype: 'CellSelectionModel',
67353 beforeload : function (_self, options)
67355 options.params = options.params || {};
67356 options.params._month = _this.monthField.getValue();
67357 options.params.limit = 9999;
67358 options.params['sort'] = 'when_dt';
67359 options.params['dir'] = 'ASC';
67360 this.proxy.loadResponse = this.loadResponse;
67362 //this.addColumns();
67364 load : function (_self, records, options)
67366 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67367 // if you click on the translation.. you can edit it...
67368 var el = Roo.get(this);
67369 var id = el.dom.getAttribute('data-id');
67370 var d = el.dom.getAttribute('data-date');
67371 var t = el.dom.getAttribute('data-time');
67372 //var id = this.child('span').dom.textContent;
67375 Pman.Dialog.CourseCalendar.show({
67379 productitem_active : id ? 1 : 0
67381 _this.grid.ds.load({});
67386 _this.panel.fireEvent('resize', [ '', '' ]);
67389 loadResponse : function(o, success, response){
67390 // this is overridden on before load..
67392 Roo.log("our code?");
67393 //Roo.log(success);
67394 //Roo.log(response)
67395 delete this.activeRequest;
67397 this.fireEvent("loadexception", this, o, response);
67398 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67403 result = o.reader.read(response);
67405 Roo.log("load exception?");
67406 this.fireEvent("loadexception", this, o, response, e);
67407 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67410 Roo.log("ready...");
67411 // loop through result.records;
67412 // and set this.tdate[date] = [] << array of records..
67414 Roo.each(result.records, function(r){
67416 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67417 _this.tdata[r.data.when_dt.format('j')] = [];
67419 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67422 //Roo.log(_this.tdata);
67424 result.records = [];
67425 result.totalRecords = 6;
67427 // let's generate some duumy records for the rows.
67428 //var st = _this.dateField.getValue();
67430 // work out monday..
67431 //st = st.add(Date.DAY, -1 * st.format('w'));
67433 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67435 var firstOfMonth = date.getFirstDayOfMonth();
67436 var days = date.getDaysInMonth();
67438 var firstAdded = false;
67439 for (var i = 0; i < result.totalRecords ; i++) {
67440 //var d= st.add(Date.DAY, i);
67443 for(var w = 0 ; w < 7 ; w++){
67444 if(!firstAdded && firstOfMonth != w){
67451 var dd = (d > 0 && d < 10) ? "0"+d : d;
67452 row['weekday'+w] = String.format(
67453 '<span style="font-size: 16px;"><b>{0}</b></span>'+
67454 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67456 date.format('Y-m-')+dd
67459 if(typeof(_this.tdata[d]) != 'undefined'){
67460 Roo.each(_this.tdata[d], function(r){
67464 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67465 if(r.parent_id*1>0){
67466 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67469 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67470 deactive = 'de-act-link';
67473 row['weekday'+w] += String.format(
67474 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67476 r.product_id_name, //1
67477 r.when_dt.format('h:ia'), //2
67487 // only do this if something added..
67489 result.records.push(_this.grid.dataSource.reader.newRow(row));
67493 // push it twice. (second one with an hour..
67497 this.fireEvent("load", this, o, o.request.arg);
67498 o.request.callback.call(o.request.scope, result, o.request.arg, true);
67500 sortInfo : {field: 'when_dt', direction : 'ASC' },
67502 xtype: 'HttpProxy',
67505 url : baseURL + '/Roo/Shop_course.php'
67508 xtype: 'JsonReader',
67525 'name': 'parent_id',
67529 'name': 'product_id',
67533 'name': 'productitem_id',
67551 click : function (_self, e)
67553 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67554 sd.setMonth(sd.getMonth()-1);
67555 _this.monthField.setValue(sd.format('Y-m-d'));
67556 _this.grid.ds.load({});
67562 xtype: 'Separator',
67566 xtype: 'MonthField',
67569 render : function (_self)
67571 _this.monthField = _self;
67572 // _this.monthField.set today
67574 select : function (combo, date)
67576 _this.grid.ds.load({});
67579 value : (function() { return new Date(); })()
67582 xtype: 'Separator',
67588 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67598 click : function (_self, e)
67600 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67601 sd.setMonth(sd.getMonth()+1);
67602 _this.monthField.setValue(sd.format('Y-m-d'));
67603 _this.grid.ds.load({});
67616 * Ext JS Library 1.1.1
67617 * Copyright(c) 2006-2007, Ext JS, LLC.
67619 * Originally Released Under LGPL - original licence link has changed is not relivant.
67622 * <script type="text/javascript">
67626 * @class Roo.LoadMask
67627 * A simple utility class for generically masking elements while loading data. If the element being masked has
67628 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67629 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
67630 * element's UpdateManager load indicator and will be destroyed after the initial load.
67632 * Create a new LoadMask
67633 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67634 * @param {Object} config The config object
67636 Roo.LoadMask = function(el, config){
67637 this.el = Roo.get(el);
67638 Roo.apply(this, config);
67640 this.store.on('beforeload', this.onBeforeLoad, this);
67641 this.store.on('load', this.onLoad, this);
67642 this.store.on('loadexception', this.onLoadException, this);
67643 this.removeMask = false;
67645 var um = this.el.getUpdateManager();
67646 um.showLoadIndicator = false; // disable the default indicator
67647 um.on('beforeupdate', this.onBeforeLoad, this);
67648 um.on('update', this.onLoad, this);
67649 um.on('failure', this.onLoad, this);
67650 this.removeMask = true;
67654 Roo.LoadMask.prototype = {
67656 * @cfg {Boolean} removeMask
67657 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67658 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
67660 removeMask : false,
67662 * @cfg {String} msg
67663 * The text to display in a centered loading message box (defaults to 'Loading...')
67665 msg : 'Loading...',
67667 * @cfg {String} msgCls
67668 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67670 msgCls : 'x-mask-loading',
67673 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67679 * Disables the mask to prevent it from being displayed
67681 disable : function(){
67682 this.disabled = true;
67686 * Enables the mask so that it can be displayed
67688 enable : function(){
67689 this.disabled = false;
67692 onLoadException : function()
67694 Roo.log(arguments);
67696 if (typeof(arguments[3]) != 'undefined') {
67697 Roo.MessageBox.alert("Error loading",arguments[3]);
67701 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67702 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67709 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67712 onLoad : function()
67714 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67718 onBeforeLoad : function(){
67719 if(!this.disabled){
67720 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67725 destroy : function(){
67727 this.store.un('beforeload', this.onBeforeLoad, this);
67728 this.store.un('load', this.onLoad, this);
67729 this.store.un('loadexception', this.onLoadException, this);
67731 var um = this.el.getUpdateManager();
67732 um.un('beforeupdate', this.onBeforeLoad, this);
67733 um.un('update', this.onLoad, this);
67734 um.un('failure', this.onLoad, this);
67739 * Ext JS Library 1.1.1
67740 * Copyright(c) 2006-2007, Ext JS, LLC.
67742 * Originally Released Under LGPL - original licence link has changed is not relivant.
67745 * <script type="text/javascript">
67750 * @class Roo.XTemplate
67751 * @extends Roo.Template
67752 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
67754 var t = new Roo.XTemplate(
67755 '<select name="{name}">',
67756 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
67760 // then append, applying the master template values
67763 * Supported features:
67768 {a_variable} - output encoded.
67769 {a_variable.format:("Y-m-d")} - call a method on the variable
67770 {a_variable:raw} - unencoded output
67771 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
67772 {a_variable:this.method_on_template(...)} - call a method on the template object.
67777 <tpl for="a_variable or condition.."></tpl>
67778 <tpl if="a_variable or condition"></tpl>
67779 <tpl exec="some javascript"></tpl>
67780 <tpl name="named_template"></tpl> (experimental)
67782 <tpl for="."></tpl> - just iterate the property..
67783 <tpl for=".."></tpl> - iterates with the parent (probably the template)
67787 Roo.XTemplate = function()
67789 Roo.XTemplate.superclass.constructor.apply(this, arguments);
67796 Roo.extend(Roo.XTemplate, Roo.Template, {
67799 * The various sub templates
67804 * basic tag replacing syntax
67807 * // you can fake an object call by doing this
67811 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
67814 * compile the template
67816 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
67819 compile: function()
67823 s = ['<tpl>', s, '</tpl>'].join('');
67825 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
67826 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
67827 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
67828 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
67829 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
67834 while(true == !!(m = s.match(re))){
67835 var forMatch = m[0].match(nameRe),
67836 ifMatch = m[0].match(ifRe),
67837 execMatch = m[0].match(execRe),
67838 namedMatch = m[0].match(namedRe),
67843 name = forMatch && forMatch[1] ? forMatch[1] : '';
67846 // if - puts fn into test..
67847 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
67849 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
67854 // exec - calls a function... returns empty if true is returned.
67855 exp = execMatch && execMatch[1] ? execMatch[1] : null;
67857 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
67865 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
67866 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
67867 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
67870 var uid = namedMatch ? namedMatch[1] : id;
67874 id: namedMatch ? namedMatch[1] : id,
67881 s = s.replace(m[0], '');
67883 s = s.replace(m[0], '{xtpl'+ id + '}');
67888 for(var i = tpls.length-1; i >= 0; --i){
67889 this.compileTpl(tpls[i]);
67890 this.tpls[tpls[i].id] = tpls[i];
67892 this.master = tpls[tpls.length-1];
67896 * same as applyTemplate, except it's done to one of the subTemplates
67897 * when using named templates, you can do:
67899 * var str = pl.applySubTemplate('your-name', values);
67902 * @param {Number} id of the template
67903 * @param {Object} values to apply to template
67904 * @param {Object} parent (normaly the instance of this object)
67906 applySubTemplate : function(id, values, parent)
67910 var t = this.tpls[id];
67914 if(t.test && !t.test.call(this, values, parent)){
67918 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
67919 Roo.log(e.toString());
67925 if(t.exec && t.exec.call(this, values, parent)){
67929 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
67930 Roo.log(e.toString());
67935 var vs = t.target ? t.target.call(this, values, parent) : values;
67936 parent = t.target ? values : parent;
67937 if(t.target && vs instanceof Array){
67939 for(var i = 0, len = vs.length; i < len; i++){
67940 buf[buf.length] = t.compiled.call(this, vs[i], parent);
67942 return buf.join('');
67944 return t.compiled.call(this, vs, parent);
67946 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
67947 Roo.log(e.toString());
67948 Roo.log(t.compiled);
67953 compileTpl : function(tpl)
67955 var fm = Roo.util.Format;
67956 var useF = this.disableFormats !== true;
67957 var sep = Roo.isGecko ? "+" : ",";
67958 var undef = function(str) {
67959 Roo.log("Property not found :" + str);
67963 var fn = function(m, name, format, args)
67965 //Roo.log(arguments);
67966 args = args ? args.replace(/\\'/g,"'") : args;
67967 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
67968 if (typeof(format) == 'undefined') {
67969 format= 'htmlEncode';
67971 if (format == 'raw' ) {
67975 if(name.substr(0, 4) == 'xtpl'){
67976 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
67979 // build an array of options to determine if value is undefined..
67981 // basically get 'xxxx.yyyy' then do
67982 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
67983 // (function () { Roo.log("Property not found"); return ''; })() :
67988 Roo.each(name.split('.'), function(st) {
67989 lookfor += (lookfor.length ? '.': '') + st;
67990 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
67993 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
67996 if(format && useF){
67998 args = args ? ',' + args : "";
68000 if(format.substr(0, 5) != "this."){
68001 format = "fm." + format + '(';
68003 format = 'this.call("'+ format.substr(5) + '", ';
68007 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
68011 // called with xxyx.yuu:(test,test)
68013 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
68015 // raw.. - :raw modifier..
68016 return "'"+ sep + udef_st + name + ")"+sep+"'";
68020 // branched to use + in gecko and [].join() in others
68022 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
68023 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68026 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
68027 body.push(tpl.body.replace(/(\r\n|\n)/g,
68028 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68029 body.push("'].join('');};};");
68030 body = body.join('');
68033 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68035 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
68041 applyTemplate : function(values){
68042 return this.master.compiled.call(this, values, {});
68043 //var s = this.subs;
68046 apply : function(){
68047 return this.applyTemplate.apply(this, arguments);
68052 Roo.XTemplate.from = function(el){
68053 el = Roo.getDom(el);
68054 return new Roo.XTemplate(el.value || el.innerHTML);