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");
508 // internal (non-delayed, will get a return value..)
509 callback : function(cb, scope, args, delay)
511 if(typeof cb != "function"){
515 cb.defer(delay, scope, args || []);
518 return cb.apply(scope, args || []);
523 * Return the dom node for the passed string (id), dom node, or Roo.Element
524 * @param {String/HTMLElement/Roo.Element} el
525 * @return HTMLElement
527 getDom : function(el){
531 return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
535 * Shorthand for {@link Roo.ComponentMgr#get}
537 * @return Roo.Component
539 getCmp : function(id){
540 return Roo.ComponentMgr.get(id);
543 num : function(v, defaultValue){
544 if(typeof v != 'number'){
550 destroy : function(){
551 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
555 as.removeAllListeners();
559 if(typeof as.purgeListeners == 'function'){
562 if(typeof as.destroy == 'function'){
569 // inpired by a similar function in mootools library
571 * Returns the type of object that is passed in. If the object passed in is null or undefined it
572 * return false otherwise it returns one of the following values:<ul>
573 * <li><b>string</b>: If the object passed is a string</li>
574 * <li><b>number</b>: If the object passed is a number</li>
575 * <li><b>boolean</b>: If the object passed is a boolean value</li>
576 * <li><b>function</b>: If the object passed is a function reference</li>
577 * <li><b>object</b>: If the object passed is an object</li>
578 * <li><b>array</b>: If the object passed is an array</li>
579 * <li><b>regexp</b>: If the object passed is a regular expression</li>
580 * <li><b>element</b>: If the object passed is a DOM Element</li>
581 * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
582 * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
583 * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
584 * @param {Mixed} object
588 if(o === undefined || o === null){
595 if(t == 'object' && o.nodeName) {
597 case 1: return 'element';
598 case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
601 if(t == 'object' || t == 'function') {
602 switch(o.constructor) {
603 case Array: return 'array';
604 case RegExp: return 'regexp';
606 if(typeof o.length == 'number' && typeof o.item == 'function') {
614 * Returns true if the passed value is null, undefined or an empty string (optional).
615 * @param {Mixed} value The value to test
616 * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
619 isEmpty : function(v, allowBlank){
620 return v === null || v === undefined || (!allowBlank ? v === '' : false);
628 isFirefox : isFirefox,
640 isBorderBox : isBorderBox,
642 isWindows : isWindows,
650 isAndroid : isAndroid,
655 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
656 * you may want to set this to true.
659 useShims : ((isIE && !isIE7) || (isGecko && isMac)),
664 * Selects a single element as a Roo Element
665 * This is about as close as you can get to jQuery's $('do crazy stuff')
666 * @param {String} selector The selector/xpath query
667 * @param {Node} root (optional) The start of the query (defaults to document).
668 * @return {Roo.Element}
670 selectNode : function(selector, root)
672 var node = Roo.DomQuery.selectNode(selector,root);
673 return node ? Roo.get(node) : new Roo.Element(false);
676 * Find the current bootstrap width Grid size
677 * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
678 * @returns {String} (xs|sm|md|lg|xl)
681 getGridSize : function()
683 var w = Roo.lib.Dom.getViewWidth();
704 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
705 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
710 * Ext JS Library 1.1.1
711 * Copyright(c) 2006-2007, Ext JS, LLC.
713 * Originally Released Under LGPL - original licence link has changed is not relivant.
716 * <script type="text/javascript">
720 // wrappedn so fnCleanup is not in global scope...
722 function fnCleanUp() {
723 var p = Function.prototype;
724 delete p.createSequence;
726 delete p.createDelegate;
727 delete p.createCallback;
728 delete p.createInterceptor;
730 window.detachEvent("onunload", fnCleanUp);
732 window.attachEvent("onunload", fnCleanUp);
739 * These functions are available on every Function object (any JavaScript function).
741 Roo.apply(Function.prototype, {
743 * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
744 * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
745 * Will create a function that is bound to those 2 args.
746 * @return {Function} The new function
748 createCallback : function(/*args...*/){
749 // make args available, in function below
750 var args = arguments;
753 return method.apply(window, args);
758 * Creates a delegate (callback) that sets the scope to obj.
759 * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
760 * Will create a function that is automatically scoped to this.
761 * @param {Object} obj (optional) The object for which the scope is set
762 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764 * if a number the args are inserted at the specified position
765 * @return {Function} The new function
767 createDelegate : function(obj, args, appendArgs){
770 var callArgs = args || arguments;
771 if(appendArgs === true){
772 callArgs = Array.prototype.slice.call(arguments, 0);
773 callArgs = callArgs.concat(args);
774 }else if(typeof appendArgs == "number"){
775 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
776 var applyArgs = [appendArgs, 0].concat(args); // create method call params
777 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
779 return method.apply(obj || window, callArgs);
784 * Calls this function after the number of millseconds specified.
785 * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
786 * @param {Object} obj (optional) The object for which the scope is set
787 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
788 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
789 * if a number the args are inserted at the specified position
790 * @return {Number} The timeout id that can be used with clearTimeout
792 defer : function(millis, obj, args, appendArgs){
793 var fn = this.createDelegate(obj, args, appendArgs);
795 return setTimeout(fn, millis);
801 * Create a combined function call sequence of the original function + the passed function.
802 * The resulting function returns the results of the original function.
803 * The passed fcn is called with the parameters of the original function
804 * @param {Function} fcn The function to sequence
805 * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
806 * @return {Function} The new function
808 createSequence : function(fcn, scope){
809 if(typeof fcn != "function"){
814 var retval = method.apply(this || window, arguments);
815 fcn.apply(scope || this || window, arguments);
821 * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
822 * The resulting function returns the results of the original function.
823 * The passed fcn is called with the parameters of the original function.
825 * @param {Function} fcn The function to call before the original
826 * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
827 * @return {Function} The new function
829 createInterceptor : function(fcn, scope){
830 if(typeof fcn != "function"){
837 if(fcn.apply(scope || this || window, arguments) === false){
840 return method.apply(this || window, arguments);
846 * Ext JS Library 1.1.1
847 * Copyright(c) 2006-2007, Ext JS, LLC.
849 * Originally Released Under LGPL - original licence link has changed is not relivant.
852 * <script type="text/javascript">
855 Roo.applyIf(String, {
860 * Escapes the passed string for ' and \
861 * @param {String} string The string to escape
862 * @return {String} The escaped string
865 escape : function(string) {
866 return string.replace(/('|\\)/g, "\\$1");
870 * Pads the left side of a string with a specified character. This is especially useful
871 * for normalizing number and date strings. Example usage:
873 var s = String.leftPad('123', 5, '0');
874 // s now contains the string: '00123'
876 * @param {String} string The original string
877 * @param {Number} size The total length of the output string
878 * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
879 * @return {String} The padded string
882 leftPad : function (val, size, ch) {
883 var result = new String(val);
884 if(ch === null || ch === undefined || ch === '') {
887 while (result.length < size) {
888 result = ch + result;
894 * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
895 * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
897 var cls = 'my-class', text = 'Some text';
898 var s = String.format('<div class="{0}">{1}</div>', cls, text);
899 // s now contains the string: '<div class="my-class">Some text</div>'
901 * @param {String} string The tokenized string to be formatted
902 * @param {String} value1 The value to replace token {0}
903 * @param {String} value2 Etc...
904 * @return {String} The formatted string
907 format : function(format){
908 var args = Array.prototype.slice.call(arguments, 1);
909 return format.replace(/\{(\d+)\}/g, function(m, i){
910 return Roo.util.Format.htmlEncode(args[i]);
918 * Utility function that allows you to easily switch a string between two alternating values. The passed value
919 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
920 * they are already different, the first value passed in is returned. Note that this method returns the new value
921 * but does not change the current string.
923 // alternate sort directions
924 sort = sort.toggle('ASC', 'DESC');
926 // instead of conditional logic:
927 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
929 * @param {String} value The value to compare to the current string
930 * @param {String} other The new value to use if the string already equals the first value passed in
931 * @return {String} The new value
934 String.prototype.toggle = function(value, other){
935 return this == value ? other : value;
940 * Remove invalid unicode characters from a string
942 * @return {String} The clean string
944 String.prototype.unicodeClean = function () {
945 return this.replace(/[\s\S]/g,
946 function(character) {
947 if (character.charCodeAt()< 256) {
951 encodeURIComponent(character);
962 * Make the first letter of a string uppercase
964 * @return {String} The new string.
966 String.prototype.toUpperCaseFirst = function () {
967 return this.charAt(0).toUpperCase() + this.slice(1);
972 * Ext JS Library 1.1.1
973 * Copyright(c) 2006-2007, Ext JS, LLC.
975 * Originally Released Under LGPL - original licence link has changed is not relivant.
978 * <script type="text/javascript">
984 Roo.applyIf(Number.prototype, {
986 * Checks whether or not the current number is within a desired range. If the number is already within the
987 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
988 * exceeded. Note that this method returns the constrained value but does not change the current number.
989 * @param {Number} min The minimum number in the range
990 * @param {Number} max The maximum number in the range
991 * @return {Number} The constrained value if outside the range, otherwise the current value
993 constrain : function(min, max){
994 return Math.min(Math.max(this, min), max);
998 * Ext JS Library 1.1.1
999 * Copyright(c) 2006-2007, Ext JS, LLC.
1001 * Originally Released Under LGPL - original licence link has changed is not relivant.
1004 * <script type="text/javascript">
1009 Roo.applyIf(Array.prototype, {
1012 * Checks whether or not the specified object exists in the array.
1013 * @param {Object} o The object to check for
1014 * @return {Number} The index of o in the array (or -1 if it is not found)
1016 indexOf : function(o){
1017 for (var i = 0, len = this.length; i < len; i++){
1018 if(this[i] == o) { return i; }
1024 * Removes the specified object from the array. If the object is not found nothing happens.
1025 * @param {Object} o The object to remove
1027 remove : function(o){
1028 var index = this.indexOf(o);
1030 this.splice(index, 1);
1034 * Map (JS 1.6 compatibility)
1035 * @param {Function} function to call
1037 map : function(fun )
1039 var len = this.length >>> 0;
1040 if (typeof fun != "function") {
1041 throw new TypeError();
1043 var res = new Array(len);
1044 var thisp = arguments[1];
1045 for (var i = 0; i < len; i++)
1048 res[i] = fun.call(thisp, this[i], i, this);
1056 * @param {Array} o The array to compare to
1057 * @returns {Boolean} true if the same
1059 equals : function(b)
1061 // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1068 if (this.length !== b.length) {
1072 // sort?? a.sort().equals(b.sort());
1074 for (var i = 0; i < this.length; ++i) {
1075 if (this[i] !== b[i]) {
1087 Roo.applyIf(Array, {
1091 * @param {Array} o Or Array like object (eg. nodelist)
1098 for (var i =0; i < o.length; i++) {
1107 * Ext JS Library 1.1.1
1108 * Copyright(c) 2006-2007, Ext JS, LLC.
1110 * Originally Released Under LGPL - original licence link has changed is not relivant.
1113 * <script type="text/javascript">
1119 * The date parsing and format syntax is a subset of
1120 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1121 * supported will provide results equivalent to their PHP versions.
1123 * Following is the list of all currently supported formats:
1126 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1128 Format Output Description
1129 ------ ---------- --------------------------------------------------------------
1130 d 10 Day of the month, 2 digits with leading zeros
1131 D Wed A textual representation of a day, three letters
1132 j 10 Day of the month without leading zeros
1133 l Wednesday A full textual representation of the day of the week
1134 S th English ordinal day of month suffix, 2 chars (use with j)
1135 w 3 Numeric representation of the day of the week
1136 z 9 The julian date, or day of the year (0-365)
1137 W 01 ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1138 F January A full textual representation of the month
1139 m 01 Numeric representation of a month, with leading zeros
1140 M Jan Month name abbreviation, three letters
1141 n 1 Numeric representation of a month, without leading zeros
1142 t 31 Number of days in the given month
1143 L 0 Whether it's a leap year (1 if it is a leap year, else 0)
1144 Y 2007 A full numeric representation of a year, 4 digits
1145 y 07 A two digit representation of a year
1146 a pm Lowercase Ante meridiem and Post meridiem
1147 A PM Uppercase Ante meridiem and Post meridiem
1148 g 3 12-hour format of an hour without leading zeros
1149 G 15 24-hour format of an hour without leading zeros
1150 h 03 12-hour format of an hour with leading zeros
1151 H 15 24-hour format of an hour with leading zeros
1152 i 05 Minutes with leading zeros
1153 s 01 Seconds, with leading zeros
1154 O -0600 Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1155 P -06:00 Difference to Greenwich time (GMT) with colon between hours and minutes
1156 T CST Timezone setting of the machine running the code
1157 Z -21600 Timezone offset in seconds (negative if west of UTC, positive if east)
1160 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1162 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1163 document.write(dt.format('Y-m-d')); //2007-01-10
1164 document.write(dt.format('F j, Y, g:i a')); //January 10, 2007, 3:05 pm
1165 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
1168 * Here are some standard date/time patterns that you might find helpful. They
1169 * are not part of the source of Date.js, but to use them you can simply copy this
1170 * block of code into any script that is included after Date.js and they will also become
1171 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
1174 ISO8601Long:"Y-m-d H:i:s",
1175 ISO8601Short:"Y-m-d",
1177 LongDate: "l, F d, Y",
1178 FullDateTime: "l, F d, Y g:i:s A",
1181 LongTime: "g:i:s A",
1182 SortableDateTime: "Y-m-d\\TH:i:s",
1183 UniversalSortableDateTime: "Y-m-d H:i:sO",
1190 var dt = new Date();
1191 document.write(dt.format(Date.patterns.ShortDate));
1196 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1197 * They generate precompiled functions from date formats instead of parsing and
1198 * processing the pattern every time you format a date. These functions are available
1199 * on every Date object (any javascript function).
1201 * The original article and download are here:
1202 * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1209 Returns the number of milliseconds between this date and date
1210 @param {Date} date (optional) Defaults to now
1211 @param {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY)
1212 @return {Number} The diff in milliseconds or units of interval
1213 @member Date getElapsed
1215 Date.prototype.getElapsed = function(date, interval)
1217 date = date || new Date();
1218 var ret = Math.abs(date.getTime()-this.getTime());
1222 return Math.floor(ret / (1000));
1224 return Math.floor(ret / (1000*60));
1226 return Math.floor(ret / (1000*60*60));
1228 return Math.floor(ret / (1000*60*60*24));
1229 case Date.MONTH: // this does not give exact number...??
1230 return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1231 case Date.YEAR: // this does not give exact number...??
1232 return (date.format("Y") - this.format("Y"));
1240 // was in date file..
1244 Date.parseFunctions = {count:0};
1246 Date.parseRegexes = [];
1248 Date.formatFunctions = {count:0};
1251 Date.prototype.dateFormat = function(format) {
1252 if (Date.formatFunctions[format] == null) {
1253 Date.createNewFormat(format);
1255 var func = Date.formatFunctions[format];
1256 return this[func]();
1261 * Formats a date given the supplied format string
1262 * @param {String} format The format string
1263 * @return {String} The formatted date
1266 Date.prototype.format = Date.prototype.dateFormat;
1269 Date.createNewFormat = function(format) {
1270 var funcName = "format" + Date.formatFunctions.count++;
1271 Date.formatFunctions[format] = funcName;
1272 var code = "Date.prototype." + funcName + " = function(){return ";
1273 var special = false;
1275 for (var i = 0; i < format.length; ++i) {
1276 ch = format.charAt(i);
1277 if (!special && ch == "\\") {
1282 code += "'" + String.escape(ch) + "' + ";
1285 code += Date.getFormatCode(ch);
1288 /** eval:var:zzzzzzzzzzzzz */
1289 eval(code.substring(0, code.length - 3) + ";}");
1293 Date.getFormatCode = function(character) {
1294 switch (character) {
1296 return "String.leftPad(this.getDate(), 2, '0') + ";
1298 return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1300 return "this.getDate() + ";
1302 return "Date.dayNames[this.getDay()] + ";
1304 return "this.getSuffix() + ";
1306 return "this.getDay() + ";
1308 return "this.getDayOfYear() + ";
1310 return "this.getWeekOfYear() + ";
1312 return "Date.monthNames[this.getMonth()] + ";
1314 return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1316 return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1318 return "(this.getMonth() + 1) + ";
1320 return "this.getDaysInMonth() + ";
1322 return "(this.isLeapYear() ? 1 : 0) + ";
1324 return "this.getFullYear() + ";
1326 return "('' + this.getFullYear()).substring(2, 4) + ";
1328 return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1330 return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1332 return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1334 return "this.getHours() + ";
1336 return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1338 return "String.leftPad(this.getHours(), 2, '0') + ";
1340 return "String.leftPad(this.getMinutes(), 2, '0') + ";
1342 return "String.leftPad(this.getSeconds(), 2, '0') + ";
1344 return "this.getGMTOffset() + ";
1346 return "this.getGMTColonOffset() + ";
1348 return "this.getTimezone() + ";
1350 return "(this.getTimezoneOffset() * -60) + ";
1352 return "'" + String.escape(character) + "' + ";
1357 * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1358 * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates. Any part of
1359 * the date format that is not specified will default to the current date value for that part. Time parts can also
1360 * be specified, but default to 0. Keep in mind that the input date string must precisely match the specified format
1361 * string or the parse operation will fail.
1364 //dt = Fri May 25 2007 (current date)
1365 var dt = new Date();
1367 //dt = Thu May 25 2006 (today's month/day in 2006)
1368 dt = Date.parseDate("2006", "Y");
1370 //dt = Sun Jan 15 2006 (all date parts specified)
1371 dt = Date.parseDate("2006-1-15", "Y-m-d");
1373 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1374 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1376 * @param {String} input The unparsed date as a string
1377 * @param {String} format The format the date is in
1378 * @return {Date} The parsed date
1381 Date.parseDate = function(input, format) {
1382 if (Date.parseFunctions[format] == null) {
1383 Date.createParser(format);
1385 var func = Date.parseFunctions[format];
1386 return Date[func](input);
1392 Date.createParser = function(format) {
1393 var funcName = "parse" + Date.parseFunctions.count++;
1394 var regexNum = Date.parseRegexes.length;
1395 var currentGroup = 1;
1396 Date.parseFunctions[format] = funcName;
1398 var code = "Date." + funcName + " = function(input){\n"
1399 + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1400 + "var d = new Date();\n"
1401 + "y = d.getFullYear();\n"
1402 + "m = d.getMonth();\n"
1403 + "d = d.getDate();\n"
1404 + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1405 + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1406 + "if (results && results.length > 0) {";
1409 var special = false;
1411 for (var i = 0; i < format.length; ++i) {
1412 ch = format.charAt(i);
1413 if (!special && ch == "\\") {
1418 regex += String.escape(ch);
1421 var obj = Date.formatCodeToRegex(ch, currentGroup);
1422 currentGroup += obj.g;
1424 if (obj.g && obj.c) {
1430 code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1431 + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1432 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1433 + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1434 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1435 + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1436 + "else if (y >= 0 && m >= 0 && d > 0)\n"
1437 + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1438 + "else if (y >= 0 && m >= 0)\n"
1439 + "{v = new Date(y, m); v.setFullYear(y);}\n"
1440 + "else if (y >= 0)\n"
1441 + "{v = new Date(y); v.setFullYear(y);}\n"
1442 + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1443 + " ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1444 + " v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1447 Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1448 /** eval:var:zzzzzzzzzzzzz */
1453 Date.formatCodeToRegex = function(character, currentGroup) {
1454 switch (character) {
1458 s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1461 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1462 s:"(\\d{1,2})"}; // day of month without leading zeroes
1465 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1466 s:"(\\d{2})"}; // day of month with leading zeroes
1470 s:"(?:" + Date.dayNames.join("|") + ")"};
1474 s:"(?:st|nd|rd|th)"};
1489 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1490 s:"(" + Date.monthNames.join("|") + ")"};
1493 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1494 s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1497 c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1498 s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1501 c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1502 s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1513 c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1517 c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1518 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1522 c:"if (results[" + currentGroup + "] == 'am') {\n"
1523 + "if (h == 12) { h = 0; }\n"
1524 + "} else { if (h < 12) { h += 12; }}",
1528 c:"if (results[" + currentGroup + "] == 'AM') {\n"
1529 + "if (h == 12) { h = 0; }\n"
1530 + "} else { if (h < 12) { h += 12; }}",
1535 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1536 s:"(\\d{1,2})"}; // 12/24-hr format format of an hour without leading zeroes
1540 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1541 s:"(\\d{2})"}; // 12/24-hr format format of an hour with leading zeroes
1544 c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1548 c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1553 "o = results[", currentGroup, "];\n",
1554 "var sn = o.substring(0,1);\n", // get + / - sign
1555 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1556 "var mn = o.substring(3,5) % 60;\n", // get minutes
1557 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1558 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1560 s:"([+\-]\\d{2,4})"};
1566 "o = results[", currentGroup, "];\n",
1567 "var sn = o.substring(0,1);\n",
1568 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1569 "var mn = o.substring(4,6) % 60;\n",
1570 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1571 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1577 s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1580 c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1581 + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1582 s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1586 s:String.escape(character)};
1591 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1592 * @return {String} The abbreviated timezone name (e.g. 'CST')
1594 Date.prototype.getTimezone = function() {
1595 return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1599 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1600 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1602 Date.prototype.getGMTOffset = function() {
1603 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1604 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1605 + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1609 * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1610 * @return {String} 2-characters representing hours and 2-characters representing minutes
1611 * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1613 Date.prototype.getGMTColonOffset = function() {
1614 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1615 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1617 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1621 * Get the numeric day number of the year, adjusted for leap year.
1622 * @return {Number} 0 through 364 (365 in leap years)
1624 Date.prototype.getDayOfYear = function() {
1626 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1627 for (var i = 0; i < this.getMonth(); ++i) {
1628 num += Date.daysInMonth[i];
1630 return num + this.getDate() - 1;
1634 * Get the string representation of the numeric week number of the year
1635 * (equivalent to the format specifier 'W').
1636 * @return {String} '00' through '52'
1638 Date.prototype.getWeekOfYear = function() {
1639 // Skip to Thursday of this week
1640 var now = this.getDayOfYear() + (4 - this.getDay());
1641 // Find the first Thursday of the year
1642 var jan1 = new Date(this.getFullYear(), 0, 1);
1643 var then = (7 - jan1.getDay() + 4);
1644 return String.leftPad(((now - then) / 7) + 1, 2, "0");
1648 * Whether or not the current date is in a leap year.
1649 * @return {Boolean} True if the current date is in a leap year, else false
1651 Date.prototype.isLeapYear = function() {
1652 var year = this.getFullYear();
1653 return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1657 * Get the first day of the current month, adjusted for leap year. The returned value
1658 * is the numeric day index within the week (0-6) which can be used in conjunction with
1659 * the {@link #monthNames} array to retrieve the textual day name.
1662 var dt = new Date('1/10/2007');
1663 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1665 * @return {Number} The day number (0-6)
1667 Date.prototype.getFirstDayOfMonth = function() {
1668 var day = (this.getDay() - (this.getDate() - 1)) % 7;
1669 return (day < 0) ? (day + 7) : day;
1673 * Get the last day of the current month, adjusted for leap year. The returned value
1674 * is the numeric day index within the week (0-6) which can be used in conjunction with
1675 * the {@link #monthNames} array to retrieve the textual day name.
1678 var dt = new Date('1/10/2007');
1679 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1681 * @return {Number} The day number (0-6)
1683 Date.prototype.getLastDayOfMonth = function() {
1684 var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1685 return (day < 0) ? (day + 7) : day;
1690 * Get the first date of this date's month
1693 Date.prototype.getFirstDateOfMonth = function() {
1694 return new Date(this.getFullYear(), this.getMonth(), 1);
1698 * Get the last date of this date's month
1701 Date.prototype.getLastDateOfMonth = function() {
1702 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1705 * Get the number of days in the current month, adjusted for leap year.
1706 * @return {Number} The number of days in the month
1708 Date.prototype.getDaysInMonth = function() {
1709 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1710 return Date.daysInMonth[this.getMonth()];
1714 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1715 * @return {String} 'st, 'nd', 'rd' or 'th'
1717 Date.prototype.getSuffix = function() {
1718 switch (this.getDate()) {
1735 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1738 * An array of textual month names.
1739 * Override these values for international dates, for example...
1740 * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1759 * An array of textual day names.
1760 * Override these values for international dates, for example...
1761 * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1777 Date.monthNumbers = {
1792 * Creates and returns a new Date instance with the exact same date value as the called instance.
1793 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1794 * variable will also be changed. When the intention is to create a new variable that will not
1795 * modify the original instance, you should create a clone.
1797 * Example of correctly cloning a date:
1800 var orig = new Date('10/1/2006');
1803 document.write(orig); //returns 'Thu Oct 05 2006'!
1806 var orig = new Date('10/1/2006');
1807 var copy = orig.clone();
1809 document.write(orig); //returns 'Thu Oct 01 2006'
1811 * @return {Date} The new Date instance
1813 Date.prototype.clone = function() {
1814 return new Date(this.getTime());
1818 * Clears any time information from this date
1819 @param {Boolean} clone true to create a clone of this date, clear the time and return it
1820 @return {Date} this or the clone
1822 Date.prototype.clearTime = function(clone){
1824 return this.clone().clearTime();
1829 this.setMilliseconds(0);
1834 // safari setMonth is broken -- check that this is only donw once...
1835 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1836 Date.brokenSetMonth = Date.prototype.setMonth;
1837 Date.prototype.setMonth = function(num){
1839 var n = Math.ceil(-num);
1840 var back_year = Math.ceil(n/12);
1841 var month = (n % 12) ? 12 - n % 12 : 0 ;
1842 this.setFullYear(this.getFullYear() - back_year);
1843 return Date.brokenSetMonth.call(this, month);
1845 return Date.brokenSetMonth.apply(this, arguments);
1850 /** Date interval constant
1854 /** Date interval constant
1858 /** Date interval constant
1862 /** Date interval constant
1866 /** Date interval constant
1870 /** Date interval constant
1874 /** Date interval constant
1880 * Provides a convenient method of performing basic date arithmetic. This method
1881 * does not modify the Date instance being called - it creates and returns
1882 * a new Date instance containing the resulting date value.
1887 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1888 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1890 //Negative values will subtract correctly:
1891 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1892 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1894 //You can even chain several calls together in one line!
1895 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1896 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1899 * @param {String} interval A valid date interval enum value
1900 * @param {Number} value The amount to add to the current date
1901 * @return {Date} The new Date instance
1903 Date.prototype.add = function(interval, value){
1904 var d = this.clone();
1905 if (!interval || value === 0) { return d; }
1906 switch(interval.toLowerCase()){
1908 d.setMilliseconds(this.getMilliseconds() + value);
1911 d.setSeconds(this.getSeconds() + value);
1914 d.setMinutes(this.getMinutes() + value);
1917 d.setHours(this.getHours() + value);
1920 d.setDate(this.getDate() + value);
1923 var day = this.getDate();
1925 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1928 d.setMonth(this.getMonth() + value);
1931 d.setFullYear(this.getFullYear() + value);
1937 * @class Roo.lib.Dom
1941 * Dom utils (from YIU afaik)
1947 * Get the view width
1948 * @param {Boolean} full True will get the full document, otherwise it's the view width
1949 * @return {Number} The width
1952 getViewWidth : function(full) {
1953 return full ? this.getDocumentWidth() : this.getViewportWidth();
1956 * Get the view height
1957 * @param {Boolean} full True will get the full document, otherwise it's the view height
1958 * @return {Number} The height
1960 getViewHeight : function(full) {
1961 return full ? this.getDocumentHeight() : this.getViewportHeight();
1964 * Get the Full Document height
1965 * @return {Number} The height
1967 getDocumentHeight: function() {
1968 var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1969 return Math.max(scrollHeight, this.getViewportHeight());
1972 * Get the Full Document width
1973 * @return {Number} The width
1975 getDocumentWidth: function() {
1976 var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1977 return Math.max(scrollWidth, this.getViewportWidth());
1980 * Get the Window Viewport height
1981 * @return {Number} The height
1983 getViewportHeight: function() {
1984 var height = self.innerHeight;
1985 var mode = document.compatMode;
1987 if ((mode || Roo.isIE) && !Roo.isOpera) {
1988 height = (mode == "CSS1Compat") ?
1989 document.documentElement.clientHeight :
1990 document.body.clientHeight;
1996 * Get the Window Viewport width
1997 * @return {Number} The width
1999 getViewportWidth: function() {
2000 var width = self.innerWidth;
2001 var mode = document.compatMode;
2003 if (mode || Roo.isIE) {
2004 width = (mode == "CSS1Compat") ?
2005 document.documentElement.clientWidth :
2006 document.body.clientWidth;
2011 isAncestor : function(p, c) {
2018 if (p.contains && !Roo.isSafari) {
2019 return p.contains(c);
2020 } else if (p.compareDocumentPosition) {
2021 return !!(p.compareDocumentPosition(c) & 16);
2023 var parent = c.parentNode;
2028 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2031 parent = parent.parentNode;
2037 getRegion : function(el) {
2038 return Roo.lib.Region.getRegion(el);
2041 getY : function(el) {
2042 return this.getXY(el)[1];
2045 getX : function(el) {
2046 return this.getXY(el)[0];
2049 getXY : function(el) {
2050 var p, pe, b, scroll, bd = document.body;
2051 el = Roo.getDom(el);
2052 var fly = Roo.lib.AnimBase.fly;
2053 if (el.getBoundingClientRect) {
2054 b = el.getBoundingClientRect();
2055 scroll = fly(document).getScroll();
2056 return [b.left + scroll.left, b.top + scroll.top];
2062 var hasAbsolute = fly(el).getStyle("position") == "absolute";
2069 if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2076 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2077 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2084 if (p != el && pe.getStyle('overflow') != 'visible') {
2092 if (Roo.isSafari && hasAbsolute) {
2097 if (Roo.isGecko && !hasAbsolute) {
2099 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2100 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2104 while (p && p != bd) {
2105 if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2117 setXY : function(el, xy) {
2118 el = Roo.fly(el, '_setXY');
2120 var pts = el.translatePoints(xy);
2121 if (xy[0] !== false) {
2122 el.dom.style.left = pts.left + "px";
2124 if (xy[1] !== false) {
2125 el.dom.style.top = pts.top + "px";
2129 setX : function(el, x) {
2130 this.setXY(el, [x, false]);
2133 setY : function(el, y) {
2134 this.setXY(el, [false, y]);
2138 * Portions of this file are based on pieces of Yahoo User Interface Library
2139 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2140 * YUI licensed under the BSD License:
2141 * http://developer.yahoo.net/yui/license.txt
2142 * <script type="text/javascript">
2146 Roo.lib.Event = function() {
2147 var loadComplete = false;
2149 var unloadListeners = [];
2151 var onAvailStack = [];
2153 var lastError = null;
2166 startInterval: function() {
2167 if (!this._interval) {
2169 var callback = function() {
2170 self._tryPreloadAttach();
2172 this._interval = setInterval(callback, this.POLL_INTERVAL);
2177 onAvailable: function(p_id, p_fn, p_obj, p_override) {
2178 onAvailStack.push({ id: p_id,
2181 override: p_override,
2182 checkReady: false });
2184 retryCount = this.POLL_RETRYS;
2185 this.startInterval();
2189 addListener: function(el, eventName, fn) {
2190 el = Roo.getDom(el);
2195 if ("unload" == eventName) {
2196 unloadListeners[unloadListeners.length] =
2197 [el, eventName, fn];
2201 var wrappedFn = function(e) {
2202 return fn(Roo.lib.Event.getEvent(e));
2205 var li = [el, eventName, fn, wrappedFn];
2207 var index = listeners.length;
2208 listeners[index] = li;
2210 this.doAdd(el, eventName, wrappedFn, false);
2216 removeListener: function(el, eventName, fn) {
2219 el = Roo.getDom(el);
2222 return this.purgeElement(el, false, eventName);
2226 if ("unload" == eventName) {
2228 for (i = 0,len = unloadListeners.length; i < len; i++) {
2229 var li = unloadListeners[i];
2232 li[1] == eventName &&
2234 unloadListeners.splice(i, 1);
2242 var cacheItem = null;
2245 var index = arguments[3];
2247 if ("undefined" == typeof index) {
2248 index = this._getCacheIndex(el, eventName, fn);
2252 cacheItem = listeners[index];
2255 if (!el || !cacheItem) {
2259 this.doRemove(el, eventName, cacheItem[this.WFN], false);
2261 delete listeners[index][this.WFN];
2262 delete listeners[index][this.FN];
2263 listeners.splice(index, 1);
2270 getTarget: function(ev, resolveTextNode) {
2271 ev = ev.browserEvent || ev;
2272 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2273 var t = ev.target || ev.srcElement;
2274 return this.resolveTextNode(t);
2278 resolveTextNode: function(node) {
2279 if (Roo.isSafari && node && 3 == node.nodeType) {
2280 return node.parentNode;
2287 getPageX: function(ev) {
2288 ev = ev.browserEvent || ev;
2289 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2291 if (!x && 0 !== x) {
2292 x = ev.clientX || 0;
2295 x += this.getScroll()[1];
2303 getPageY: function(ev) {
2304 ev = ev.browserEvent || ev;
2305 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2307 if (!y && 0 !== y) {
2308 y = ev.clientY || 0;
2311 y += this.getScroll()[0];
2320 getXY: function(ev) {
2321 ev = ev.browserEvent || ev;
2322 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2323 return [this.getPageX(ev), this.getPageY(ev)];
2327 getRelatedTarget: function(ev) {
2328 ev = ev.browserEvent || ev;
2329 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2330 var t = ev.relatedTarget;
2332 if (ev.type == "mouseout") {
2334 } else if (ev.type == "mouseover") {
2339 return this.resolveTextNode(t);
2343 getTime: function(ev) {
2344 ev = ev.browserEvent || ev;
2345 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2347 var t = new Date().getTime();
2351 this.lastError = ex;
2360 stopEvent: function(ev) {
2361 this.stopPropagation(ev);
2362 this.preventDefault(ev);
2366 stopPropagation: function(ev) {
2367 ev = ev.browserEvent || ev;
2368 if (ev.stopPropagation) {
2369 ev.stopPropagation();
2371 ev.cancelBubble = true;
2376 preventDefault: function(ev) {
2377 ev = ev.browserEvent || ev;
2378 if(ev.preventDefault) {
2379 ev.preventDefault();
2381 ev.returnValue = false;
2386 getEvent: function(e) {
2387 var ev = e || window.event;
2389 var c = this.getEvent.caller;
2391 ev = c.arguments[0];
2392 if (ev && Event == ev.constructor) {
2402 getCharCode: function(ev) {
2403 ev = ev.browserEvent || ev;
2404 return ev.charCode || ev.keyCode || 0;
2408 _getCacheIndex: function(el, eventName, fn) {
2409 for (var i = 0,len = listeners.length; i < len; ++i) {
2410 var li = listeners[i];
2412 li[this.FN] == fn &&
2413 li[this.EL] == el &&
2414 li[this.TYPE] == eventName) {
2426 getEl: function(id) {
2427 return document.getElementById(id);
2431 clearCache: function() {
2435 _load: function(e) {
2436 loadComplete = true;
2437 var EU = Roo.lib.Event;
2441 EU.doRemove(window, "load", EU._load);
2446 _tryPreloadAttach: function() {
2455 var tryAgain = !loadComplete;
2457 tryAgain = (retryCount > 0);
2462 for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2463 var item = onAvailStack[i];
2465 var el = this.getEl(item.id);
2468 if (!item.checkReady ||
2471 (document && document.body)) {
2474 if (item.override) {
2475 if (item.override === true) {
2478 scope = item.override;
2481 item.fn.call(scope, item.obj);
2482 onAvailStack[i] = null;
2485 notAvail.push(item);
2490 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2494 this.startInterval();
2496 clearInterval(this._interval);
2497 this._interval = null;
2500 this.locked = false;
2507 purgeElement: function(el, recurse, eventName) {
2508 var elListeners = this.getListeners(el, eventName);
2510 for (var i = 0,len = elListeners.length; i < len; ++i) {
2511 var l = elListeners[i];
2512 this.removeListener(el, l.type, l.fn);
2516 if (recurse && el && el.childNodes) {
2517 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2518 this.purgeElement(el.childNodes[i], recurse, eventName);
2524 getListeners: function(el, eventName) {
2525 var results = [], searchLists;
2527 searchLists = [listeners, unloadListeners];
2528 } else if (eventName == "unload") {
2529 searchLists = [unloadListeners];
2531 searchLists = [listeners];
2534 for (var j = 0; j < searchLists.length; ++j) {
2535 var searchList = searchLists[j];
2536 if (searchList && searchList.length > 0) {
2537 for (var i = 0,len = searchList.length; i < len; ++i) {
2538 var l = searchList[i];
2539 if (l && l[this.EL] === el &&
2540 (!eventName || eventName === l[this.TYPE])) {
2545 adjust: l[this.ADJ_SCOPE],
2553 return (results.length) ? results : null;
2557 _unload: function(e) {
2559 var EU = Roo.lib.Event, i, j, l, len, index;
2561 for (i = 0,len = unloadListeners.length; i < len; ++i) {
2562 l = unloadListeners[i];
2565 if (l[EU.ADJ_SCOPE]) {
2566 if (l[EU.ADJ_SCOPE] === true) {
2569 scope = l[EU.ADJ_SCOPE];
2572 l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2573 unloadListeners[i] = null;
2579 unloadListeners = null;
2581 if (listeners && listeners.length > 0) {
2582 j = listeners.length;
2585 l = listeners[index];
2587 EU.removeListener(l[EU.EL], l[EU.TYPE],
2597 EU.doRemove(window, "unload", EU._unload);
2602 getScroll: function() {
2603 var dd = document.documentElement, db = document.body;
2604 if (dd && (dd.scrollTop || dd.scrollLeft)) {
2605 return [dd.scrollTop, dd.scrollLeft];
2607 return [db.scrollTop, db.scrollLeft];
2614 doAdd: function () {
2615 if (window.addEventListener) {
2616 return function(el, eventName, fn, capture) {
2617 el.addEventListener(eventName, fn, (capture));
2619 } else if (window.attachEvent) {
2620 return function(el, eventName, fn, capture) {
2621 el.attachEvent("on" + eventName, fn);
2630 doRemove: function() {
2631 if (window.removeEventListener) {
2632 return function (el, eventName, fn, capture) {
2633 el.removeEventListener(eventName, fn, (capture));
2635 } else if (window.detachEvent) {
2636 return function (el, eventName, fn) {
2637 el.detachEvent("on" + eventName, fn);
2649 var E = Roo.lib.Event;
2650 E.on = E.addListener;
2651 E.un = E.removeListener;
2653 if (document && document.body) {
2656 E.doAdd(window, "load", E._load);
2658 E.doAdd(window, "unload", E._unload);
2659 E._tryPreloadAttach();
2666 * @class Roo.lib.Ajax
2668 * provide a simple Ajax request utility functions
2670 * Portions of this file are based on pieces of Yahoo User Interface Library
2671 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2672 * YUI licensed under the BSD License:
2673 * http://developer.yahoo.net/yui/license.txt
2674 * <script type="text/javascript">
2682 request : function(method, uri, cb, data, options) {
2684 var hs = options.headers;
2687 if(hs.hasOwnProperty(h)){
2688 this.initHeader(h, hs[h], false);
2692 if(options.xmlData){
2693 this.initHeader('Content-Type', 'text/xml', false);
2695 data = options.xmlData;
2699 return this.asyncRequest(method, uri, cb, data);
2705 * @param {DomForm} form element
2706 * @return {String} urlencode form output.
2708 serializeForm : function(form, include_disabled) {
2710 include_disabled = typeof(include_disabled) == 'undefined' ? false : include_disabled;
2712 if(typeof form == 'string') {
2713 form = (document.getElementById(form) || document.forms[form]);
2716 var el, name, val, disabled, data = '', hasSubmit = false;
2717 for (var i = 0; i < form.elements.length; i++) {
2718 el = form.elements[i];
2719 disabled = include_disabled ? false : form.elements[i].disabled;
2720 name = form.elements[i].name;
2721 val = form.elements[i].value;
2723 if (!disabled && name){
2727 case 'select-multiple':
2728 for (var j = 0; j < el.options.length; j++) {
2729 if (el.options[j].selected) {
2731 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2734 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2742 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2755 if(hasSubmit == false) {
2756 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2761 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2766 data = data.substr(0, data.length - 1);
2774 useDefaultHeader:true,
2776 defaultPostHeader:'application/x-www-form-urlencoded',
2778 useDefaultXhrHeader:true,
2780 defaultXhrHeader:'XMLHttpRequest',
2782 hasDefaultHeaders:true,
2794 setProgId:function(id)
2796 this.activeX.unshift(id);
2799 setDefaultPostHeader:function(b)
2801 this.useDefaultHeader = b;
2804 setDefaultXhrHeader:function(b)
2806 this.useDefaultXhrHeader = b;
2809 setPollingInterval:function(i)
2811 if (typeof i == 'number' && isFinite(i)) {
2812 this.pollInterval = i;
2816 createXhrObject:function(transactionId)
2822 http = new XMLHttpRequest();
2824 obj = { conn:http, tId:transactionId };
2828 for (var i = 0; i < this.activeX.length; ++i) {
2832 http = new ActiveXObject(this.activeX[i]);
2834 obj = { conn:http, tId:transactionId };
2847 getConnectionObject:function()
2850 var tId = this.transactionId;
2854 o = this.createXhrObject(tId);
2856 this.transactionId++;
2867 asyncRequest:function(method, uri, callback, postData)
2869 var o = this.getConnectionObject();
2875 o.conn.open(method, uri, true);
2877 if (this.useDefaultXhrHeader) {
2878 if (!this.defaultHeaders['X-Requested-With']) {
2879 this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2883 if(postData && this.useDefaultHeader){
2884 this.initHeader('Content-Type', this.defaultPostHeader);
2887 if (this.hasDefaultHeaders || this.hasHeaders) {
2891 this.handleReadyState(o, callback);
2892 o.conn.send(postData || null);
2898 handleReadyState:function(o, callback)
2902 if (callback && callback.timeout) {
2904 this.timeout[o.tId] = window.setTimeout(function() {
2905 oConn.abort(o, callback, true);
2906 }, callback.timeout);
2909 this.poll[o.tId] = window.setInterval(
2911 if (o.conn && o.conn.readyState == 4) {
2912 window.clearInterval(oConn.poll[o.tId]);
2913 delete oConn.poll[o.tId];
2915 if(callback && callback.timeout) {
2916 window.clearTimeout(oConn.timeout[o.tId]);
2917 delete oConn.timeout[o.tId];
2920 oConn.handleTransactionResponse(o, callback);
2923 , this.pollInterval);
2926 handleTransactionResponse:function(o, callback, isAbort)
2930 this.releaseObject(o);
2934 var httpStatus, responseObject;
2938 if (o.conn.status !== undefined && o.conn.status != 0) {
2939 httpStatus = o.conn.status;
2951 if (httpStatus >= 200 && httpStatus < 300) {
2952 responseObject = this.createResponseObject(o, callback.argument);
2953 if (callback.success) {
2954 if (!callback.scope) {
2955 callback.success(responseObject);
2960 callback.success.apply(callback.scope, [responseObject]);
2965 switch (httpStatus) {
2973 responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2974 if (callback.failure) {
2975 if (!callback.scope) {
2976 callback.failure(responseObject);
2979 callback.failure.apply(callback.scope, [responseObject]);
2984 responseObject = this.createResponseObject(o, callback.argument);
2985 if (callback.failure) {
2986 if (!callback.scope) {
2987 callback.failure(responseObject);
2990 callback.failure.apply(callback.scope, [responseObject]);
2996 this.releaseObject(o);
2997 responseObject = null;
3000 createResponseObject:function(o, callbackArg)
3007 var headerStr = o.conn.getAllResponseHeaders();
3008 var header = headerStr.split('\n');
3009 for (var i = 0; i < header.length; i++) {
3010 var delimitPos = header[i].indexOf(':');
3011 if (delimitPos != -1) {
3012 headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3020 obj.status = o.conn.status;
3021 obj.statusText = o.conn.statusText;
3022 obj.getResponseHeader = headerObj;
3023 obj.getAllResponseHeaders = headerStr;
3024 obj.responseText = o.conn.responseText;
3025 obj.responseXML = o.conn.responseXML;
3027 if (typeof callbackArg !== undefined) {
3028 obj.argument = callbackArg;
3034 createExceptionObject:function(tId, callbackArg, isAbort)
3037 var COMM_ERROR = 'communication failure';
3038 var ABORT_CODE = -1;
3039 var ABORT_ERROR = 'transaction aborted';
3045 obj.status = ABORT_CODE;
3046 obj.statusText = ABORT_ERROR;
3049 obj.status = COMM_CODE;
3050 obj.statusText = COMM_ERROR;
3054 obj.argument = callbackArg;
3060 initHeader:function(label, value, isDefault)
3062 var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3064 if (headerObj[label] === undefined) {
3065 headerObj[label] = value;
3070 headerObj[label] = value + "," + headerObj[label];
3074 this.hasDefaultHeaders = true;
3077 this.hasHeaders = true;
3082 setHeader:function(o)
3084 if (this.hasDefaultHeaders) {
3085 for (var prop in this.defaultHeaders) {
3086 if (this.defaultHeaders.hasOwnProperty(prop)) {
3087 o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3092 if (this.hasHeaders) {
3093 for (var prop in this.headers) {
3094 if (this.headers.hasOwnProperty(prop)) {
3095 o.conn.setRequestHeader(prop, this.headers[prop]);
3099 this.hasHeaders = false;
3103 resetDefaultHeaders:function() {
3104 delete this.defaultHeaders;
3105 this.defaultHeaders = {};
3106 this.hasDefaultHeaders = false;
3109 abort:function(o, callback, isTimeout)
3111 if(this.isCallInProgress(o)) {
3113 window.clearInterval(this.poll[o.tId]);
3114 delete this.poll[o.tId];
3116 delete this.timeout[o.tId];
3119 this.handleTransactionResponse(o, callback, true);
3129 isCallInProgress:function(o)
3132 return o.conn.readyState != 4 && o.conn.readyState != 0;
3141 releaseObject:function(o)
3150 'MSXML2.XMLHTTP.3.0',
3158 * Portions of this file are based on pieces of Yahoo User Interface Library
3159 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3160 * YUI licensed under the BSD License:
3161 * http://developer.yahoo.net/yui/license.txt
3162 * <script type="text/javascript">
3166 Roo.lib.Region = function(t, r, b, l) {
3176 Roo.lib.Region.prototype = {
3177 contains : function(region) {
3178 return ( region.left >= this.left &&
3179 region.right <= this.right &&
3180 region.top >= this.top &&
3181 region.bottom <= this.bottom );
3185 getArea : function() {
3186 return ( (this.bottom - this.top) * (this.right - this.left) );
3189 intersect : function(region) {
3190 var t = Math.max(this.top, region.top);
3191 var r = Math.min(this.right, region.right);
3192 var b = Math.min(this.bottom, region.bottom);
3193 var l = Math.max(this.left, region.left);
3195 if (b >= t && r >= l) {
3196 return new Roo.lib.Region(t, r, b, l);
3201 union : function(region) {
3202 var t = Math.min(this.top, region.top);
3203 var r = Math.max(this.right, region.right);
3204 var b = Math.max(this.bottom, region.bottom);
3205 var l = Math.min(this.left, region.left);
3207 return new Roo.lib.Region(t, r, b, l);
3210 adjust : function(t, l, b, r) {
3219 Roo.lib.Region.getRegion = function(el) {
3220 var p = Roo.lib.Dom.getXY(el);
3223 var r = p[0] + el.offsetWidth;
3224 var b = p[1] + el.offsetHeight;
3227 return new Roo.lib.Region(t, r, b, l);
3230 * Portions of this file are based on pieces of Yahoo User Interface Library
3231 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3232 * YUI licensed under the BSD License:
3233 * http://developer.yahoo.net/yui/license.txt
3234 * <script type="text/javascript">
3237 //@@dep Roo.lib.Region
3240 Roo.lib.Point = function(x, y) {
3241 if (x instanceof Array) {
3245 this.x = this.right = this.left = this[0] = x;
3246 this.y = this.top = this.bottom = this[1] = y;
3249 Roo.lib.Point.prototype = new Roo.lib.Region();
3251 * Portions of this file are based on pieces of Yahoo User Interface Library
3252 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3253 * YUI licensed under the BSD License:
3254 * http://developer.yahoo.net/yui/license.txt
3255 * <script type="text/javascript">
3262 scroll : function(el, args, duration, easing, cb, scope) {
3263 this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3266 motion : function(el, args, duration, easing, cb, scope) {
3267 this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3270 color : function(el, args, duration, easing, cb, scope) {
3271 this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3274 run : function(el, args, duration, easing, cb, scope, type) {
3275 type = type || Roo.lib.AnimBase;
3276 if (typeof easing == "string") {
3277 easing = Roo.lib.Easing[easing];
3279 var anim = new type(el, args, duration, easing);
3280 anim.animateX(function() {
3281 Roo.callback(cb, scope);
3287 * Portions of this file are based on pieces of Yahoo User Interface Library
3288 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3289 * YUI licensed under the BSD License:
3290 * http://developer.yahoo.net/yui/license.txt
3291 * <script type="text/javascript">
3299 if (!libFlyweight) {
3300 libFlyweight = new Roo.Element.Flyweight();
3302 libFlyweight.dom = el;
3303 return libFlyweight;
3306 // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3310 Roo.lib.AnimBase = function(el, attributes, duration, method) {
3312 this.init(el, attributes, duration, method);
3316 Roo.lib.AnimBase.fly = fly;
3320 Roo.lib.AnimBase.prototype = {
3322 toString: function() {
3323 var el = this.getEl();
3324 var id = el.id || el.tagName;
3325 return ("Anim " + id);
3329 noNegatives: /width|height|opacity|padding/i,
3330 offsetAttribute: /^((width|height)|(top|left))$/,
3331 defaultUnit: /width|height|top$|bottom$|left$|right$/i,
3332 offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3336 doMethod: function(attr, start, end) {
3337 return this.method(this.currentFrame, start, end - start, this.totalFrames);
3341 setAttribute: function(attr, val, unit) {
3342 if (this.patterns.noNegatives.test(attr)) {
3343 val = (val > 0) ? val : 0;
3346 Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3350 getAttribute: function(attr) {
3351 var el = this.getEl();
3352 var val = fly(el).getStyle(attr);
3354 if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3355 return parseFloat(val);
3358 var a = this.patterns.offsetAttribute.exec(attr) || [];
3359 var pos = !!( a[3] );
3360 var box = !!( a[2] );
3363 if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3364 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3373 getDefaultUnit: function(attr) {
3374 if (this.patterns.defaultUnit.test(attr)) {
3381 animateX : function(callback, scope) {
3382 var f = function() {
3383 this.onComplete.removeListener(f);
3384 if (typeof callback == "function") {
3385 callback.call(scope || this, this);
3388 this.onComplete.addListener(f, this);
3393 setRuntimeAttribute: function(attr) {
3396 var attributes = this.attributes;
3398 this.runtimeAttributes[attr] = {};
3400 var isset = function(prop) {
3401 return (typeof prop !== 'undefined');
3404 if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3408 start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3411 if (isset(attributes[attr]['to'])) {
3412 end = attributes[attr]['to'];
3413 } else if (isset(attributes[attr]['by'])) {
3414 if (start.constructor == Array) {
3416 for (var i = 0, len = start.length; i < len; ++i) {
3417 end[i] = start[i] + attributes[attr]['by'][i];
3420 end = start + attributes[attr]['by'];
3424 this.runtimeAttributes[attr].start = start;
3425 this.runtimeAttributes[attr].end = end;
3428 this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3432 init: function(el, attributes, duration, method) {
3434 var isAnimated = false;
3437 var startTime = null;
3440 var actualFrames = 0;
3443 el = Roo.getDom(el);
3446 this.attributes = attributes || {};
3449 this.duration = duration || 1;
3452 this.method = method || Roo.lib.Easing.easeNone;
3455 this.useSeconds = true;
3458 this.currentFrame = 0;
3461 this.totalFrames = Roo.lib.AnimMgr.fps;
3464 this.getEl = function() {
3469 this.isAnimated = function() {
3474 this.getStartTime = function() {
3478 this.runtimeAttributes = {};
3481 this.animate = function() {
3482 if (this.isAnimated()) {
3486 this.currentFrame = 0;
3488 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3490 Roo.lib.AnimMgr.registerElement(this);
3494 this.stop = function(finish) {
3496 this.currentFrame = this.totalFrames;
3497 this._onTween.fire();
3499 Roo.lib.AnimMgr.stop(this);
3502 var onStart = function() {
3503 this.onStart.fire();
3505 this.runtimeAttributes = {};
3506 for (var attr in this.attributes) {
3507 this.setRuntimeAttribute(attr);
3512 startTime = new Date();
3516 var onTween = function() {
3518 duration: new Date() - this.getStartTime(),
3519 currentFrame: this.currentFrame
3522 data.toString = function() {
3524 'duration: ' + data.duration +
3525 ', currentFrame: ' + data.currentFrame
3529 this.onTween.fire(data);
3531 var runtimeAttributes = this.runtimeAttributes;
3533 for (var attr in runtimeAttributes) {
3534 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3540 var onComplete = function() {
3541 var actual_duration = (new Date() - startTime) / 1000 ;
3544 duration: actual_duration,
3545 frames: actualFrames,
3546 fps: actualFrames / actual_duration
3549 data.toString = function() {
3551 'duration: ' + data.duration +
3552 ', frames: ' + data.frames +
3553 ', fps: ' + data.fps
3559 this.onComplete.fire(data);
3563 this._onStart = new Roo.util.Event(this);
3564 this.onStart = new Roo.util.Event(this);
3565 this.onTween = new Roo.util.Event(this);
3566 this._onTween = new Roo.util.Event(this);
3567 this.onComplete = new Roo.util.Event(this);
3568 this._onComplete = new Roo.util.Event(this);
3569 this._onStart.addListener(onStart);
3570 this._onTween.addListener(onTween);
3571 this._onComplete.addListener(onComplete);
3576 * Portions of this file are based on pieces of Yahoo User Interface Library
3577 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3578 * YUI licensed under the BSD License:
3579 * http://developer.yahoo.net/yui/license.txt
3580 * <script type="text/javascript">
3584 Roo.lib.AnimMgr = new function() {
3601 this.registerElement = function(tween) {
3602 queue[queue.length] = tween;
3604 tween._onStart.fire();
3609 this.unRegister = function(tween, index) {
3610 tween._onComplete.fire();
3611 index = index || getIndex(tween);
3613 queue.splice(index, 1);
3617 if (tweenCount <= 0) {
3623 this.start = function() {
3624 if (thread === null) {
3625 thread = setInterval(this.run, this.delay);
3630 this.stop = function(tween) {
3632 clearInterval(thread);
3634 for (var i = 0, len = queue.length; i < len; ++i) {
3635 if (queue[0].isAnimated()) {
3636 this.unRegister(queue[0], 0);
3645 this.unRegister(tween);
3650 this.run = function() {
3651 for (var i = 0, len = queue.length; i < len; ++i) {
3652 var tween = queue[i];
3653 if (!tween || !tween.isAnimated()) {
3657 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3659 tween.currentFrame += 1;
3661 if (tween.useSeconds) {
3662 correctFrame(tween);
3664 tween._onTween.fire();
3667 Roo.lib.AnimMgr.stop(tween, i);
3672 var getIndex = function(anim) {
3673 for (var i = 0, len = queue.length; i < len; ++i) {
3674 if (queue[i] == anim) {
3682 var correctFrame = function(tween) {
3683 var frames = tween.totalFrames;
3684 var frame = tween.currentFrame;
3685 var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3686 var elapsed = (new Date() - tween.getStartTime());
3689 if (elapsed < tween.duration * 1000) {
3690 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3692 tweak = frames - (frame + 1);
3694 if (tweak > 0 && isFinite(tweak)) {
3695 if (tween.currentFrame + tweak >= frames) {
3696 tweak = frames - (frame + 1);
3699 tween.currentFrame += tweak;
3705 * Portions of this file are based on pieces of Yahoo User Interface Library
3706 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3707 * YUI licensed under the BSD License:
3708 * http://developer.yahoo.net/yui/license.txt
3709 * <script type="text/javascript">
3712 Roo.lib.Bezier = new function() {
3714 this.getPosition = function(points, t) {
3715 var n = points.length;
3718 for (var i = 0; i < n; ++i) {
3719 tmp[i] = [points[i][0], points[i][1]];
3722 for (var j = 1; j < n; ++j) {
3723 for (i = 0; i < n - j; ++i) {
3724 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3725 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3729 return [ tmp[0][0], tmp[0][1] ];
3735 * @class Roo.lib.Color
3737 * An abstract Color implementation. Concrete Color implementations should use
3738 * an instance of this function as their prototype, and implement the getRGB and
3739 * getHSL functions. getRGB should return an object representing the RGB
3740 * components of this Color, with the red, green, and blue components in the
3741 * range [0,255] and the alpha component in the range [0,100]. getHSL should
3742 * return an object representing the HSL components of this Color, with the hue
3743 * component in the range [0,360), the saturation and lightness components in
3744 * the range [0,100], and the alpha component in the range [0,1].
3749 * Functions for Color handling and processing.
3751 * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3753 * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3754 * rights to this program, with the intention of it becoming part of the public
3755 * domain. Because this program is released into the public domain, it comes with
3756 * no warranty either expressed or implied, to the extent permitted by law.
3758 * For more free and public domain JavaScript code by the same author, visit:
3759 * http://www.safalra.com/web-design/javascript/
3762 Roo.lib.Color = function() { }
3765 Roo.apply(Roo.lib.Color.prototype, {
3773 * @return {Object} an object representing the RGBA components of this Color. The red,
3774 * green, and blue components are converted to integers in the range [0,255].
3775 * The alpha is a value in the range [0,1].
3777 getIntegerRGB : function(){
3779 // get the RGB components of this Color
3780 var rgb = this.getRGB();
3782 // return the integer components
3784 'r' : Math.round(rgb.r),
3785 'g' : Math.round(rgb.g),
3786 'b' : Math.round(rgb.b),
3794 * @return {Object} an object representing the RGBA components of this Color. The red,
3795 * green, and blue components are converted to numbers in the range [0,100].
3796 * The alpha is a value in the range [0,1].
3798 getPercentageRGB : function(){
3800 // get the RGB components of this Color
3801 var rgb = this.getRGB();
3803 // return the percentage components
3805 'r' : 100 * rgb.r / 255,
3806 'g' : 100 * rgb.g / 255,
3807 'b' : 100 * rgb.b / 255,
3814 * getCSSHexadecimalRGB
3815 * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3816 * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3817 * are two-digit hexadecimal numbers.
3819 getCSSHexadecimalRGB : function()
3822 // get the integer RGB components
3823 var rgb = this.getIntegerRGB();
3825 // determine the hexadecimal equivalents
3826 var r16 = rgb.r.toString(16);
3827 var g16 = rgb.g.toString(16);
3828 var b16 = rgb.b.toString(16);
3830 // return the CSS RGB Color value
3832 + (r16.length == 2 ? r16 : '0' + r16)
3833 + (g16.length == 2 ? g16 : '0' + g16)
3834 + (b16.length == 2 ? b16 : '0' + b16);
3840 * @return {String} a string representing this Color as a CSS integer RGB Color
3841 * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3842 * are integers in the range [0,255].
3844 getCSSIntegerRGB : function(){
3846 // get the integer RGB components
3847 var rgb = this.getIntegerRGB();
3849 // return the CSS RGB Color value
3850 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3856 * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3857 * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3858 * b are integers in the range [0,255] and a is in the range [0,1].
3860 getCSSIntegerRGBA : function(){
3862 // get the integer RGB components
3863 var rgb = this.getIntegerRGB();
3865 // return the CSS integer RGBA Color value
3866 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3871 * getCSSPercentageRGB
3872 * @return {String} a string representing this Color as a CSS percentage RGB Color
3873 * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3874 * b are in the range [0,100].
3876 getCSSPercentageRGB : function(){
3878 // get the percentage RGB components
3879 var rgb = this.getPercentageRGB();
3881 // return the CSS RGB Color value
3882 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3887 * getCSSPercentageRGBA
3888 * @return {String} a string representing this Color as a CSS percentage RGBA Color
3889 * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3890 * and b are in the range [0,100] and a is in the range [0,1].
3892 getCSSPercentageRGBA : function(){
3894 // get the percentage RGB components
3895 var rgb = this.getPercentageRGB();
3897 // return the CSS percentage RGBA Color value
3898 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3904 * @return {String} a string representing this Color as a CSS HSL Color value - that
3905 * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3906 * s and l are in the range [0,100].
3908 getCSSHSL : function(){
3910 // get the HSL components
3911 var hsl = this.getHSL();
3913 // return the CSS HSL Color value
3914 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3920 * @return {String} a string representing this Color as a CSS HSLA Color value - that
3921 * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3922 * s and l are in the range [0,100], and a is in the range [0,1].
3924 getCSSHSLA : function(){
3926 // get the HSL components
3927 var hsl = this.getHSL();
3929 // return the CSS HSL Color value
3930 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3935 * Sets the Color of the specified node to this Color. This functions sets
3936 * the CSS 'color' property for the node. The parameter is:
3938 * @param {DomElement} node - the node whose Color should be set
3940 setNodeColor : function(node){
3942 // set the Color of the node
3943 node.style.color = this.getCSSHexadecimalRGB();
3948 * Sets the background Color of the specified node to this Color. This
3949 * functions sets the CSS 'background-color' property for the node. The
3952 * @param {DomElement} node - the node whose background Color should be set
3954 setNodeBackgroundColor : function(node){
3956 // set the background Color of the node
3957 node.style.backgroundColor = this.getCSSHexadecimalRGB();
3960 // convert between formats..
3963 var r = this.getIntegerRGB();
3964 return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3969 var hsl = this.getHSL();
3970 // return the CSS HSL Color value
3971 return new Roo.lib.HSLColor(hsl.h, hsl.s, hsl.l , hsl.a );
3977 var rgb = this.toRGB();
3978 var hsv = rgb.getHSV();
3979 // return the CSS HSL Color value
3980 return new Roo.lib.HSVColor(hsv.h, hsv.s, hsv.v , hsv.a );
3984 // modify v = 0 ... 1 (eg. 0.5)
3985 saturate : function(v)
3987 var rgb = this.toRGB();
3988 var hsv = rgb.getHSV();
3989 return new Roo.lib.HSVColor(hsv.h, hsv.s * v, hsv.v , hsv.a );
3997 * @return {Object} the RGB and alpha components of this Color as an object with r,
3998 * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
4003 // return the RGB components
4015 * @return {Object} the HSV and alpha components of this Color as an object with h,
4016 * s, v, and a properties. h is in the range [0,360), s and v are in the range
4017 * [0,100], and a is in the range [0,1].
4022 // calculate the HSV components if necessary
4023 if (this.hsv == null) {
4024 this.calculateHSV();
4027 // return the HSV components
4039 * @return {Object} the HSL and alpha components of this Color as an object with h,
4040 * s, l, and a properties. h is in the range [0,360), s and l are in the range
4041 * [0,100], and a is in the range [0,1].
4043 getHSL : function(){
4046 // calculate the HSV components if necessary
4047 if (this.hsl == null) { this.calculateHSL(); }
4049 // return the HSL components
4064 * @class Roo.lib.RGBColor
4065 * @extends Roo.lib.Color
4066 * Creates a Color specified in the RGB Color space, with an optional alpha
4067 * component. The parameters are:
4071 * @param {Number} r - the red component, clipped to the range [0,255]
4072 * @param {Number} g - the green component, clipped to the range [0,255]
4073 * @param {Number} b - the blue component, clipped to the range [0,255]
4074 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4075 * optional and defaults to 1
4077 Roo.lib.RGBColor = function (r, g, b, a){
4079 // store the alpha component after clipping it if necessary
4080 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4082 // store the RGB components after clipping them if necessary
4085 'r' : Math.max(0, Math.min(255, r)),
4086 'g' : Math.max(0, Math.min(255, g)),
4087 'b' : Math.max(0, Math.min(255, b))
4090 // initialise the HSV and HSL components to null
4094 * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4095 * range [0,360). The parameters are:
4097 * maximum - the maximum of the RGB component values
4098 * range - the range of the RGB component values
4103 // this does an 'exteds'
4104 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4107 getHue : function(maximum, range)
4111 // check whether the range is zero
4114 // set the hue to zero (any hue is acceptable as the Color is grey)
4119 // determine which of the components has the highest value and set the hue
4122 // red has the highest value
4124 var hue = (rgb.g - rgb.b) / range * 60;
4125 if (hue < 0) { hue += 360; }
4128 // green has the highest value
4130 var hue = (rgb.b - rgb.r) / range * 60 + 120;
4133 // blue has the highest value
4135 var hue = (rgb.r - rgb.g) / range * 60 + 240;
4147 /* //private Calculates and stores the HSV components of this RGBColor so that they can
4148 * be returned be the getHSV function.
4150 calculateHSV : function(){
4152 // get the maximum and range of the RGB component values
4153 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4154 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4156 // store the HSV components
4159 'h' : this.getHue(maximum, range),
4160 's' : (maximum == 0 ? 0 : 100 * range / maximum),
4161 'v' : maximum / 2.55
4166 /* //private Calculates and stores the HSL components of this RGBColor so that they can
4167 * be returned be the getHSL function.
4169 calculateHSL : function(){
4171 // get the maximum and range of the RGB component values
4172 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4173 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4175 // determine the lightness in the range [0,1]
4176 var l = maximum / 255 - range / 510;
4178 // store the HSL components
4181 'h' : this.getHue(maximum, range),
4182 's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4191 * @class Roo.lib.HSVColor
4192 * @extends Roo.lib.Color
4193 * Creates a Color specified in the HSV Color space, with an optional alpha
4194 * component. The parameters are:
4197 * @param {Number} h - the hue component, wrapped to the range [0,360)
4198 * @param {Number} s - the saturation component, clipped to the range [0,100]
4199 * @param {Number} v - the value component, clipped to the range [0,100]
4200 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4201 * optional and defaults to 1
4203 Roo.lib.HSVColor = function (h, s, v, a){
4205 // store the alpha component after clipping it if necessary
4206 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4208 // store the HSV components after clipping or wrapping them if necessary
4211 'h' : (h % 360 + 360) % 360,
4212 's' : Math.max(0, Math.min(100, s)),
4213 'v' : Math.max(0, Math.min(100, v))
4216 // initialise the RGB and HSL components to null
4221 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4222 /* Calculates and stores the RGB components of this HSVColor so that they can
4223 * be returned be the getRGB function.
4225 calculateRGB: function ()
4228 // check whether the saturation is zero
4231 // set the Color to the appropriate shade of grey
4238 // set some temporary values
4239 var f = hsv.h / 60 - Math.floor(hsv.h / 60);
4240 var p = hsv.v * (1 - hsv.s / 100);
4241 var q = hsv.v * (1 - hsv.s / 100 * f);
4242 var t = hsv.v * (1 - hsv.s / 100 * (1 - f));
4244 // set the RGB Color components to their temporary values
4245 switch (Math.floor(hsv.h / 60)){
4246 case 0: var r = hsv.v; var g = t; var b = p; break;
4247 case 1: var r = q; var g = hsv.v; var b = p; break;
4248 case 2: var r = p; var g = hsv.v; var b = t; break;
4249 case 3: var r = p; var g = q; var b = hsv.v; break;
4250 case 4: var r = t; var g = p; var b = hsv.v; break;
4251 case 5: var r = hsv.v; var g = p; var b = q; break;
4256 // store the RGB components
4266 /* Calculates and stores the HSL components of this HSVColor so that they can
4267 * be returned be the getHSL function.
4269 calculateHSL : function (){
4272 // determine the lightness in the range [0,100]
4273 var l = (2 - hsv.s / 100) * hsv.v / 2;
4275 // store the HSL components
4279 's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4283 // correct a division-by-zero error
4284 if (isNaN(hsl.s)) { hsl.s = 0; }
4293 * @class Roo.lib.HSLColor
4294 * @extends Roo.lib.Color
4297 * Creates a Color specified in the HSL Color space, with an optional alpha
4298 * component. The parameters are:
4300 * @param {Number} h - the hue component, wrapped to the range [0,360)
4301 * @param {Number} s - the saturation component, clipped to the range [0,100]
4302 * @param {Number} l - the lightness component, clipped to the range [0,100]
4303 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4304 * optional and defaults to 1
4307 Roo.lib.HSLColor = function(h, s, l, a){
4309 // store the alpha component after clipping it if necessary
4310 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4312 // store the HSL components after clipping or wrapping them if necessary
4315 'h' : (h % 360 + 360) % 360,
4316 's' : Math.max(0, Math.min(100, s)),
4317 'l' : Math.max(0, Math.min(100, l))
4320 // initialise the RGB and HSV components to null
4323 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4325 /* Calculates and stores the RGB components of this HSLColor so that they can
4326 * be returned be the getRGB function.
4328 calculateRGB: function (){
4330 // check whether the saturation is zero
4331 if (this.hsl.s == 0){
4333 // store the RGB components representing the appropriate shade of grey
4336 'r' : this.hsl.l * 2.55,
4337 'g' : this.hsl.l * 2.55,
4338 'b' : this.hsl.l * 2.55
4343 // set some temporary values
4344 var p = this.hsl.l < 50
4345 ? this.hsl.l * (1 + hsl.s / 100)
4346 : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4347 var q = 2 * hsl.l - p;
4349 // initialise the RGB components
4352 'r' : (h + 120) / 60 % 6,
4354 'b' : (h + 240) / 60 % 6
4357 // loop over the RGB components
4358 for (var key in this.rgb){
4360 // ensure that the property is not inherited from the root object
4361 if (this.rgb.hasOwnProperty(key)){
4363 // set the component to its value in the range [0,100]
4364 if (this.rgb[key] < 1){
4365 this.rgb[key] = q + (p - q) * this.rgb[key];
4366 }else if (this.rgb[key] < 3){
4368 }else if (this.rgb[key] < 4){
4369 this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4374 // set the component to its value in the range [0,255]
4375 this.rgb[key] *= 2.55;
4385 /* Calculates and stores the HSV components of this HSLColor so that they can
4386 * be returned be the getHSL function.
4388 calculateHSV : function(){
4390 // set a temporary value
4391 var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4393 // store the HSV components
4397 's' : 200 * t / (this.hsl.l + t),
4398 'v' : t + this.hsl.l
4401 // correct a division-by-zero error
4402 if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4409 * Portions of this file are based on pieces of Yahoo User Interface Library
4410 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4411 * YUI licensed under the BSD License:
4412 * http://developer.yahoo.net/yui/license.txt
4413 * <script type="text/javascript">
4418 Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4419 Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4422 Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4424 var fly = Roo.lib.AnimBase.fly;
4426 var superclass = Y.ColorAnim.superclass;
4427 var proto = Y.ColorAnim.prototype;
4429 proto.toString = function() {
4430 var el = this.getEl();
4431 var id = el.id || el.tagName;
4432 return ("ColorAnim " + id);
4435 proto.patterns.color = /color$/i;
4436 proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4437 proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4438 proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4439 proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4442 proto.parseColor = function(s) {
4443 if (s.length == 3) {
4447 var c = this.patterns.hex.exec(s);
4448 if (c && c.length == 4) {
4449 return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4452 c = this.patterns.rgb.exec(s);
4453 if (c && c.length == 4) {
4454 return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4457 c = this.patterns.hex3.exec(s);
4458 if (c && c.length == 4) {
4459 return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4464 // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4465 proto.getAttribute = function(attr) {
4466 var el = this.getEl();
4467 if (this.patterns.color.test(attr)) {
4468 var val = fly(el).getStyle(attr);
4470 if (this.patterns.transparent.test(val)) {
4471 var parent = el.parentNode;
4472 val = fly(parent).getStyle(attr);
4474 while (parent && this.patterns.transparent.test(val)) {
4475 parent = parent.parentNode;
4476 val = fly(parent).getStyle(attr);
4477 if (parent.tagName.toUpperCase() == 'HTML') {
4483 val = superclass.getAttribute.call(this, attr);
4488 proto.getAttribute = function(attr) {
4489 var el = this.getEl();
4490 if (this.patterns.color.test(attr)) {
4491 var val = fly(el).getStyle(attr);
4493 if (this.patterns.transparent.test(val)) {
4494 var parent = el.parentNode;
4495 val = fly(parent).getStyle(attr);
4497 while (parent && this.patterns.transparent.test(val)) {
4498 parent = parent.parentNode;
4499 val = fly(parent).getStyle(attr);
4500 if (parent.tagName.toUpperCase() == 'HTML') {
4506 val = superclass.getAttribute.call(this, attr);
4512 proto.doMethod = function(attr, start, end) {
4515 if (this.patterns.color.test(attr)) {
4517 for (var i = 0, len = start.length; i < len; ++i) {
4518 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4521 val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4524 val = superclass.doMethod.call(this, attr, start, end);
4530 proto.setRuntimeAttribute = function(attr) {
4531 superclass.setRuntimeAttribute.call(this, attr);
4533 if (this.patterns.color.test(attr)) {
4534 var attributes = this.attributes;
4535 var start = this.parseColor(this.runtimeAttributes[attr].start);
4536 var end = this.parseColor(this.runtimeAttributes[attr].end);
4538 if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4539 end = this.parseColor(attributes[attr].by);
4541 for (var i = 0, len = start.length; i < len; ++i) {
4542 end[i] = start[i] + end[i];
4546 this.runtimeAttributes[attr].start = start;
4547 this.runtimeAttributes[attr].end = end;
4553 * Portions of this file are based on pieces of Yahoo User Interface Library
4554 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4555 * YUI licensed under the BSD License:
4556 * http://developer.yahoo.net/yui/license.txt
4557 * <script type="text/javascript">
4563 easeNone: function (t, b, c, d) {
4564 return c * t / d + b;
4568 easeIn: function (t, b, c, d) {
4569 return c * (t /= d) * t + b;
4573 easeOut: function (t, b, c, d) {
4574 return -c * (t /= d) * (t - 2) + b;
4578 easeBoth: function (t, b, c, d) {
4579 if ((t /= d / 2) < 1) {
4580 return c / 2 * t * t + b;
4583 return -c / 2 * ((--t) * (t - 2) - 1) + b;
4587 easeInStrong: function (t, b, c, d) {
4588 return c * (t /= d) * t * t * t + b;
4592 easeOutStrong: function (t, b, c, d) {
4593 return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4597 easeBothStrong: function (t, b, c, d) {
4598 if ((t /= d / 2) < 1) {
4599 return c / 2 * t * t * t * t + b;
4602 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4607 elasticIn: function (t, b, c, d, a, p) {
4611 if ((t /= d) == 1) {
4618 if (!a || a < Math.abs(c)) {
4623 var s = p / (2 * Math.PI) * Math.asin(c / a);
4626 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4630 elasticOut: function (t, b, c, d, a, p) {
4634 if ((t /= d) == 1) {
4641 if (!a || a < Math.abs(c)) {
4646 var s = p / (2 * Math.PI) * Math.asin(c / a);
4649 return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4653 elasticBoth: function (t, b, c, d, a, p) {
4658 if ((t /= d / 2) == 2) {
4666 if (!a || a < Math.abs(c)) {
4671 var s = p / (2 * Math.PI) * Math.asin(c / a);
4675 return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4676 Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4678 return a * Math.pow(2, -10 * (t -= 1)) *
4679 Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4684 backIn: function (t, b, c, d, s) {
4685 if (typeof s == 'undefined') {
4688 return c * (t /= d) * t * ((s + 1) * t - s) + b;
4692 backOut: function (t, b, c, d, s) {
4693 if (typeof s == 'undefined') {
4696 return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4700 backBoth: function (t, b, c, d, s) {
4701 if (typeof s == 'undefined') {
4705 if ((t /= d / 2 ) < 1) {
4706 return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4708 return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4712 bounceIn: function (t, b, c, d) {
4713 return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4717 bounceOut: function (t, b, c, d) {
4718 if ((t /= d) < (1 / 2.75)) {
4719 return c * (7.5625 * t * t) + b;
4720 } else if (t < (2 / 2.75)) {
4721 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4722 } else if (t < (2.5 / 2.75)) {
4723 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4725 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4729 bounceBoth: function (t, b, c, d) {
4731 return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4733 return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4736 * Portions of this file are based on pieces of Yahoo User Interface Library
4737 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4738 * YUI licensed under the BSD License:
4739 * http://developer.yahoo.net/yui/license.txt
4740 * <script type="text/javascript">
4744 Roo.lib.Motion = function(el, attributes, duration, method) {
4746 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4750 Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4754 var superclass = Y.Motion.superclass;
4755 var proto = Y.Motion.prototype;
4757 proto.toString = function() {
4758 var el = this.getEl();
4759 var id = el.id || el.tagName;
4760 return ("Motion " + id);
4763 proto.patterns.points = /^points$/i;
4765 proto.setAttribute = function(attr, val, unit) {
4766 if (this.patterns.points.test(attr)) {
4767 unit = unit || 'px';
4768 superclass.setAttribute.call(this, 'left', val[0], unit);
4769 superclass.setAttribute.call(this, 'top', val[1], unit);
4771 superclass.setAttribute.call(this, attr, val, unit);
4775 proto.getAttribute = function(attr) {
4776 if (this.patterns.points.test(attr)) {
4778 superclass.getAttribute.call(this, 'left'),
4779 superclass.getAttribute.call(this, 'top')
4782 val = superclass.getAttribute.call(this, attr);
4788 proto.doMethod = function(attr, start, end) {
4791 if (this.patterns.points.test(attr)) {
4792 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4793 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4795 val = superclass.doMethod.call(this, attr, start, end);
4800 proto.setRuntimeAttribute = function(attr) {
4801 if (this.patterns.points.test(attr)) {
4802 var el = this.getEl();
4803 var attributes = this.attributes;
4805 var control = attributes['points']['control'] || [];
4809 if (control.length > 0 && !(control[0] instanceof Array)) {
4810 control = [control];
4813 for (i = 0,len = control.length; i < len; ++i) {
4814 tmp[i] = control[i];
4819 Roo.fly(el).position();
4821 if (isset(attributes['points']['from'])) {
4822 Roo.lib.Dom.setXY(el, attributes['points']['from']);
4825 Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4828 start = this.getAttribute('points');
4831 if (isset(attributes['points']['to'])) {
4832 end = translateValues.call(this, attributes['points']['to'], start);
4834 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4835 for (i = 0,len = control.length; i < len; ++i) {
4836 control[i] = translateValues.call(this, control[i], start);
4840 } else if (isset(attributes['points']['by'])) {
4841 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4843 for (i = 0,len = control.length; i < len; ++i) {
4844 control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4848 this.runtimeAttributes[attr] = [start];
4850 if (control.length > 0) {
4851 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4854 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4857 superclass.setRuntimeAttribute.call(this, attr);
4861 var translateValues = function(val, start) {
4862 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4863 val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4868 var isset = function(prop) {
4869 return (typeof prop !== 'undefined');
4873 * Portions of this file are based on pieces of Yahoo User Interface Library
4874 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4875 * YUI licensed under the BSD License:
4876 * http://developer.yahoo.net/yui/license.txt
4877 * <script type="text/javascript">
4881 Roo.lib.Scroll = function(el, attributes, duration, method) {
4883 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4887 Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4891 var superclass = Y.Scroll.superclass;
4892 var proto = Y.Scroll.prototype;
4894 proto.toString = function() {
4895 var el = this.getEl();
4896 var id = el.id || el.tagName;
4897 return ("Scroll " + id);
4900 proto.doMethod = function(attr, start, end) {
4903 if (attr == 'scroll') {
4905 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4906 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4910 val = superclass.doMethod.call(this, attr, start, end);
4915 proto.getAttribute = function(attr) {
4917 var el = this.getEl();
4919 if (attr == 'scroll') {
4920 val = [ el.scrollLeft, el.scrollTop ];
4922 val = superclass.getAttribute.call(this, attr);
4928 proto.setAttribute = function(attr, val, unit) {
4929 var el = this.getEl();
4931 if (attr == 'scroll') {
4932 el.scrollLeft = val[0];
4933 el.scrollTop = val[1];
4935 superclass.setAttribute.call(this, attr, val, unit);
4940 * Originally based of this code... - refactored for Roo...
4941 * https://github.com/aaalsaleh/undo-manager
4944 * @author Abdulrahman Alsaleh
4945 * @copyright 2015 Abdulrahman Alsaleh
4946 * @license MIT License (c)
4948 * Hackily modifyed by alan@roojs.com
4953 * TOTALLY UNTESTED...
4955 * Documentation to be done....
4960 * @class Roo.lib.UndoManager
4961 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4962 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4968 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4972 * For more information see this blog post with examples:
4973 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4974 - Create Elements using DOM, HTML fragments and Templates</a>.
4976 * @param {Number} limit how far back to go ... use 1000?
4977 * @param {Object} scope usually use document..
4980 Roo.lib.UndoManager = function (limit, undoScopeHost)
4984 this.scope = undoScopeHost;
4985 this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4986 if (this.fireEvent) {
4993 Roo.lib.UndoManager.prototype = {
5004 * To push and execute a transaction, the method undoManager.transact
5005 * must be called by passing a transaction object as the first argument, and a merge
5006 * flag as the second argument. A transaction object has the following properties:
5010 undoManager.transact({
5012 execute: function() { ... },
5013 undo: function() { ... },
5014 // redo same as execute
5015 redo: function() { this.execute(); }
5018 // merge transaction
5019 undoManager.transact({
5021 execute: function() { ... }, // this will be run...
5022 undo: function() { ... }, // what to do when undo is run.
5023 // redo same as execute
5024 redo: function() { this.execute(); }
5029 * @param {Object} transaction The transaction to add to the stack.
5030 * @return {String} The HTML fragment
5034 transact : function (transaction, merge)
5036 if (arguments.length < 2) {
5037 throw new TypeError('Not enough arguments to UndoManager.transact.');
5040 transaction.execute();
5042 this.stack.splice(0, this.position);
5043 if (merge && this.length) {
5044 this.stack[0].push(transaction);
5046 this.stack.unshift([transaction]);
5051 if (this.limit && this.stack.length > this.limit) {
5052 this.length = this.stack.length = this.limit;
5054 this.length = this.stack.length;
5057 if (this.fireEvent) {
5058 this.scope.dispatchEvent(
5059 new CustomEvent('DOMTransaction', {
5061 transactions: this.stack[0].slice()
5069 //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5076 //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5078 if (this.position < this.length) {
5079 for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5080 this.stack[this.position][i].undo();
5084 if (this.fireEvent) {
5085 this.scope.dispatchEvent(
5086 new CustomEvent('undo', {
5088 transactions: this.stack[this.position - 1].slice()
5100 if (this.position > 0) {
5101 for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5102 this.stack[this.position - 1][i].redo();
5106 if (this.fireEvent) {
5107 this.scope.dispatchEvent(
5108 new CustomEvent('redo', {
5110 transactions: this.stack[this.position].slice()
5120 item : function (index)
5122 if (index >= 0 && index < this.length) {
5123 return this.stack[index].slice();
5128 clearUndo : function () {
5129 this.stack.length = this.length = this.position;
5132 clearRedo : function () {
5133 this.stack.splice(0, this.position);
5135 this.length = this.stack.length;
5138 * Reset the undo - probaly done on load to clear all history.
5145 this.current_html = this.scope.innerHTML;
5146 if (this.timer !== false) {
5147 clearTimeout(this.timer);
5159 // this will handle the undo/redo on the element.?
5160 bindEvents : function()
5162 var el = this.scope;
5163 el.undoManager = this;
5166 this.scope.addEventListener('keydown', function(e) {
5167 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5169 el.undoManager.redo(); // Ctrl/Command + Shift + Z
5171 el.undoManager.undo(); // Ctrl/Command + Z
5178 this.scope.addEventListener('keyup', function(e) {
5179 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5188 el.addEventListener('input', function(e) {
5189 if(el.innerHTML == t.current_html) {
5192 // only record events every second.
5193 if (t.timer !== false) {
5194 clearTimeout(t.timer);
5197 t.timer = setTimeout(function() { t.merge = false; }, 1000);
5199 t.addEvent(t.merge);
5200 t.merge = true; // ignore changes happening every second..
5204 * Manually add an event.
5205 * Normall called without arguements - and it will just get added to the stack.
5209 addEvent : function(merge)
5211 //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5212 // not sure if this should clear the timer
5213 merge = typeof(merge) == 'undefined' ? false : merge;
5215 this.scope.undoManager.transact({
5217 oldHTML: this.current_html,
5218 newHTML: this.scope.innerHTML,
5219 // nothing to execute (content already changed when input is fired)
5220 execute: function() { },
5222 this.scope.innerHTML = this.current_html = this.oldHTML;
5225 this.scope.innerHTML = this.current_html = this.newHTML;
5227 }, false); //merge);
5231 this.current_html = this.scope.innerHTML;
5241 * @class Roo.lib.Range
5243 * This is a toolkit, normally used to copy features into a Dom Range element
5244 * Roo.lib.Range.wrap(x);
5249 Roo.lib.Range = function() { };
5252 * Wrap a Dom Range object, to give it new features...
5254 * @param {Range} the range to wrap
5256 Roo.lib.Range.wrap = function(r) {
5257 return Roo.apply(r, Roo.lib.Range.prototype);
5260 * find a parent node eg. LI / OL
5261 * @param {string|Array} node name or array of nodenames
5262 * @return {DomElement|false}
5264 Roo.apply(Roo.lib.Range.prototype,
5267 closest : function(str)
5269 if (typeof(str) != 'string') {
5270 // assume it's a array.
5271 for(var i = 0;i < str.length;i++) {
5272 var r = this.closest(str[i]);
5280 str = str.toLowerCase();
5281 var n = this.commonAncestorContainer; // might not be a node
5282 while (n.nodeType != 1) {
5286 if (n.nodeName.toLowerCase() == str ) {
5289 if (n.nodeName.toLowerCase() == 'body') {
5293 return n.closest(str) || false;
5296 cloneRange : function()
5298 return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5301 * @class Roo.lib.Selection
5303 * This is a toolkit, normally used to copy features into a Dom Selection element
5304 * Roo.lib.Selection.wrap(x);
5309 Roo.lib.Selection = function() { };
5312 * Wrap a Dom Range object, to give it new features...
5314 * @param {Range} the range to wrap
5316 Roo.lib.Selection.wrap = function(r, doc) {
5317 Roo.apply(r, Roo.lib.Selection.prototype);
5318 r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5322 * find a parent node eg. LI / OL
5323 * @param {string|Array} node name or array of nodenames
5324 * @return {DomElement|false}
5326 Roo.apply(Roo.lib.Selection.prototype,
5329 * the owner document
5331 ownerDocument : false,
5333 getRangeAt : function(n)
5335 return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5339 * insert node at selection
5340 * @param {DomElement|string} node
5341 * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5343 insertNode: function(node, cursor)
5345 if (typeof(node) == 'string') {
5346 node = this.ownerDocument.createElement(node);
5347 if (cursor == 'in') {
5348 node.innerHTML = ' ';
5352 var range = this.getRangeAt(0);
5354 if (this.type != 'Caret') {
5355 range.deleteContents();
5357 var sn = node.childNodes[0]; // select the contents.
5361 range.insertNode(node);
5362 if (cursor == 'after') {
5363 node.insertAdjacentHTML('afterend', ' ');
5364 sn = node.nextSibling;
5367 if (cursor == 'none') {
5371 this.cursorText(sn);
5374 cursorText : function(n)
5377 //var range = this.getRangeAt(0);
5378 range = Roo.lib.Range.wrap(new Range());
5379 //range.selectNode(n);
5381 var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5382 range.setStart(n.parentNode,ix);
5383 range.setEnd(n.parentNode,ix+1);
5384 //range.collapse(false);
5386 this.removeAllRanges();
5387 this.addRange(range);
5389 Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5391 cursorAfter : function(n)
5393 if (!n.nextSibling || n.nextSibling.nodeValue != ' ') {
5394 n.insertAdjacentHTML('afterend', ' ');
5396 this.cursorText (n.nextSibling);
5402 * Ext JS Library 1.1.1
5403 * Copyright(c) 2006-2007, Ext JS, LLC.
5405 * Originally Released Under LGPL - original licence link has changed is not relivant.
5408 * <script type="text/javascript">
5412 // nasty IE9 hack - what a pile of crap that is..
5414 if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5415 Range.prototype.createContextualFragment = function (html) {
5416 var doc = window.document;
5417 var container = doc.createElement("div");
5418 container.innerHTML = html;
5419 var frag = doc.createDocumentFragment(), n;
5420 while ((n = container.firstChild)) {
5421 frag.appendChild(n);
5428 * @class Roo.DomHelper
5429 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5430 * 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>.
5433 Roo.DomHelper = function(){
5434 var tempTableEl = null;
5435 var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5436 var tableRe = /^table|tbody|tr|td$/i;
5438 // build as innerHTML where available
5440 var createHtml = function(o){
5441 if(typeof o == 'string'){
5450 if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5451 if(attr == "style"){
5453 if(typeof s == "function"){
5456 if(typeof s == "string"){
5457 b += ' style="' + s + '"';
5458 }else if(typeof s == "object"){
5461 if(typeof s[key] != "function"){
5462 b += key + ":" + s[key] + ";";
5469 b += ' class="' + o["cls"] + '"';
5470 }else if(attr == "htmlFor"){
5471 b += ' for="' + o["htmlFor"] + '"';
5473 b += " " + attr + '="' + o[attr] + '"';
5477 if(emptyTags.test(o.tag)){
5481 var cn = o.children || o.cn;
5483 //http://bugs.kde.org/show_bug.cgi?id=71506
5484 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5485 for(var i = 0, len = cn.length; i < len; i++) {
5486 b += createHtml(cn[i], b);
5489 b += createHtml(cn, b);
5495 b += "</" + o.tag + ">";
5502 var createDom = function(o, parentNode){
5504 // defininition craeted..
5506 if (o.ns && o.ns != 'html') {
5508 if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5509 xmlns[o.ns] = o.xmlns;
5512 if (typeof(xmlns[o.ns]) == 'undefined') {
5513 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5519 if (typeof(o) == 'string') {
5520 return parentNode.appendChild(document.createTextNode(o));
5522 o.tag = o.tag || 'div';
5523 if (o.ns && Roo.isIE) {
5525 o.tag = o.ns + ':' + o.tag;
5528 var el = ns ? document.createElementNS( ns, o.tag||'div') : document.createElement(o.tag||'div');
5529 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5532 if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" ||
5533 attr == "style" || typeof o[attr] == "function") { continue; }
5535 if(attr=="cls" && Roo.isIE){
5536 el.className = o["cls"];
5538 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5544 Roo.DomHelper.applyStyles(el, o.style);
5545 var cn = o.children || o.cn;
5547 //http://bugs.kde.org/show_bug.cgi?id=71506
5548 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5549 for(var i = 0, len = cn.length; i < len; i++) {
5550 createDom(cn[i], el);
5557 el.innerHTML = o.html;
5560 parentNode.appendChild(el);
5565 var ieTable = function(depth, s, h, e){
5566 tempTableEl.innerHTML = [s, h, e].join('');
5567 var i = -1, el = tempTableEl;
5568 while(++i < depth && el.firstChild){
5574 // kill repeat to save bytes
5578 tbe = '</tbody>'+te,
5584 * Nasty code for IE's broken table implementation
5586 var insertIntoTable = function(tag, where, el, html){
5588 tempTableEl = document.createElement('div');
5593 if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5596 if(where == 'beforebegin'){
5600 before = el.nextSibling;
5603 node = ieTable(4, trs, html, tre);
5605 else if(tag == 'tr'){
5606 if(where == 'beforebegin'){
5609 node = ieTable(3, tbs, html, tbe);
5610 } else if(where == 'afterend'){
5611 before = el.nextSibling;
5613 node = ieTable(3, tbs, html, tbe);
5614 } else{ // INTO a TR
5615 if(where == 'afterbegin'){
5616 before = el.firstChild;
5618 node = ieTable(4, trs, html, tre);
5620 } else if(tag == 'tbody'){
5621 if(where == 'beforebegin'){
5624 node = ieTable(2, ts, html, te);
5625 } else if(where == 'afterend'){
5626 before = el.nextSibling;
5628 node = ieTable(2, ts, html, te);
5630 if(where == 'afterbegin'){
5631 before = el.firstChild;
5633 node = ieTable(3, tbs, html, tbe);
5636 if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5639 if(where == 'afterbegin'){
5640 before = el.firstChild;
5642 node = ieTable(2, ts, html, te);
5644 el.insertBefore(node, before);
5648 // this is a bit like the react update code...
5651 var updateNode = function(from, to)
5653 // should we handle non-standard elements?
5654 Roo.log(["UpdateNode" , from, to]);
5655 if (from.nodeType != to.nodeType) {
5656 Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5657 from.parentNode.replaceChild(to, from);
5660 if (from.nodeType == 3) {
5661 // assume it's text?!
5662 if (from.data == to.data) {
5665 from.data = to.data;
5668 if (!from.parentNode) {
5669 // not sure why this is happening?
5672 // assume 'to' doesnt have '1/3 nodetypes!
5673 // not sure why, by from, parent node might not exist?
5674 if (from.nodeType !=1 || from.tagName != to.tagName) {
5675 Roo.log(["ReplaceChild" , from, to ]);
5677 from.parentNode.replaceChild(to, from);
5680 // compare attributes
5681 var ar = Array.from(from.attributes);
5682 for(var i = 0; i< ar.length;i++) {
5683 if (to.hasAttribute(ar[i].name)) {
5686 if (ar[i].name == 'id') { // always keep ids?
5689 //if (ar[i].name == 'style') {
5690 // throw "style removed?";
5692 Roo.log("removeAttribute" + ar[i].name);
5693 from.removeAttribute(ar[i].name);
5696 for(var i = 0; i< ar.length;i++) {
5697 if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5698 Roo.log("skipAttribute " + ar[i].name + '=' + to.getAttribute(ar[i].name));
5701 Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5702 from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5705 var far = Array.from(from.childNodes);
5706 var tar = Array.from(to.childNodes);
5707 // if the lengths are different.. then it's probably a editable content change, rather than
5708 // a change of the block definition..
5710 // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5711 /*if (from.innerHTML == to.innerHTML) {
5714 if (far.length != tar.length) {
5715 from.innerHTML = to.innerHTML;
5720 for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5721 if (i >= far.length) {
5722 from.appendChild(tar[i]);
5723 Roo.log(["add", tar[i]]);
5725 } else if ( i >= tar.length) {
5726 from.removeChild(far[i]);
5727 Roo.log(["remove", far[i]]);
5730 updateNode(far[i], tar[i]);
5742 /** True to force the use of DOM instead of html fragments @type Boolean */
5746 * Returns the markup for the passed Element(s) config
5747 * @param {Object} o The Dom object spec (and children)
5750 markup : function(o){
5751 return createHtml(o);
5755 * Applies a style specification to an element
5756 * @param {String/HTMLElement} el The element to apply styles to
5757 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5758 * a function which returns such a specification.
5760 applyStyles : function(el, styles){
5763 if(typeof styles == "string"){
5764 var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5766 while ((matches = re.exec(styles)) != null){
5767 el.setStyle(matches[1], matches[2]);
5769 }else if (typeof styles == "object"){
5770 for (var style in styles){
5771 el.setStyle(style, styles[style]);
5773 }else if (typeof styles == "function"){
5774 Roo.DomHelper.applyStyles(el, styles.call());
5780 * Inserts an HTML fragment into the Dom
5781 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5782 * @param {HTMLElement} el The context element
5783 * @param {String} html The HTML fragmenet
5784 * @return {HTMLElement} The new node
5786 insertHtml : function(where, el, html){
5787 where = where.toLowerCase();
5788 if(el.insertAdjacentHTML){
5789 if(tableRe.test(el.tagName)){
5791 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5797 el.insertAdjacentHTML('BeforeBegin', html);
5798 return el.previousSibling;
5800 el.insertAdjacentHTML('AfterBegin', html);
5801 return el.firstChild;
5803 el.insertAdjacentHTML('BeforeEnd', html);
5804 return el.lastChild;
5806 el.insertAdjacentHTML('AfterEnd', html);
5807 return el.nextSibling;
5809 throw 'Illegal insertion point -> "' + where + '"';
5811 var range = el.ownerDocument.createRange();
5815 range.setStartBefore(el);
5816 frag = range.createContextualFragment(html);
5817 el.parentNode.insertBefore(frag, el);
5818 return el.previousSibling;
5821 range.setStartBefore(el.firstChild);
5822 frag = range.createContextualFragment(html);
5823 el.insertBefore(frag, el.firstChild);
5824 return el.firstChild;
5826 el.innerHTML = html;
5827 return el.firstChild;
5831 range.setStartAfter(el.lastChild);
5832 frag = range.createContextualFragment(html);
5833 el.appendChild(frag);
5834 return el.lastChild;
5836 el.innerHTML = html;
5837 return el.lastChild;
5840 range.setStartAfter(el);
5841 frag = range.createContextualFragment(html);
5842 el.parentNode.insertBefore(frag, el.nextSibling);
5843 return el.nextSibling;
5845 throw 'Illegal insertion point -> "' + where + '"';
5849 * Creates new Dom element(s) and inserts them before el
5850 * @param {String/HTMLElement/Element} el The context element
5851 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5852 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5853 * @return {HTMLElement/Roo.Element} The new node
5855 insertBefore : function(el, o, returnElement){
5856 return this.doInsert(el, o, returnElement, "beforeBegin");
5860 * Creates new Dom element(s) and inserts them after el
5861 * @param {String/HTMLElement/Element} el The context element
5862 * @param {Object} o The Dom object spec (and children)
5863 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5864 * @return {HTMLElement/Roo.Element} The new node
5866 insertAfter : function(el, o, returnElement){
5867 return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5871 * Creates new Dom element(s) and inserts them as the first child of el
5872 * @param {String/HTMLElement/Element} el The context element
5873 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5874 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5875 * @return {HTMLElement/Roo.Element} The new node
5877 insertFirst : function(el, o, returnElement){
5878 return this.doInsert(el, o, returnElement, "afterBegin");
5882 doInsert : function(el, o, returnElement, pos, sibling){
5883 el = Roo.getDom(el);
5885 if(this.useDom || o.ns){
5886 newNode = createDom(o, null);
5887 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5889 var html = createHtml(o);
5890 newNode = this.insertHtml(pos, el, html);
5892 return returnElement ? Roo.get(newNode, true) : newNode;
5896 * Creates new Dom element(s) and appends them to el
5897 * @param {String/HTMLElement/Element} el The context element
5898 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5899 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5900 * @return {HTMLElement/Roo.Element} The new node
5902 append : function(el, o, returnElement){
5903 el = Roo.getDom(el);
5905 if(this.useDom || o.ns){
5906 newNode = createDom(o, null);
5907 el.appendChild(newNode);
5909 var html = createHtml(o);
5910 newNode = this.insertHtml("beforeEnd", el, html);
5912 return returnElement ? Roo.get(newNode, true) : newNode;
5916 * Creates new Dom element(s) and overwrites the contents of el with them
5917 * @param {String/HTMLElement/Element} el The context element
5918 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5919 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5920 * @return {HTMLElement/Roo.Element} The new node
5922 overwrite : function(el, o, returnElement)
5924 el = Roo.getDom(el);
5927 while (el.childNodes.length) {
5928 el.removeChild(el.firstChild);
5932 el.innerHTML = createHtml(o);
5935 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5939 * Creates a new Roo.DomHelper.Template from the Dom object spec
5940 * @param {Object} o The Dom object spec (and children)
5941 * @return {Roo.DomHelper.Template} The new template
5943 createTemplate : function(o){
5944 var html = createHtml(o);
5945 return new Roo.Template(html);
5948 * Updates the first element with the spec from the o (replacing if necessary)
5949 * This iterates through the children, and updates attributes / children etc..
5950 * @param {String/HTMLElement/Element} el The context element
5951 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5954 update : function(el, o)
5956 updateNode(Roo.getDom(el), createDom(o));
5965 * Ext JS Library 1.1.1
5966 * Copyright(c) 2006-2007, Ext JS, LLC.
5968 * Originally Released Under LGPL - original licence link has changed is not relivant.
5971 * <script type="text/javascript">
5975 * @class Roo.Template
5976 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5977 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5980 var t = new Roo.Template({
5981 html : '<div name="{id}">' +
5982 '<span class="{cls}">{name:trim} {someval:this.myformat}{value:ellipsis(10)}</span>' +
5984 myformat: function (value, allValues) {
5985 return 'XX' + value;
5988 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5990 * For more information see this blog post with examples:
5991 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5992 - Create Elements using DOM, HTML fragments and Templates</a>.
5994 * @param {Object} cfg - Configuration object.
5996 Roo.Template = function(cfg){
5998 if(cfg instanceof Array){
6000 }else if(arguments.length > 1){
6001 cfg = Array.prototype.join.call(arguments, "");
6005 if (typeof(cfg) == 'object') {
6016 Roo.Template.prototype = {
6019 * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6025 * @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..
6026 * it should be fixed so that template is observable...
6030 * @cfg {String} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6038 * Returns an HTML fragment of this template with the specified values applied.
6039 * @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'})
6040 * @return {String} The HTML fragment
6045 applyTemplate : function(values){
6046 //Roo.log(["applyTemplate", values]);
6050 return this.compiled(values);
6052 var useF = this.disableFormats !== true;
6053 var fm = Roo.util.Format, tpl = this;
6054 var fn = function(m, name, format, args){
6056 if(format.substr(0, 5) == "this."){
6057 return tpl.call(format.substr(5), values[name], values);
6060 // quoted values are required for strings in compiled templates,
6061 // but for non compiled we need to strip them
6062 // quoted reversed for jsmin
6063 var re = /^\s*['"](.*)["']\s*$/;
6064 args = args.split(',');
6065 for(var i = 0, len = args.length; i < len; i++){
6066 args[i] = args[i].replace(re, "$1");
6068 args = [values[name]].concat(args);
6070 args = [values[name]];
6072 return fm[format].apply(fm, args);
6075 return values[name] !== undefined ? values[name] : "";
6078 return this.html.replace(this.re, fn);
6096 this.loading = true;
6097 this.compiled = false;
6099 var cx = new Roo.data.Connection();
6103 success : function (response) {
6107 _t.set(response.responseText,true);
6113 failure : function(response) {
6114 Roo.log("Template failed to load from " + _t.url);
6121 * Sets the HTML used as the template and optionally compiles it.
6122 * @param {String} html
6123 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6124 * @return {Roo.Template} this
6126 set : function(html, compile){
6128 this.compiled = false;
6136 * True to disable format functions (defaults to false)
6139 disableFormats : false,
6142 * The regular expression used to match template variables
6146 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6149 * Compiles the template into an internal function, eliminating the RegEx overhead.
6150 * @return {Roo.Template} this
6152 compile : function(){
6153 var fm = Roo.util.Format;
6154 var useF = this.disableFormats !== true;
6155 var sep = Roo.isGecko ? "+" : ",";
6156 var fn = function(m, name, format, args){
6158 args = args ? ',' + args : "";
6159 if(format.substr(0, 5) != "this."){
6160 format = "fm." + format + '(';
6162 format = 'this.call("'+ format.substr(5) + '", ';
6166 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6168 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6171 // branched to use + in gecko and [].join() in others
6173 body = "this.compiled = function(values){ return '" +
6174 this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6177 body = ["this.compiled = function(values){ return ['"];
6178 body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6179 body.push("'].join('');};");
6180 body = body.join('');
6190 // private function used to call members
6191 call : function(fnName, value, allValues){
6192 return this[fnName](value, allValues);
6196 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6197 * @param {String/HTMLElement/Roo.Element} el The context element
6198 * @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'})
6199 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6200 * @return {HTMLElement/Roo.Element} The new node or Element
6202 insertFirst: function(el, values, returnElement){
6203 return this.doInsert('afterBegin', el, values, returnElement);
6207 * Applies the supplied values to the template and inserts the new node(s) before el.
6208 * @param {String/HTMLElement/Roo.Element} el The context element
6209 * @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'})
6210 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6211 * @return {HTMLElement/Roo.Element} The new node or Element
6213 insertBefore: function(el, values, returnElement){
6214 return this.doInsert('beforeBegin', el, values, returnElement);
6218 * Applies the supplied values to the template and inserts the new node(s) after el.
6219 * @param {String/HTMLElement/Roo.Element} el The context element
6220 * @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'})
6221 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6222 * @return {HTMLElement/Roo.Element} The new node or Element
6224 insertAfter : function(el, values, returnElement){
6225 return this.doInsert('afterEnd', el, values, returnElement);
6229 * Applies the supplied values to the template and appends the new node(s) to el.
6230 * @param {String/HTMLElement/Roo.Element} el The context element
6231 * @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'})
6232 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6233 * @return {HTMLElement/Roo.Element} The new node or Element
6235 append : function(el, values, returnElement){
6236 return this.doInsert('beforeEnd', el, values, returnElement);
6239 doInsert : function(where, el, values, returnEl){
6240 el = Roo.getDom(el);
6241 var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6242 return returnEl ? Roo.get(newNode, true) : newNode;
6246 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6247 * @param {String/HTMLElement/Roo.Element} el The context element
6248 * @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'})
6249 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6250 * @return {HTMLElement/Roo.Element} The new node or Element
6252 overwrite : function(el, values, returnElement){
6253 el = Roo.getDom(el);
6254 el.innerHTML = this.applyTemplate(values);
6255 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6259 * Alias for {@link #applyTemplate}
6262 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6265 Roo.DomHelper.Template = Roo.Template;
6268 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6269 * @param {String/HTMLElement} el A DOM element or its id
6270 * @returns {Roo.Template} The created template
6273 Roo.Template.from = function(el){
6274 el = Roo.getDom(el);
6275 return new Roo.Template(el.value || el.innerHTML);
6278 * Ext JS Library 1.1.1
6279 * Copyright(c) 2006-2007, Ext JS, LLC.
6281 * Originally Released Under LGPL - original licence link has changed is not relivant.
6284 * <script type="text/javascript">
6289 * This is code is also distributed under MIT license for use
6290 * with jQuery and prototype JavaScript libraries.
6293 * @class Roo.DomQuery
6294 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).
6296 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>
6299 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.
6301 <h4>Element Selectors:</h4>
6303 <li> <b>*</b> any element</li>
6304 <li> <b>E</b> an element with the tag E</li>
6305 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6306 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6307 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6308 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6310 <h4>Attribute Selectors:</h4>
6311 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6313 <li> <b>E[foo]</b> has an attribute "foo"</li>
6314 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6315 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6316 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6317 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6318 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6319 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6321 <h4>Pseudo Classes:</h4>
6323 <li> <b>E:first-child</b> E is the first child of its parent</li>
6324 <li> <b>E:last-child</b> E is the last child of its parent</li>
6325 <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>
6326 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6327 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6328 <li> <b>E:only-child</b> E is the only child of its parent</li>
6329 <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>
6330 <li> <b>E:first</b> the first E in the resultset</li>
6331 <li> <b>E:last</b> the last E in the resultset</li>
6332 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6333 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6334 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6335 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6336 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6337 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6338 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6339 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6340 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6342 <h4>CSS Value Selectors:</h4>
6344 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6345 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6346 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6347 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6348 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6349 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6353 Roo.DomQuery = function(){
6354 var cache = {}, simpleCache = {}, valueCache = {};
6355 var nonSpace = /\S/;
6356 var trimRe = /^\s+|\s+$/g;
6357 var tplRe = /\{(\d+)\}/g;
6358 var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6359 var tagTokenRe = /^(#)?([\w-\*]+)/;
6360 var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6362 function child(p, index){
6364 var n = p.firstChild;
6366 if(n.nodeType == 1){
6377 while((n = n.nextSibling) && n.nodeType != 1);
6382 while((n = n.previousSibling) && n.nodeType != 1);
6386 function children(d){
6387 var n = d.firstChild, ni = -1;
6389 var nx = n.nextSibling;
6390 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6400 function byClassName(c, a, v){
6404 var r = [], ri = -1, cn;
6405 for(var i = 0, ci; ci = c[i]; i++){
6409 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6410 +' ').indexOf(v) != -1){
6417 function attrValue(n, attr){
6418 if(!n.tagName && typeof n.length != "undefined"){
6427 if(attr == "class" || attr == "className"){
6428 return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6430 return n.getAttribute(attr) || n[attr];
6434 function getNodes(ns, mode, tagName){
6435 var result = [], ri = -1, cs;
6439 tagName = tagName || "*";
6440 if(typeof ns.getElementsByTagName != "undefined"){
6444 for(var i = 0, ni; ni = ns[i]; i++){
6445 cs = ni.getElementsByTagName(tagName);
6446 for(var j = 0, ci; ci = cs[j]; j++){
6450 }else if(mode == "/" || mode == ">"){
6451 var utag = tagName.toUpperCase();
6452 for(var i = 0, ni, cn; ni = ns[i]; i++){
6453 cn = ni.children || ni.childNodes;
6454 for(var j = 0, cj; cj = cn[j]; j++){
6455 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
6460 }else if(mode == "+"){
6461 var utag = tagName.toUpperCase();
6462 for(var i = 0, n; n = ns[i]; i++){
6463 while((n = n.nextSibling) && n.nodeType != 1);
6464 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6468 }else if(mode == "~"){
6469 for(var i = 0, n; n = ns[i]; i++){
6470 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6479 function concat(a, b){
6483 for(var i = 0, l = b.length; i < l; i++){
6489 function byTag(cs, tagName){
6490 if(cs.tagName || cs == document){
6496 var r = [], ri = -1;
6497 tagName = tagName.toLowerCase();
6498 for(var i = 0, ci; ci = cs[i]; i++){
6499 if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6506 function byId(cs, attr, id){
6507 if(cs.tagName || cs == document){
6513 var r = [], ri = -1;
6514 for(var i = 0,ci; ci = cs[i]; i++){
6515 if(ci && ci.id == id){
6523 function byAttribute(cs, attr, value, op, custom){
6524 var r = [], ri = -1, st = custom=="{";
6525 var f = Roo.DomQuery.operators[op];
6526 for(var i = 0, ci; ci = cs[i]; i++){
6529 a = Roo.DomQuery.getStyle(ci, attr);
6531 else if(attr == "class" || attr == "className"){
6532 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6533 }else if(attr == "for"){
6535 }else if(attr == "href"){
6536 a = ci.getAttribute("href", 2);
6538 a = ci.getAttribute(attr);
6540 if((f && f(a, value)) || (!f && a)){
6547 function byPseudo(cs, name, value){
6548 return Roo.DomQuery.pseudos[name](cs, value);
6551 // This is for IE MSXML which does not support expandos.
6552 // IE runs the same speed using setAttribute, however FF slows way down
6553 // and Safari completely fails so they need to continue to use expandos.
6554 var isIE = window.ActiveXObject ? true : false;
6556 // this eval is stop the compressor from
6557 // renaming the variable to something shorter
6559 /** eval:var:batch */
6564 function nodupIEXml(cs){
6566 cs[0].setAttribute("_nodup", d);
6568 for(var i = 1, len = cs.length; i < len; i++){
6570 if(!c.getAttribute("_nodup") != d){
6571 c.setAttribute("_nodup", d);
6575 for(var i = 0, len = cs.length; i < len; i++){
6576 cs[i].removeAttribute("_nodup");
6585 var len = cs.length, c, i, r = cs, cj, ri = -1;
6586 if(!len || typeof cs.nodeType != "undefined" || len == 1){
6589 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6590 return nodupIEXml(cs);
6594 for(i = 1; c = cs[i]; i++){
6599 for(var j = 0; j < i; j++){
6602 for(j = i+1; cj = cs[j]; j++){
6614 function quickDiffIEXml(c1, c2){
6616 for(var i = 0, len = c1.length; i < len; i++){
6617 c1[i].setAttribute("_qdiff", d);
6620 for(var i = 0, len = c2.length; i < len; i++){
6621 if(c2[i].getAttribute("_qdiff") != d){
6622 r[r.length] = c2[i];
6625 for(var i = 0, len = c1.length; i < len; i++){
6626 c1[i].removeAttribute("_qdiff");
6631 function quickDiff(c1, c2){
6632 var len1 = c1.length;
6636 if(isIE && c1[0].selectSingleNode){
6637 return quickDiffIEXml(c1, c2);
6640 for(var i = 0; i < len1; i++){
6644 for(var i = 0, len = c2.length; i < len; i++){
6645 if(c2[i]._qdiff != d){
6646 r[r.length] = c2[i];
6652 function quickId(ns, mode, root, id){
6654 var d = root.ownerDocument || root;
6655 return d.getElementById(id);
6657 ns = getNodes(ns, mode, "*");
6658 return byId(ns, null, id);
6662 getStyle : function(el, name){
6663 return Roo.fly(el).getStyle(name);
6666 * Compiles a selector/xpath query into a reusable function. The returned function
6667 * takes one parameter "root" (optional), which is the context node from where the query should start.
6668 * @param {String} selector The selector/xpath query
6669 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6670 * @return {Function}
6672 compile : function(path, type){
6673 type = type || "select";
6675 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6676 var q = path, mode, lq;
6677 var tk = Roo.DomQuery.matchers;
6678 var tklen = tk.length;
6681 // accept leading mode switch
6682 var lmode = q.match(modeRe);
6683 if(lmode && lmode[1]){
6684 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6685 q = q.replace(lmode[1], "");
6687 // strip leading slashes
6688 while(path.substr(0, 1)=="/"){
6689 path = path.substr(1);
6692 while(q && lq != q){
6694 var tm = q.match(tagTokenRe);
6695 if(type == "select"){
6698 fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6700 fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6702 q = q.replace(tm[0], "");
6703 }else if(q.substr(0, 1) != '@'){
6704 fn[fn.length] = 'n = getNodes(n, mode, "*");';
6709 fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6711 fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6713 q = q.replace(tm[0], "");
6716 while(!(mm = q.match(modeRe))){
6717 var matched = false;
6718 for(var j = 0; j < tklen; j++){
6720 var m = q.match(t.re);
6722 fn[fn.length] = t.select.replace(tplRe, function(x, i){
6725 q = q.replace(m[0], "");
6730 // prevent infinite loop on bad selector
6732 throw 'Error parsing selector, parsing failed at "' + q + '"';
6736 fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6737 q = q.replace(mm[1], "");
6740 fn[fn.length] = "return nodup(n);\n}";
6743 * list of variables that need from compression as they are used by eval.
6753 * eval:var:byClassName
6755 * eval:var:byAttribute
6756 * eval:var:attrValue
6764 * Selects a group of elements.
6765 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6766 * @param {Node} root (optional) The start of the query (defaults to document).
6769 select : function(path, root, type){
6770 if(!root || root == document){
6773 if(typeof root == "string"){
6774 root = document.getElementById(root);
6776 var paths = path.split(",");
6778 for(var i = 0, len = paths.length; i < len; i++){
6779 var p = paths[i].replace(trimRe, "");
6781 cache[p] = Roo.DomQuery.compile(p);
6783 throw p + " is not a valid selector";
6786 var result = cache[p](root);
6787 if(result && result != document){
6788 results = results.concat(result);
6791 if(paths.length > 1){
6792 return nodup(results);
6798 * Selects a single element.
6799 * @param {String} selector The selector/xpath query
6800 * @param {Node} root (optional) The start of the query (defaults to document).
6803 selectNode : function(path, root){
6804 return Roo.DomQuery.select(path, root)[0];
6808 * Selects the value of a node, optionally replacing null with the defaultValue.
6809 * @param {String} selector The selector/xpath query
6810 * @param {Node} root (optional) The start of the query (defaults to document).
6811 * @param {String} defaultValue
6813 selectValue : function(path, root, defaultValue){
6814 path = path.replace(trimRe, "");
6815 if(!valueCache[path]){
6816 valueCache[path] = Roo.DomQuery.compile(path, "select");
6818 var n = valueCache[path](root);
6819 n = n[0] ? n[0] : n;
6820 var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6821 return ((v === null||v === undefined||v==='') ? defaultValue : v);
6825 * Selects the value of a node, parsing integers and floats.
6826 * @param {String} selector The selector/xpath query
6827 * @param {Node} root (optional) The start of the query (defaults to document).
6828 * @param {Number} defaultValue
6831 selectNumber : function(path, root, defaultValue){
6832 var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6833 return parseFloat(v);
6837 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6838 * @param {String/HTMLElement/Array} el An element id, element or array of elements
6839 * @param {String} selector The simple selector to test
6842 is : function(el, ss){
6843 if(typeof el == "string"){
6844 el = document.getElementById(el);
6846 var isArray = (el instanceof Array);
6847 var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6848 return isArray ? (result.length == el.length) : (result.length > 0);
6852 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6853 * @param {Array} el An array of elements to filter
6854 * @param {String} selector The simple selector to test
6855 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6856 * the selector instead of the ones that match
6859 filter : function(els, ss, nonMatches){
6860 ss = ss.replace(trimRe, "");
6861 if(!simpleCache[ss]){
6862 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6864 var result = simpleCache[ss](els);
6865 return nonMatches ? quickDiff(result, els) : result;
6869 * Collection of matching regular expressions and code snippets.
6873 select: 'n = byClassName(n, null, " {1} ");'
6875 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6876 select: 'n = byPseudo(n, "{1}", "{2}");'
6878 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6879 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6882 select: 'n = byId(n, null, "{1}");'
6885 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6890 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6891 * 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, > <.
6894 "=" : function(a, v){
6897 "!=" : function(a, v){
6900 "^=" : function(a, v){
6901 return a && a.substr(0, v.length) == v;
6903 "$=" : function(a, v){
6904 return a && a.substr(a.length-v.length) == v;
6906 "*=" : function(a, v){
6907 return a && a.indexOf(v) !== -1;
6909 "%=" : function(a, v){
6910 return (a % v) == 0;
6912 "|=" : function(a, v){
6913 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6915 "~=" : function(a, v){
6916 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6921 * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6922 * and the argument (if any) supplied in the selector.
6925 "first-child" : function(c){
6926 var r = [], ri = -1, n;
6927 for(var i = 0, ci; ci = n = c[i]; i++){
6928 while((n = n.previousSibling) && n.nodeType != 1);
6936 "last-child" : function(c){
6937 var r = [], ri = -1, n;
6938 for(var i = 0, ci; ci = n = c[i]; i++){
6939 while((n = n.nextSibling) && n.nodeType != 1);
6947 "nth-child" : function(c, a) {
6948 var r = [], ri = -1;
6949 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6950 var f = (m[1] || 1) - 0, l = m[2] - 0;
6951 for(var i = 0, n; n = c[i]; i++){
6952 var pn = n.parentNode;
6953 if (batch != pn._batch) {
6955 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6956 if(cn.nodeType == 1){
6963 if (l == 0 || n.nodeIndex == l){
6966 } else if ((n.nodeIndex + l) % f == 0){
6974 "only-child" : function(c){
6975 var r = [], ri = -1;;
6976 for(var i = 0, ci; ci = c[i]; i++){
6977 if(!prev(ci) && !next(ci)){
6984 "empty" : function(c){
6985 var r = [], ri = -1;
6986 for(var i = 0, ci; ci = c[i]; i++){
6987 var cns = ci.childNodes, j = 0, cn, empty = true;
6990 if(cn.nodeType == 1 || cn.nodeType == 3){
7002 "contains" : function(c, v){
7003 var r = [], ri = -1;
7004 for(var i = 0, ci; ci = c[i]; i++){
7005 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7012 "nodeValue" : function(c, v){
7013 var r = [], ri = -1;
7014 for(var i = 0, ci; ci = c[i]; i++){
7015 if(ci.firstChild && ci.firstChild.nodeValue == v){
7022 "checked" : function(c){
7023 var r = [], ri = -1;
7024 for(var i = 0, ci; ci = c[i]; i++){
7025 if(ci.checked == true){
7032 "not" : function(c, ss){
7033 return Roo.DomQuery.filter(c, ss, true);
7036 "odd" : function(c){
7037 return this["nth-child"](c, "odd");
7040 "even" : function(c){
7041 return this["nth-child"](c, "even");
7044 "nth" : function(c, a){
7045 return c[a-1] || [];
7048 "first" : function(c){
7052 "last" : function(c){
7053 return c[c.length-1] || [];
7056 "has" : function(c, ss){
7057 var s = Roo.DomQuery.select;
7058 var r = [], ri = -1;
7059 for(var i = 0, ci; ci = c[i]; i++){
7060 if(s(ss, ci).length > 0){
7067 "next" : function(c, ss){
7068 var is = Roo.DomQuery.is;
7069 var r = [], ri = -1;
7070 for(var i = 0, ci; ci = c[i]; i++){
7079 "prev" : function(c, ss){
7080 var is = Roo.DomQuery.is;
7081 var r = [], ri = -1;
7082 for(var i = 0, ci; ci = c[i]; i++){
7095 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7096 * @param {String} path The selector/xpath query
7097 * @param {Node} root (optional) The start of the query (defaults to document).
7102 Roo.query = Roo.DomQuery.select;
7105 * Ext JS Library 1.1.1
7106 * Copyright(c) 2006-2007, Ext JS, LLC.
7108 * Originally Released Under LGPL - original licence link has changed is not relivant.
7111 * <script type="text/javascript">
7115 * @class Roo.util.Observable
7116 * Base class that provides a common interface for publishing events. Subclasses are expected to
7117 * to have a property "events" with all the events defined.<br>
7120 Employee = function(name){
7127 Roo.extend(Employee, Roo.util.Observable);
7129 * @param {Object} config properties to use (incuding events / listeners)
7132 Roo.util.Observable = function(cfg){
7135 this.addEvents(cfg.events || {});
7137 delete cfg.events; // make sure
7140 Roo.apply(this, cfg);
7143 this.on(this.listeners);
7144 delete this.listeners;
7147 Roo.util.Observable.prototype = {
7149 * @cfg {Object} listeners list of events and functions to call for this object,
7153 'click' : function(e) {
7163 * Fires the specified event with the passed parameters (minus the event name).
7164 * @param {String} eventName
7165 * @param {Object...} args Variable number of parameters are passed to handlers
7166 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7168 fireEvent : function(){
7169 var ce = this.events[arguments[0].toLowerCase()];
7170 if(typeof ce == "object"){
7171 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7178 filterOptRe : /^(?:scope|delay|buffer|single)$/,
7181 * Appends an event handler to this component
7182 * @param {String} eventName The type of event to listen for
7183 * @param {Function} handler The method the event invokes
7184 * @param {Object} scope (optional) The scope in which to execute the handler
7185 * function. The handler function's "this" context.
7186 * @param {Object} options (optional) An object containing handler configuration
7187 * properties. This may contain any of the following properties:<ul>
7188 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7189 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7190 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7191 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7192 * by the specified number of milliseconds. If the event fires again within that time, the original
7193 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7196 * <b>Combining Options</b><br>
7197 * Using the options argument, it is possible to combine different types of listeners:<br>
7199 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7201 el.on('click', this.onClick, this, {
7208 * <b>Attaching multiple handlers in 1 call</b><br>
7209 * The method also allows for a single argument to be passed which is a config object containing properties
7210 * which specify multiple handlers.
7219 fn: this.onMouseOver,
7223 fn: this.onMouseOut,
7229 * Or a shorthand syntax which passes the same scope object to all handlers:
7232 'click': this.onClick,
7233 'mouseover': this.onMouseOver,
7234 'mouseout': this.onMouseOut,
7239 addListener : function(eventName, fn, scope, o){
7240 if(typeof eventName == "object"){
7243 if(this.filterOptRe.test(e)){
7246 if(typeof o[e] == "function"){
7248 this.addListener(e, o[e], o.scope, o);
7250 // individual options
7251 this.addListener(e, o[e].fn, o[e].scope, o[e]);
7256 o = (!o || typeof o == "boolean") ? {} : o;
7257 eventName = eventName.toLowerCase();
7258 var ce = this.events[eventName] || true;
7259 if(typeof ce == "boolean"){
7260 ce = new Roo.util.Event(this, eventName);
7261 this.events[eventName] = ce;
7263 ce.addListener(fn, scope, o);
7267 * Removes a listener
7268 * @param {String} eventName The type of event to listen for
7269 * @param {Function} handler The handler to remove
7270 * @param {Object} scope (optional) The scope (this object) for the handler
7272 removeListener : function(eventName, fn, scope){
7273 var ce = this.events[eventName.toLowerCase()];
7274 if(typeof ce == "object"){
7275 ce.removeListener(fn, scope);
7280 * Removes all listeners for this object
7282 purgeListeners : function(){
7283 for(var evt in this.events){
7284 if(typeof this.events[evt] == "object"){
7285 this.events[evt].clearListeners();
7290 relayEvents : function(o, events){
7291 var createHandler = function(ename){
7294 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7297 for(var i = 0, len = events.length; i < len; i++){
7298 var ename = events[i];
7299 if(!this.events[ename]){
7300 this.events[ename] = true;
7302 o.on(ename, createHandler(ename), this);
7307 * Used to define events on this Observable
7308 * @param {Object} object The object with the events defined
7310 addEvents : function(o){
7314 Roo.applyIf(this.events, o);
7318 * Checks to see if this object has any listeners for a specified event
7319 * @param {String} eventName The name of the event to check for
7320 * @return {Boolean} True if the event is being listened for, else false
7322 hasListener : function(eventName){
7323 var e = this.events[eventName];
7324 return typeof e == "object" && e.listeners.length > 0;
7328 * Appends an event handler to this element (shorthand for addListener)
7329 * @param {String} eventName The type of event to listen for
7330 * @param {Function} handler The method the event invokes
7331 * @param {Object} scope (optional) The scope in which to execute the handler
7332 * function. The handler function's "this" context.
7333 * @param {Object} options (optional)
7336 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7338 * Removes a listener (shorthand for removeListener)
7339 * @param {String} eventName The type of event to listen for
7340 * @param {Function} handler The handler to remove
7341 * @param {Object} scope (optional) The scope (this object) for the handler
7344 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7347 * Starts capture on the specified Observable. All events will be passed
7348 * to the supplied function with the event name + standard signature of the event
7349 * <b>before</b> the event is fired. If the supplied function returns false,
7350 * the event will not fire.
7351 * @param {Observable} o The Observable to capture
7352 * @param {Function} fn The function to call
7353 * @param {Object} scope (optional) The scope (this object) for the fn
7356 Roo.util.Observable.capture = function(o, fn, scope){
7357 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7361 * Removes <b>all</b> added captures from the Observable.
7362 * @param {Observable} o The Observable to release
7365 Roo.util.Observable.releaseCapture = function(o){
7366 o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7371 var createBuffered = function(h, o, scope){
7372 var task = new Roo.util.DelayedTask();
7374 task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7378 var createSingle = function(h, e, fn, scope){
7380 e.removeListener(fn, scope);
7381 return h.apply(scope, arguments);
7385 var createDelayed = function(h, o, scope){
7387 var args = Array.prototype.slice.call(arguments, 0);
7388 setTimeout(function(){
7389 h.apply(scope, args);
7394 Roo.util.Event = function(obj, name){
7397 this.listeners = [];
7400 Roo.util.Event.prototype = {
7401 addListener : function(fn, scope, options){
7402 var o = options || {};
7403 scope = scope || this.obj;
7404 if(!this.isListening(fn, scope)){
7405 var l = {fn: fn, scope: scope, options: o};
7408 h = createDelayed(h, o, scope);
7411 h = createSingle(h, this, fn, scope);
7414 h = createBuffered(h, o, scope);
7417 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7418 this.listeners.push(l);
7420 this.listeners = this.listeners.slice(0);
7421 this.listeners.push(l);
7426 findListener : function(fn, scope){
7427 scope = scope || this.obj;
7428 var ls = this.listeners;
7429 for(var i = 0, len = ls.length; i < len; i++){
7431 if(l.fn == fn && l.scope == scope){
7438 isListening : function(fn, scope){
7439 return this.findListener(fn, scope) != -1;
7442 removeListener : function(fn, scope){
7444 if((index = this.findListener(fn, scope)) != -1){
7446 this.listeners.splice(index, 1);
7448 this.listeners = this.listeners.slice(0);
7449 this.listeners.splice(index, 1);
7456 clearListeners : function(){
7457 this.listeners = [];
7461 var ls = this.listeners, scope, len = ls.length;
7464 var args = Array.prototype.slice.call(arguments, 0);
7465 for(var i = 0; i < len; i++){
7467 if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7468 this.firing = false;
7472 this.firing = false;
7479 * Copyright(c) 2007-2017, Roo J Solutions Ltd
7486 * @class Roo.Document
7487 * @extends Roo.util.Observable
7488 * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7490 * @param {Object} config the methods and properties of the 'base' class for the application.
7492 * Generic Page handler - implement this to start your app..
7495 * MyProject = new Roo.Document({
7497 'load' : true // your events..
7500 'ready' : function() {
7501 // fired on Roo.onReady()
7506 Roo.Document = function(cfg) {
7511 Roo.util.Observable.call(this,cfg);
7515 Roo.onReady(function() {
7516 _this.fireEvent('ready');
7522 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7524 * Ext JS Library 1.1.1
7525 * Copyright(c) 2006-2007, Ext JS, LLC.
7527 * Originally Released Under LGPL - original licence link has changed is not relivant.
7530 * <script type="text/javascript">
7534 * @class Roo.EventManager
7535 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
7536 * several useful events directly.
7537 * See {@link Roo.EventObject} for more details on normalized event objects.
7540 Roo.EventManager = function(){
7541 var docReadyEvent, docReadyProcId, docReadyState = false;
7542 var resizeEvent, resizeTask, textEvent, textSize;
7543 var E = Roo.lib.Event;
7544 var D = Roo.lib.Dom;
7549 var fireDocReady = function(){
7551 docReadyState = true;
7554 clearInterval(docReadyProcId);
7556 if(Roo.isGecko || Roo.isOpera) {
7557 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7560 var defer = document.getElementById("ie-deferred-loader");
7562 defer.onreadystatechange = null;
7563 defer.parentNode.removeChild(defer);
7567 docReadyEvent.fire();
7568 docReadyEvent.clearListeners();
7573 var initDocReady = function(){
7574 docReadyEvent = new Roo.util.Event();
7575 if(Roo.isGecko || Roo.isOpera) {
7576 document.addEventListener("DOMContentLoaded", fireDocReady, false);
7578 document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7579 var defer = document.getElementById("ie-deferred-loader");
7580 defer.onreadystatechange = function(){
7581 if(this.readyState == "complete"){
7585 }else if(Roo.isSafari){
7586 docReadyProcId = setInterval(function(){
7587 var rs = document.readyState;
7588 if(rs == "complete") {
7593 // no matter what, make sure it fires on load
7594 E.on(window, "load", fireDocReady);
7597 var createBuffered = function(h, o){
7598 var task = new Roo.util.DelayedTask(h);
7600 // create new event object impl so new events don't wipe out properties
7601 e = new Roo.EventObjectImpl(e);
7602 task.delay(o.buffer, h, null, [e]);
7606 var createSingle = function(h, el, ename, fn){
7608 Roo.EventManager.removeListener(el, ename, fn);
7613 var createDelayed = function(h, o){
7615 // create new event object impl so new events don't wipe out properties
7616 e = new Roo.EventObjectImpl(e);
7617 setTimeout(function(){
7622 var transitionEndVal = false;
7624 var transitionEnd = function()
7626 if (transitionEndVal) {
7627 return transitionEndVal;
7629 var el = document.createElement('div');
7631 var transEndEventNames = {
7632 WebkitTransition : 'webkitTransitionEnd',
7633 MozTransition : 'transitionend',
7634 OTransition : 'oTransitionEnd otransitionend',
7635 transition : 'transitionend'
7638 for (var name in transEndEventNames) {
7639 if (el.style[name] !== undefined) {
7640 transitionEndVal = transEndEventNames[name];
7641 return transitionEndVal ;
7648 var listen = function(element, ename, opt, fn, scope)
7650 var o = (!opt || typeof opt == "boolean") ? {} : opt;
7651 fn = fn || o.fn; scope = scope || o.scope;
7652 var el = Roo.getDom(element);
7656 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7659 if (ename == 'transitionend') {
7660 ename = transitionEnd();
7662 var h = function(e){
7663 e = Roo.EventObject.setEvent(e);
7666 t = e.getTarget(o.delegate, el);
7673 if(o.stopEvent === true){
7676 if(o.preventDefault === true){
7679 if(o.stopPropagation === true){
7680 e.stopPropagation();
7683 if(o.normalized === false){
7687 fn.call(scope || el, e, t, o);
7690 h = createDelayed(h, o);
7693 h = createSingle(h, el, ename, fn);
7696 h = createBuffered(h, o);
7699 fn._handlers = fn._handlers || [];
7702 fn._handlers.push([Roo.id(el), ename, h]);
7706 E.on(el, ename, h); // this adds the actuall listener to the object..
7709 if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7710 el.addEventListener("DOMMouseScroll", h, false);
7711 E.on(window, 'unload', function(){
7712 el.removeEventListener("DOMMouseScroll", h, false);
7715 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7716 Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7721 var stopListening = function(el, ename, fn){
7722 var id = Roo.id(el), hds = fn._handlers, hd = fn;
7724 for(var i = 0, len = hds.length; i < len; i++){
7726 if(h[0] == id && h[1] == ename){
7733 E.un(el, ename, hd);
7734 el = Roo.getDom(el);
7735 if(ename == "mousewheel" && el.addEventListener){
7736 el.removeEventListener("DOMMouseScroll", hd, false);
7738 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7739 Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7743 var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7750 * @scope Roo.EventManager
7755 * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7756 * object with a Roo.EventObject
7757 * @param {Function} fn The method the event invokes
7758 * @param {Object} scope An object that becomes the scope of the handler
7759 * @param {boolean} override If true, the obj passed in becomes
7760 * the execution scope of the listener
7761 * @return {Function} The wrapped function
7764 wrap : function(fn, scope, override){
7766 Roo.EventObject.setEvent(e);
7767 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7772 * Appends an event handler to an element (shorthand for addListener)
7773 * @param {String/HTMLElement} element The html element or id to assign the
7774 * @param {String} eventName The type of event to listen for
7775 * @param {Function} handler The method the event invokes
7776 * @param {Object} scope (optional) The scope in which to execute the handler
7777 * function. The handler function's "this" context.
7778 * @param {Object} options (optional) An object containing handler configuration
7779 * properties. This may contain any of the following properties:<ul>
7780 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7781 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7782 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7783 * <li>preventDefault {Boolean} True to prevent the default action</li>
7784 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7785 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7786 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7787 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7788 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7789 * by the specified number of milliseconds. If the event fires again within that time, the original
7790 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7793 * <b>Combining Options</b><br>
7794 * Using the options argument, it is possible to combine different types of listeners:<br>
7796 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7798 el.on('click', this.onClick, this, {
7805 * <b>Attaching multiple handlers in 1 call</b><br>
7806 * The method also allows for a single argument to be passed which is a config object containing properties
7807 * which specify multiple handlers.
7817 fn: this.onMouseOver
7826 * Or a shorthand syntax:<br>
7829 'click' : this.onClick,
7830 'mouseover' : this.onMouseOver,
7831 'mouseout' : this.onMouseOut
7835 addListener : function(element, eventName, fn, scope, options){
7836 if(typeof eventName == "object"){
7842 if(typeof o[e] == "function"){
7844 listen(element, e, o, o[e], o.scope);
7846 // individual options
7847 listen(element, e, o[e]);
7852 return listen(element, eventName, options, fn, scope);
7856 * Removes an event handler
7858 * @param {String/HTMLElement} element The id or html element to remove the
7860 * @param {String} eventName The type of event
7861 * @param {Function} fn
7862 * @return {Boolean} True if a listener was actually removed
7864 removeListener : function(element, eventName, fn){
7865 return stopListening(element, eventName, fn);
7869 * Fires when the document is ready (before onload and before images are loaded). Can be
7870 * accessed shorthanded Roo.onReady().
7871 * @param {Function} fn The method the event invokes
7872 * @param {Object} scope An object that becomes the scope of the handler
7873 * @param {boolean} options
7875 onDocumentReady : function(fn, scope, options){
7876 if(docReadyState){ // if it already fired
7877 docReadyEvent.addListener(fn, scope, options);
7878 docReadyEvent.fire();
7879 docReadyEvent.clearListeners();
7885 docReadyEvent.addListener(fn, scope, options);
7889 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7890 * @param {Function} fn The method the event invokes
7891 * @param {Object} scope An object that becomes the scope of the handler
7892 * @param {boolean} options
7894 onWindowResize : function(fn, scope, options)
7897 resizeEvent = new Roo.util.Event();
7898 resizeTask = new Roo.util.DelayedTask(function(){
7899 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7901 E.on(window, "resize", function()
7904 resizeTask.delay(50);
7906 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7910 resizeEvent.addListener(fn, scope, options);
7914 * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7915 * @param {Function} fn The method the event invokes
7916 * @param {Object} scope An object that becomes the scope of the handler
7917 * @param {boolean} options
7919 onTextResize : function(fn, scope, options){
7921 textEvent = new Roo.util.Event();
7922 var textEl = new Roo.Element(document.createElement('div'));
7923 textEl.dom.className = 'x-text-resize';
7924 textEl.dom.innerHTML = 'X';
7925 textEl.appendTo(document.body);
7926 textSize = textEl.dom.offsetHeight;
7927 setInterval(function(){
7928 if(textEl.dom.offsetHeight != textSize){
7929 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7931 }, this.textResizeInterval);
7933 textEvent.addListener(fn, scope, options);
7937 * Removes the passed window resize listener.
7938 * @param {Function} fn The method the event invokes
7939 * @param {Object} scope The scope of handler
7941 removeResizeListener : function(fn, scope){
7943 resizeEvent.removeListener(fn, scope);
7948 fireResize : function(){
7950 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7954 * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7958 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7960 textResizeInterval : 50
7965 * @scopeAlias pub=Roo.EventManager
7969 * Appends an event handler to an element (shorthand for addListener)
7970 * @param {String/HTMLElement} element The html element or id to assign the
7971 * @param {String} eventName The type of event to listen for
7972 * @param {Function} handler The method the event invokes
7973 * @param {Object} scope (optional) The scope in which to execute the handler
7974 * function. The handler function's "this" context.
7975 * @param {Object} options (optional) An object containing handler configuration
7976 * properties. This may contain any of the following properties:<ul>
7977 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7978 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7979 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7980 * <li>preventDefault {Boolean} True to prevent the default action</li>
7981 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7982 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7983 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7984 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7985 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7986 * by the specified number of milliseconds. If the event fires again within that time, the original
7987 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7990 * <b>Combining Options</b><br>
7991 * Using the options argument, it is possible to combine different types of listeners:<br>
7993 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7995 el.on('click', this.onClick, this, {
8002 * <b>Attaching multiple handlers in 1 call</b><br>
8003 * The method also allows for a single argument to be passed which is a config object containing properties
8004 * which specify multiple handlers.
8014 fn: this.onMouseOver
8023 * Or a shorthand syntax:<br>
8026 'click' : this.onClick,
8027 'mouseover' : this.onMouseOver,
8028 'mouseout' : this.onMouseOut
8032 pub.on = pub.addListener;
8033 pub.un = pub.removeListener;
8035 pub.stoppedMouseDownEvent = new Roo.util.Event();
8039 * Fires when the document is ready (before onload and before images are loaded). Shorthand of {@link Roo.EventManager#onDocumentReady}.
8040 * @param {Function} fn The method the event invokes
8041 * @param {Object} scope An object that becomes the scope of the handler
8042 * @param {boolean} override If true, the obj passed in becomes
8043 * the execution scope of the listener
8047 Roo.onReady = Roo.EventManager.onDocumentReady;
8049 Roo.onReady(function(){
8050 var bd = Roo.get(document.body);
8055 : Roo.isIE11 ? "roo-ie11"
8056 : Roo.isEdge ? "roo-edge"
8057 : Roo.isGecko ? "roo-gecko"
8058 : Roo.isOpera ? "roo-opera"
8059 : Roo.isSafari ? "roo-safari" : ""];
8062 cls.push("roo-mac");
8065 cls.push("roo-linux");
8068 cls.push("roo-ios");
8071 cls.push("roo-touch");
8073 if(Roo.isBorderBox){
8074 cls.push('roo-border-box');
8076 if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8077 var p = bd.dom.parentNode;
8079 p.className += ' roo-strict';
8082 bd.addClass(cls.join(' '));
8086 * @class Roo.EventObject
8087 * EventObject exposes the Yahoo! UI Event functionality directly on the object
8088 * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code
8091 function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8093 var target = e.getTarget();
8096 var myDiv = Roo.get("myDiv");
8097 myDiv.on("click", handleClick);
8099 Roo.EventManager.on("myDiv", 'click', handleClick);
8100 Roo.EventManager.addListener("myDiv", 'click', handleClick);
8104 Roo.EventObject = function(){
8106 var E = Roo.lib.Event;
8108 // safari keypress events for special keys return bad keycodes
8111 63235 : 39, // right
8114 63276 : 33, // page up
8115 63277 : 34, // page down
8116 63272 : 46, // delete
8121 // normalize button clicks
8122 var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8123 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8125 Roo.EventObjectImpl = function(e){
8127 this.setEvent(e.browserEvent || e);
8130 Roo.EventObjectImpl.prototype = {
8132 * Used to fix doc tools.
8133 * @scope Roo.EventObject.prototype
8139 /** The normal browser event */
8140 browserEvent : null,
8141 /** The button pressed in a mouse event */
8143 /** True if the shift key was down during the event */
8145 /** True if the control key was down during the event */
8147 /** True if the alt key was down during the event */
8206 setEvent : function(e){
8207 if(e == this || (e && e.browserEvent)){ // already wrapped
8210 this.browserEvent = e;
8212 // normalize buttons
8213 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8214 if(e.type == 'click' && this.button == -1){
8218 this.shiftKey = e.shiftKey;
8219 // mac metaKey behaves like ctrlKey
8220 this.ctrlKey = e.ctrlKey || e.metaKey;
8221 this.altKey = e.altKey;
8222 // in getKey these will be normalized for the mac
8223 this.keyCode = e.keyCode;
8224 // keyup warnings on firefox.
8225 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8226 // cache the target for the delayed and or buffered events
8227 this.target = E.getTarget(e);
8229 this.xy = E.getXY(e);
8232 this.shiftKey = false;
8233 this.ctrlKey = false;
8234 this.altKey = false;
8244 * Stop the event (preventDefault and stopPropagation)
8246 stopEvent : function(){
8247 if(this.browserEvent){
8248 if(this.browserEvent.type == 'mousedown'){
8249 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8251 E.stopEvent(this.browserEvent);
8256 * Prevents the browsers default handling of the event.
8258 preventDefault : function(){
8259 if(this.browserEvent){
8260 E.preventDefault(this.browserEvent);
8265 isNavKeyPress : function(){
8266 var k = this.keyCode;
8267 k = Roo.isSafari ? (safariKeys[k] || k) : k;
8268 return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8271 isSpecialKey : function(){
8272 var k = this.keyCode;
8273 return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13 || k == 40 || k == 27 ||
8274 (k == 16) || (k == 17) ||
8275 (k >= 18 && k <= 20) ||
8276 (k >= 33 && k <= 35) ||
8277 (k >= 36 && k <= 39) ||
8278 (k >= 44 && k <= 45);
8281 * Cancels bubbling of the event.
8283 stopPropagation : function(){
8284 if(this.browserEvent){
8285 if(this.type == 'mousedown'){
8286 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8288 E.stopPropagation(this.browserEvent);
8293 * Gets the key code for the event.
8296 getCharCode : function(){
8297 return this.charCode || this.keyCode;
8301 * Returns a normalized keyCode for the event.
8302 * @return {Number} The key code
8304 getKey : function(){
8305 var k = this.keyCode || this.charCode;
8306 return Roo.isSafari ? (safariKeys[k] || k) : k;
8310 * Gets the x coordinate of the event.
8313 getPageX : function(){
8318 * Gets the y coordinate of the event.
8321 getPageY : function(){
8326 * Gets the time of the event.
8329 getTime : function(){
8330 if(this.browserEvent){
8331 return E.getTime(this.browserEvent);
8337 * Gets the page coordinates of the event.
8338 * @return {Array} The xy values like [x, y]
8345 * Gets the target for the event.
8346 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8347 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8348 search as a number or element (defaults to 10 || document.body)
8349 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8350 * @return {HTMLelement}
8352 getTarget : function(selector, maxDepth, returnEl){
8353 return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8356 * Gets the related target.
8357 * @return {HTMLElement}
8359 getRelatedTarget : function(){
8360 if(this.browserEvent){
8361 return E.getRelatedTarget(this.browserEvent);
8367 * Normalizes mouse wheel delta across browsers
8368 * @return {Number} The delta
8370 getWheelDelta : function(){
8371 var e = this.browserEvent;
8373 if(e.wheelDelta){ /* IE/Opera. */
8374 delta = e.wheelDelta/120;
8375 }else if(e.detail){ /* Mozilla case. */
8376 delta = -e.detail/3;
8382 * Returns true if the control, meta, shift or alt key was pressed during this event.
8385 hasModifier : function(){
8386 return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8390 * Returns true if the target of this event equals el or is a child of el
8391 * @param {String/HTMLElement/Element} el
8392 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8395 within : function(el, related){
8396 var t = this[related ? "getRelatedTarget" : "getTarget"]();
8397 return t && Roo.fly(el).contains(t);
8400 getPoint : function(){
8401 return new Roo.lib.Point(this.xy[0], this.xy[1]);
8405 return new Roo.EventObjectImpl();
8410 * Ext JS Library 1.1.1
8411 * Copyright(c) 2006-2007, Ext JS, LLC.
8413 * Originally Released Under LGPL - original licence link has changed is not relivant.
8416 * <script type="text/javascript">
8420 // was in Composite Element!??!?!
8423 var D = Roo.lib.Dom;
8424 var E = Roo.lib.Event;
8425 var A = Roo.lib.Anim;
8427 // local style camelizing for speed
8429 var camelRe = /(-[a-z])/gi;
8430 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8431 var view = document.defaultView;
8434 * @class Roo.Element
8435 * Represents an Element in the DOM.<br><br>
8438 var el = Roo.get("my-div");
8441 var el = getEl("my-div");
8443 // or with a DOM element
8444 var el = Roo.get(myDivElement);
8446 * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8447 * each call instead of constructing a new one.<br><br>
8448 * <b>Animations</b><br />
8449 * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8450 * should either be a boolean (true) or an object literal with animation options. The animation options are:
8452 Option Default Description
8453 --------- -------- ---------------------------------------------
8454 duration .35 The duration of the animation in seconds
8455 easing easeOut The YUI easing method
8456 callback none A function to execute when the anim completes
8457 scope this The scope (this) of the callback function
8459 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8460 * manipulate the animation. Here's an example:
8462 var el = Roo.get("my-div");
8467 // default animation
8468 el.setWidth(100, true);
8470 // animation with some options set
8477 // using the "anim" property to get the Anim object
8483 el.setWidth(100, opt);
8485 if(opt.anim.isAnimated()){
8489 * <b> Composite (Collections of) Elements</b><br />
8490 * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8491 * @constructor Create a new Element directly.
8492 * @param {String/HTMLElement} element
8493 * @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).
8495 Roo.Element = function(element, forceNew)
8497 var dom = typeof element == "string" ?
8498 document.getElementById(element) : element;
8500 this.listeners = {};
8502 if(!dom){ // invalid id/element
8506 if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8507 return Roo.Element.cache[id];
8517 * The DOM element ID
8520 this.id = id || Roo.id(dom);
8522 return this; // assumed for cctor?
8525 var El = Roo.Element;
8529 * The element's default display mode (defaults to "")
8532 originalDisplay : "",
8535 // note this is overridden in BS version..
8538 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8544 * Sets the element's visibility mode. When setVisible() is called it
8545 * will use this to determine whether to set the visibility or the display property.
8546 * @param visMode Element.VISIBILITY or Element.DISPLAY
8547 * @return {Roo.Element} this
8549 setVisibilityMode : function(visMode){
8550 this.visibilityMode = visMode;
8554 * Convenience method for setVisibilityMode(Element.DISPLAY)
8555 * @param {String} display (optional) What to set display to when visible
8556 * @return {Roo.Element} this
8558 enableDisplayMode : function(display){
8559 this.setVisibilityMode(El.DISPLAY);
8560 if(typeof display != "undefined") { this.originalDisplay = display; }
8565 * 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)
8566 * @param {String} selector The simple selector to test
8567 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8568 search as a number or element (defaults to 10 || document.body)
8569 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8570 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8572 findParent : function(simpleSelector, maxDepth, returnEl){
8573 var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8574 maxDepth = maxDepth || 50;
8575 if(typeof maxDepth != "number"){
8576 stopEl = Roo.getDom(maxDepth);
8579 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8580 if(dq.is(p, simpleSelector)){
8581 return returnEl ? Roo.get(p) : p;
8591 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8592 * @param {String} selector The simple selector to test
8593 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8594 search as a number or element (defaults to 10 || document.body)
8595 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8596 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8598 findParentNode : function(simpleSelector, maxDepth, returnEl){
8599 var p = Roo.fly(this.dom.parentNode, '_internal');
8600 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8604 * Looks at the scrollable parent element
8606 findScrollableParent : function()
8608 var overflowRegex = /(auto|scroll)/;
8610 if(this.getStyle('position') === 'fixed'){
8611 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8614 var excludeStaticParent = this.getStyle('position') === "absolute";
8616 for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8618 if (excludeStaticParent && parent.getStyle('position') === "static") {
8622 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8626 if(parent.dom.nodeName.toLowerCase() == 'body'){
8627 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8631 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8635 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8636 * This is a shortcut for findParentNode() that always returns an Roo.Element.
8637 * @param {String} selector The simple selector to test
8638 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8639 search as a number or element (defaults to 10 || document.body)
8640 * @return {Roo.Element} The matching DOM node (or null if no match was found)
8642 up : function(simpleSelector, maxDepth){
8643 return this.findParentNode(simpleSelector, maxDepth, true);
8649 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8650 * @param {String} selector The simple selector to test
8651 * @return {Boolean} True if this element matches the selector, else false
8653 is : function(simpleSelector){
8654 return Roo.DomQuery.is(this.dom, simpleSelector);
8658 * Perform animation on this element.
8659 * @param {Object} args The YUI animation control args
8660 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8661 * @param {Function} onComplete (optional) Function to call when animation completes
8662 * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8663 * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8664 * @return {Roo.Element} this
8666 animate : function(args, duration, onComplete, easing, animType){
8667 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8672 * @private Internal animation call
8674 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8675 animType = animType || 'run';
8677 var anim = Roo.lib.Anim[animType](
8679 (opt.duration || defaultDur) || .35,
8680 (opt.easing || defaultEase) || 'easeOut',
8682 Roo.callback(cb, this);
8683 Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8691 // private legacy anim prep
8692 preanim : function(a, i){
8693 return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8697 * Removes worthless text nodes
8698 * @param {Boolean} forceReclean (optional) By default the element
8699 * keeps track if it has been cleaned already so
8700 * you can call this over and over. However, if you update the element and
8701 * need to force a reclean, you can pass true.
8703 clean : function(forceReclean){
8704 if(this.isCleaned && forceReclean !== true){
8708 var d = this.dom, n = d.firstChild, ni = -1;
8710 var nx = n.nextSibling;
8711 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8718 this.isCleaned = true;
8723 calcOffsetsTo : function(el){
8726 var restorePos = false;
8727 if(el.getStyle('position') == 'static'){
8728 el.position('relative');
8733 while(op && op != d && op.tagName != 'HTML'){
8736 op = op.offsetParent;
8739 el.position('static');
8745 * Scrolls this element into view within the passed container.
8746 * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8747 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8748 * @return {Roo.Element} this
8750 scrollIntoView : function(container, hscroll){
8751 var c = Roo.getDom(container) || document.body;
8754 var o = this.calcOffsetsTo(c),
8757 b = t+el.offsetHeight,
8758 r = l+el.offsetWidth;
8760 var ch = c.clientHeight;
8761 var ct = parseInt(c.scrollTop, 10);
8762 var cl = parseInt(c.scrollLeft, 10);
8764 var cr = cl + c.clientWidth;
8772 if(hscroll !== false){
8776 c.scrollLeft = r-c.clientWidth;
8783 scrollChildIntoView : function(child, hscroll){
8784 Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8788 * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8789 * the new height may not be available immediately.
8790 * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8791 * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8792 * @param {Function} onComplete (optional) Function to call when animation completes
8793 * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8794 * @return {Roo.Element} this
8796 autoHeight : function(animate, duration, onComplete, easing){
8797 var oldHeight = this.getHeight();
8799 this.setHeight(1); // force clipping
8800 setTimeout(function(){
8801 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8803 this.setHeight(height);
8805 if(typeof onComplete == "function"){
8809 this.setHeight(oldHeight); // restore original height
8810 this.setHeight(height, animate, duration, function(){
8812 if(typeof onComplete == "function") { onComplete(); }
8813 }.createDelegate(this), easing);
8815 }.createDelegate(this), 0);
8820 * Returns true if this element is an ancestor of the passed element
8821 * @param {HTMLElement/String} el The element to check
8822 * @return {Boolean} True if this element is an ancestor of el, else false
8824 contains : function(el){
8825 if(!el){return false;}
8826 return D.isAncestor(this.dom, el.dom ? el.dom : el);
8830 * Checks whether the element is currently visible using both visibility and display properties.
8831 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8832 * @return {Boolean} True if the element is currently visible, else false
8834 isVisible : function(deep) {
8835 var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8836 if(deep !== true || !vis){
8839 var p = this.dom.parentNode;
8840 while(p && p.tagName.toLowerCase() != "body"){
8841 if(!Roo.fly(p, '_isVisible').isVisible()){
8850 * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8851 * @param {String} selector The CSS selector
8852 * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8853 * @return {CompositeElement/CompositeElementLite} The composite element
8855 select : function(selector, unique){
8856 return El.select(selector, unique, this.dom);
8860 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8861 * @param {String} selector The CSS selector
8862 * @return {Array} An array of the matched nodes
8864 query : function(selector, unique){
8865 return Roo.DomQuery.select(selector, this.dom);
8869 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8870 * @param {String} selector The CSS selector
8871 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8872 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8874 child : function(selector, returnDom){
8875 var n = Roo.DomQuery.selectNode(selector, this.dom);
8876 return returnDom ? n : Roo.get(n);
8880 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8881 * @param {String} selector The CSS selector
8882 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8883 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8885 down : function(selector, returnDom){
8886 var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8887 return returnDom ? n : Roo.get(n);
8891 * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8892 * @param {String} group The group the DD object is member of
8893 * @param {Object} config The DD config object
8894 * @param {Object} overrides An object containing methods to override/implement on the DD object
8895 * @return {Roo.dd.DD} The DD object
8897 initDD : function(group, config, overrides){
8898 var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8899 return Roo.apply(dd, overrides);
8903 * Initializes a {@link Roo.dd.DDProxy} object for this element.
8904 * @param {String} group The group the DDProxy object is member of
8905 * @param {Object} config The DDProxy config object
8906 * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8907 * @return {Roo.dd.DDProxy} The DDProxy object
8909 initDDProxy : function(group, config, overrides){
8910 var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8911 return Roo.apply(dd, overrides);
8915 * Initializes a {@link Roo.dd.DDTarget} object for this element.
8916 * @param {String} group The group the DDTarget object is member of
8917 * @param {Object} config The DDTarget config object
8918 * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8919 * @return {Roo.dd.DDTarget} The DDTarget object
8921 initDDTarget : function(group, config, overrides){
8922 var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8923 return Roo.apply(dd, overrides);
8927 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8928 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8929 * @param {Boolean} visible Whether the element is visible
8930 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8931 * @return {Roo.Element} this
8933 setVisible : function(visible, animate){
8935 if(this.visibilityMode == El.DISPLAY){
8936 this.setDisplayed(visible);
8939 this.dom.style.visibility = visible ? "visible" : "hidden";
8942 // closure for composites
8944 var visMode = this.visibilityMode;
8946 this.setOpacity(.01);
8947 this.setVisible(true);
8949 this.anim({opacity: { to: (visible?1:0) }},
8950 this.preanim(arguments, 1),
8951 null, .35, 'easeIn', function(){
8953 if(visMode == El.DISPLAY){
8954 dom.style.display = "none";
8956 dom.style.visibility = "hidden";
8958 Roo.get(dom).setOpacity(1);
8966 * Returns true if display is not "none"
8969 isDisplayed : function() {
8970 return this.getStyle("display") != "none";
8974 * Toggles the element's visibility or display, depending on visibility mode.
8975 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8976 * @return {Roo.Element} this
8978 toggle : function(animate){
8979 this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8984 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8985 * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8986 * @return {Roo.Element} this
8988 setDisplayed : function(value) {
8989 if(typeof value == "boolean"){
8990 value = value ? this.originalDisplay : "none";
8992 this.setStyle("display", value);
8997 * Tries to focus the element. Any exceptions are caught and ignored.
8998 * @return {Roo.Element} this
9000 focus : function() {
9008 * Tries to blur the element. Any exceptions are caught and ignored.
9009 * @return {Roo.Element} this
9019 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9020 * @param {String/Array} className The CSS class to add, or an array of classes
9021 * @return {Roo.Element} this
9023 addClass : function(className){
9024 if(className instanceof Array){
9025 for(var i = 0, len = className.length; i < len; i++) {
9026 this.addClass(className[i]);
9029 if(className && !this.hasClass(className)){
9030 if (this.dom instanceof SVGElement) {
9031 this.dom.className.baseVal =this.dom.className.baseVal + " " + className;
9033 this.dom.className = this.dom.className + " " + className;
9041 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9042 * @param {String/Array} className The CSS class to add, or an array of classes
9043 * @return {Roo.Element} this
9045 radioClass : function(className){
9046 var siblings = this.dom.parentNode.childNodes;
9047 for(var i = 0; i < siblings.length; i++) {
9048 var s = siblings[i];
9049 if(s.nodeType == 1){
9050 Roo.get(s).removeClass(className);
9053 this.addClass(className);
9058 * Removes one or more CSS classes from the element.
9059 * @param {String/Array} className The CSS class to remove, or an array of classes
9060 * @return {Roo.Element} this
9062 removeClass : function(className){
9064 var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9065 if(!className || !cn){
9068 if(className instanceof Array){
9069 for(var i = 0, len = className.length; i < len; i++) {
9070 this.removeClass(className[i]);
9073 if(this.hasClass(className)){
9074 var re = this.classReCache[className];
9076 re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9077 this.classReCache[className] = re;
9079 if (this.dom instanceof SVGElement) {
9080 this.dom.className.baseVal = cn.replace(re, " ");
9082 this.dom.className = cn.replace(re, " ");
9093 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9094 * @param {String} className The CSS class to toggle
9095 * @return {Roo.Element} this
9097 toggleClass : function(className){
9098 if(this.hasClass(className)){
9099 this.removeClass(className);
9101 this.addClass(className);
9107 * Checks if the specified CSS class exists on this element's DOM node.
9108 * @param {String} className The CSS class to check for
9109 * @return {Boolean} True if the class exists, else false
9111 hasClass : function(className){
9112 if (this.dom instanceof SVGElement) {
9113 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1;
9115 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9119 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
9120 * @param {String} oldClassName The CSS class to replace
9121 * @param {String} newClassName The replacement CSS class
9122 * @return {Roo.Element} this
9124 replaceClass : function(oldClassName, newClassName){
9125 this.removeClass(oldClassName);
9126 this.addClass(newClassName);
9131 * Returns an object with properties matching the styles requested.
9132 * For example, el.getStyles('color', 'font-size', 'width') might return
9133 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9134 * @param {String} style1 A style name
9135 * @param {String} style2 A style name
9136 * @param {String} etc.
9137 * @return {Object} The style object
9139 getStyles : function(){
9140 var a = arguments, len = a.length, r = {};
9141 for(var i = 0; i < len; i++){
9142 r[a[i]] = this.getStyle(a[i]);
9148 * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9149 * @param {String} property The style property whose value is returned.
9150 * @return {String} The current value of the style property for this element.
9152 getStyle : function(){
9153 return view && view.getComputedStyle ?
9155 var el = this.dom, v, cs, camel;
9156 if(prop == 'float'){
9159 if(el.style && (v = el.style[prop])){
9162 if(cs = view.getComputedStyle(el, "")){
9163 if(!(camel = propCache[prop])){
9164 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9171 var el = this.dom, v, cs, camel;
9172 if(prop == 'opacity'){
9173 if(typeof el.style.filter == 'string'){
9174 var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9176 var fv = parseFloat(m[1]);
9178 return fv ? fv / 100 : 0;
9183 }else if(prop == 'float'){
9184 prop = "styleFloat";
9186 if(!(camel = propCache[prop])){
9187 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9189 if(v = el.style[camel]){
9192 if(cs = el.currentStyle){
9200 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9201 * @param {String/Object} property The style property to be set, or an object of multiple styles.
9202 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9203 * @return {Roo.Element} this
9205 setStyle : function(prop, value){
9206 if(typeof prop == "string"){
9208 if (prop == 'float') {
9209 this.setStyle(Roo.isIE ? 'styleFloat' : 'cssFloat', value);
9214 if(!(camel = propCache[prop])){
9215 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9218 if(camel == 'opacity') {
9219 this.setOpacity(value);
9221 this.dom.style[camel] = value;
9224 for(var style in prop){
9225 if(typeof prop[style] != "function"){
9226 this.setStyle(style, prop[style]);
9234 * More flexible version of {@link #setStyle} for setting style properties.
9235 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9236 * a function which returns such a specification.
9237 * @return {Roo.Element} this
9239 applyStyles : function(style){
9240 Roo.DomHelper.applyStyles(this.dom, style);
9245 * 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).
9246 * @return {Number} The X position of the element
9249 return D.getX(this.dom);
9253 * 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).
9254 * @return {Number} The Y position of the element
9257 return D.getY(this.dom);
9261 * 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).
9262 * @return {Array} The XY position of the element
9265 return D.getXY(this.dom);
9269 * 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).
9270 * @param {Number} The X position of the element
9271 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9272 * @return {Roo.Element} this
9274 setX : function(x, animate){
9276 D.setX(this.dom, x);
9278 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9284 * 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).
9285 * @param {Number} The Y position of the element
9286 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9287 * @return {Roo.Element} this
9289 setY : function(y, animate){
9291 D.setY(this.dom, y);
9293 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9299 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9300 * @param {String} left The left CSS property value
9301 * @return {Roo.Element} this
9303 setLeft : function(left){
9304 this.setStyle("left", this.addUnits(left));
9309 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9310 * @param {String} top The top CSS property value
9311 * @return {Roo.Element} this
9313 setTop : function(top){
9314 this.setStyle("top", this.addUnits(top));
9319 * Sets the element's CSS right style.
9320 * @param {String} right The right CSS property value
9321 * @return {Roo.Element} this
9323 setRight : function(right){
9324 this.setStyle("right", this.addUnits(right));
9329 * Sets the element's CSS bottom style.
9330 * @param {String} bottom The bottom CSS property value
9331 * @return {Roo.Element} this
9333 setBottom : function(bottom){
9334 this.setStyle("bottom", this.addUnits(bottom));
9339 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9340 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9341 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9342 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9343 * @return {Roo.Element} this
9345 setXY : function(pos, animate){
9347 D.setXY(this.dom, pos);
9349 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9355 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9356 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9357 * @param {Number} x X value for new position (coordinates are page-based)
9358 * @param {Number} y Y value for new position (coordinates are page-based)
9359 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9360 * @return {Roo.Element} this
9362 setLocation : function(x, y, animate){
9363 this.setXY([x, y], this.preanim(arguments, 2));
9368 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9369 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9370 * @param {Number} x X value for new position (coordinates are page-based)
9371 * @param {Number} y Y value for new position (coordinates are page-based)
9372 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9373 * @return {Roo.Element} this
9375 moveTo : function(x, y, animate){
9376 this.setXY([x, y], this.preanim(arguments, 2));
9381 * Returns the region of the given element.
9382 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9383 * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9385 getRegion : function(){
9386 return D.getRegion(this.dom);
9390 * Returns the offset height of the element
9391 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9392 * @return {Number} The element's height
9394 getHeight : function(contentHeight){
9395 var h = this.dom.offsetHeight || 0;
9396 return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9400 * Returns the offset width of the element
9401 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9402 * @return {Number} The element's width
9404 getWidth : function(contentWidth){
9405 var w = this.dom.offsetWidth || 0;
9406 return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9410 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9411 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9412 * if a height has not been set using CSS.
9415 getComputedHeight : function(){
9416 var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9418 h = parseInt(this.getStyle('height'), 10) || 0;
9419 if(!this.isBorderBox()){
9420 h += this.getFrameWidth('tb');
9427 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9428 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9429 * if a width has not been set using CSS.
9432 getComputedWidth : function(){
9433 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9435 w = parseInt(this.getStyle('width'), 10) || 0;
9436 if(!this.isBorderBox()){
9437 w += this.getFrameWidth('lr');
9444 * Returns the size of the element.
9445 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9446 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9448 getSize : function(contentSize){
9449 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9453 * Returns the width and height of the viewport.
9454 * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9456 getViewSize : function(){
9457 var d = this.dom, doc = document, aw = 0, ah = 0;
9458 if(d == doc || d == doc.body){
9459 return {width : D.getViewWidth(), height: D.getViewHeight()};
9462 width : d.clientWidth,
9463 height: d.clientHeight
9469 * Returns the value of the "value" attribute
9470 * @param {Boolean} asNumber true to parse the value as a number
9471 * @return {String/Number}
9473 getValue : function(asNumber){
9474 return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9478 adjustWidth : function(width){
9479 if(typeof width == "number"){
9480 if(this.autoBoxAdjust && !this.isBorderBox()){
9481 width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9491 adjustHeight : function(height){
9492 if(typeof height == "number"){
9493 if(this.autoBoxAdjust && !this.isBorderBox()){
9494 height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9504 * Set the width of the element
9505 * @param {Number} width The new width
9506 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9507 * @return {Roo.Element} this
9509 setWidth : function(width, animate){
9510 width = this.adjustWidth(width);
9512 this.dom.style.width = this.addUnits(width);
9514 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9520 * Set the height of the element
9521 * @param {Number} height The new height
9522 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9523 * @return {Roo.Element} this
9525 setHeight : function(height, animate){
9526 height = this.adjustHeight(height);
9528 this.dom.style.height = this.addUnits(height);
9530 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9536 * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9537 * @param {Number} width The new width
9538 * @param {Number} height The new height
9539 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9540 * @return {Roo.Element} this
9542 setSize : function(width, height, animate){
9543 if(typeof width == "object"){ // in case of object from getSize()
9544 height = width.height; width = width.width;
9546 width = this.adjustWidth(width); height = this.adjustHeight(height);
9548 this.dom.style.width = this.addUnits(width);
9549 this.dom.style.height = this.addUnits(height);
9551 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9557 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9558 * @param {Number} x X value for new position (coordinates are page-based)
9559 * @param {Number} y Y value for new position (coordinates are page-based)
9560 * @param {Number} width The new width
9561 * @param {Number} height The new height
9562 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9563 * @return {Roo.Element} this
9565 setBounds : function(x, y, width, height, animate){
9567 this.setSize(width, height);
9568 this.setLocation(x, y);
9570 width = this.adjustWidth(width); height = this.adjustHeight(height);
9571 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9572 this.preanim(arguments, 4), 'motion');
9578 * 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.
9579 * @param {Roo.lib.Region} region The region to fill
9580 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9581 * @return {Roo.Element} this
9583 setRegion : function(region, animate){
9584 this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9589 * Appends an event handler
9591 * @param {String} eventName The type of event to append
9592 * @param {Function} fn The method the event invokes
9593 * @param {Object} scope (optional) The scope (this object) of the fn
9594 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
9596 addListener : function(eventName, fn, scope, options)
9598 if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9599 this.addListener('touchstart', this.onTapHandler, this);
9602 // we need to handle a special case where dom element is a svg element.
9603 // in this case we do not actua
9608 if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9609 if (typeof(this.listeners[eventName]) == 'undefined') {
9610 this.listeners[eventName] = new Roo.util.Event(this, eventName);
9612 this.listeners[eventName].addListener(fn, scope, options);
9617 Roo.EventManager.on(this.dom, eventName, fn, scope || this, options);
9622 onTapHandler : function(event)
9624 if(!this.tapedTwice) {
9625 this.tapedTwice = true;
9627 setTimeout( function() {
9628 s.tapedTwice = false;
9632 event.preventDefault();
9633 var revent = new MouseEvent('dblclick', {
9639 this.dom.dispatchEvent(revent);
9640 //action on double tap goes below
9645 * Removes an event handler from this element
9646 * @param {String} eventName the type of event to remove
9647 * @param {Function} fn the method the event invokes
9648 * @param {Function} scope (needed for svg fake listeners)
9649 * @return {Roo.Element} this
9651 removeListener : function(eventName, fn, scope){
9652 Roo.EventManager.removeListener(this.dom, eventName, fn);
9653 if (typeof(this.listeners) == 'undefined' || typeof(this.listeners[eventName]) == 'undefined') {
9656 this.listeners[eventName].removeListener(fn, scope);
9661 * Removes all previous added listeners from this element
9662 * @return {Roo.Element} this
9664 removeAllListeners : function(){
9665 E.purgeElement(this.dom);
9666 this.listeners = {};
9670 relayEvent : function(eventName, observable){
9671 this.on(eventName, function(e){
9672 observable.fireEvent(eventName, e);
9678 * Set the opacity of the element
9679 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9680 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9681 * @return {Roo.Element} this
9683 setOpacity : function(opacity, animate){
9685 var s = this.dom.style;
9688 s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9689 (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9691 s.opacity = opacity;
9694 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9700 * Gets the left X coordinate
9701 * @param {Boolean} local True to get the local css position instead of page coordinate
9704 getLeft : function(local){
9708 return parseInt(this.getStyle("left"), 10) || 0;
9713 * Gets the right X coordinate of the element (element X position + element width)
9714 * @param {Boolean} local True to get the local css position instead of page coordinate
9717 getRight : function(local){
9719 return this.getX() + this.getWidth();
9721 return (this.getLeft(true) + this.getWidth()) || 0;
9726 * Gets the top Y coordinate
9727 * @param {Boolean} local True to get the local css position instead of page coordinate
9730 getTop : function(local) {
9734 return parseInt(this.getStyle("top"), 10) || 0;
9739 * Gets the bottom Y coordinate of the element (element Y position + element height)
9740 * @param {Boolean} local True to get the local css position instead of page coordinate
9743 getBottom : function(local){
9745 return this.getY() + this.getHeight();
9747 return (this.getTop(true) + this.getHeight()) || 0;
9752 * Initializes positioning on this element. If a desired position is not passed, it will make the
9753 * the element positioned relative IF it is not already positioned.
9754 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9755 * @param {Number} zIndex (optional) The zIndex to apply
9756 * @param {Number} x (optional) Set the page X position
9757 * @param {Number} y (optional) Set the page Y position
9759 position : function(pos, zIndex, x, y){
9761 if(this.getStyle('position') == 'static'){
9762 this.setStyle('position', 'relative');
9765 this.setStyle("position", pos);
9768 this.setStyle("z-index", zIndex);
9770 if(x !== undefined && y !== undefined){
9772 }else if(x !== undefined){
9774 }else if(y !== undefined){
9780 * Clear positioning back to the default when the document was loaded
9781 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9782 * @return {Roo.Element} this
9784 clearPositioning : function(value){
9792 "position" : "static"
9798 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9799 * snapshot before performing an update and then restoring the element.
9802 getPositioning : function(){
9803 var l = this.getStyle("left");
9804 var t = this.getStyle("top");
9806 "position" : this.getStyle("position"),
9808 "right" : l ? "" : this.getStyle("right"),
9810 "bottom" : t ? "" : this.getStyle("bottom"),
9811 "z-index" : this.getStyle("z-index")
9816 * Gets the width of the border(s) for the specified side(s)
9817 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9818 * passing lr would get the border (l)eft width + the border (r)ight width.
9819 * @return {Number} The width of the sides passed added together
9821 getBorderWidth : function(side){
9822 return this.addStyles(side, El.borders);
9826 * Gets the width of the padding(s) for the specified side(s)
9827 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9828 * passing lr would get the padding (l)eft + the padding (r)ight.
9829 * @return {Number} The padding of the sides passed added together
9831 getPadding : function(side){
9832 return this.addStyles(side, El.paddings);
9836 * Set positioning with an object returned by getPositioning().
9837 * @param {Object} posCfg
9838 * @return {Roo.Element} this
9840 setPositioning : function(pc){
9841 this.applyStyles(pc);
9842 if(pc.right == "auto"){
9843 this.dom.style.right = "";
9845 if(pc.bottom == "auto"){
9846 this.dom.style.bottom = "";
9852 fixDisplay : function(){
9853 if(this.getStyle("display") == "none"){
9854 this.setStyle("visibility", "hidden");
9855 this.setStyle("display", this.originalDisplay); // first try reverting to default
9856 if(this.getStyle("display") == "none"){ // if that fails, default to block
9857 this.setStyle("display", "block");
9863 * Quick set left and top adding default units
9864 * @param {String} left The left CSS property value
9865 * @param {String} top The top CSS property value
9866 * @return {Roo.Element} this
9868 setLeftTop : function(left, top){
9869 this.dom.style.left = this.addUnits(left);
9870 this.dom.style.top = this.addUnits(top);
9875 * Move this element relative to its current position.
9876 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9877 * @param {Number} distance How far to move the element in pixels
9878 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9879 * @return {Roo.Element} this
9881 move : function(direction, distance, animate){
9882 var xy = this.getXY();
9883 direction = direction.toLowerCase();
9887 this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9891 this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9896 this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9901 this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9908 * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9909 * @return {Roo.Element} this
9912 if(!this.isClipped){
9913 this.isClipped = true;
9914 this.originalClip = {
9915 "o": this.getStyle("overflow"),
9916 "x": this.getStyle("overflow-x"),
9917 "y": this.getStyle("overflow-y")
9919 this.setStyle("overflow", "hidden");
9920 this.setStyle("overflow-x", "hidden");
9921 this.setStyle("overflow-y", "hidden");
9927 * Return clipping (overflow) to original clipping before clip() was called
9928 * @return {Roo.Element} this
9930 unclip : function(){
9932 this.isClipped = false;
9933 var o = this.originalClip;
9934 if(o.o){this.setStyle("overflow", o.o);}
9935 if(o.x){this.setStyle("overflow-x", o.x);}
9936 if(o.y){this.setStyle("overflow-y", o.y);}
9943 * Gets the x,y coordinates specified by the anchor position on the element.
9944 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo} for details on supported anchor positions.
9945 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9946 * {width: (target width), height: (target height)} (defaults to the element's current size)
9947 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9948 * @return {Array} [x, y] An array containing the element's x and y coordinates
9950 getAnchorXY : function(anchor, local, s){
9951 //Passing a different size is useful for pre-calculating anchors,
9952 //especially for anchored animations that change the el size.
9954 var w, h, vp = false;
9957 if(d == document.body || d == document){
9959 w = D.getViewWidth(); h = D.getViewHeight();
9961 w = this.getWidth(); h = this.getHeight();
9964 w = s.width; h = s.height;
9966 var x = 0, y = 0, r = Math.round;
9967 switch((anchor || "tl").toLowerCase()){
10005 if(local === true){
10009 var sc = this.getScroll();
10010 return [x + sc.left, y + sc.top];
10012 //Add the element's offset xy
10013 var o = this.getXY();
10014 return [x+o[0], y+o[1]];
10018 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10019 * supported position values.
10020 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10021 * @param {String} position The position to align to.
10022 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10023 * @return {Array} [x, y]
10025 getAlignToXY : function(el, p, o)
10030 throw "Element.alignTo with an element that doesn't exist";
10032 var c = false; //constrain to viewport
10033 var p1 = "", p2 = "";
10038 }else if(p == "?"){
10040 }else if(p.indexOf("-") == -1){
10043 p = p.toLowerCase();
10044 var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10046 throw "Element.alignTo with an invalid alignment " + p;
10048 p1 = m[1]; p2 = m[2]; c = !!m[3];
10050 //Subtract the aligned el's internal xy from the target's offset xy
10051 //plus custom offset to get the aligned el's new offset xy
10052 var a1 = this.getAnchorXY(p1, true);
10053 var a2 = el.getAnchorXY(p2, false);
10054 var x = a2[0] - a1[0] + o[0];
10055 var y = a2[1] - a1[1] + o[1];
10057 //constrain the aligned el to viewport if necessary
10058 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10059 // 5px of margin for ie
10060 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10062 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10063 //perpendicular to the vp border, allow the aligned el to slide on that border,
10064 //otherwise swap the aligned el to the opposite border of the target.
10065 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10066 var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10067 var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t") );
10068 var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10070 var doc = document;
10071 var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10072 var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10074 if((x+w) > dw + scrollX){
10075 x = swapX ? r.left-w : dw+scrollX-w;
10078 x = swapX ? r.right : scrollX;
10080 if((y+h) > dh + scrollY){
10081 y = swapY ? r.top-h : dh+scrollY-h;
10084 y = swapY ? r.bottom : scrollY;
10091 getConstrainToXY : function(){
10092 var os = {top:0, left:0, bottom:0, right: 0};
10094 return function(el, local, offsets, proposedXY){
10096 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10098 var vw, vh, vx = 0, vy = 0;
10099 if(el.dom == document.body || el.dom == document){
10100 vw = Roo.lib.Dom.getViewWidth();
10101 vh = Roo.lib.Dom.getViewHeight();
10103 vw = el.dom.clientWidth;
10104 vh = el.dom.clientHeight;
10106 var vxy = el.getXY();
10112 var s = el.getScroll();
10114 vx += offsets.left + s.left;
10115 vy += offsets.top + s.top;
10117 vw -= offsets.right;
10118 vh -= offsets.bottom;
10123 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10124 var x = xy[0], y = xy[1];
10125 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10127 // only move it if it needs it
10130 // first validate right/bottom
10139 // then make sure top/left isn't negative
10148 return moved ? [x, y] : false;
10153 adjustForConstraints : function(xy, parent, offsets){
10154 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
10158 * Aligns this element with another element relative to the specified anchor points. If the other element is the
10159 * document it aligns it to the viewport.
10160 * The position parameter is optional, and can be specified in any one of the following formats:
10162 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10163 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10164 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
10165 * deprecated in favor of the newer two anchor syntax below</i>.</li>
10166 * <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
10167 * element's anchor point, and the second value is used as the target's anchor point.</li>
10169 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
10170 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10171 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
10172 * that specified in order to enforce the viewport constraints.
10173 * Following are all of the supported anchor positions:
10176 ----- -----------------------------
10177 tl The top left corner (default)
10178 t The center of the top edge
10179 tr The top right corner
10180 l The center of the left edge
10181 c In the center of the element
10182 r The center of the right edge
10183 bl The bottom left corner
10184 b The center of the bottom edge
10185 br The bottom right corner
10189 // align el to other-el using the default positioning ("tl-bl", non-constrained)
10190 el.alignTo("other-el");
10192 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10193 el.alignTo("other-el", "tr?");
10195 // align the bottom right corner of el with the center left edge of other-el
10196 el.alignTo("other-el", "br-l?");
10198 // align the center of el with the bottom left corner of other-el and
10199 // adjust the x position by -6 pixels (and the y position by 0)
10200 el.alignTo("other-el", "c-bl", [-6, 0]);
10202 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10203 * @param {String} position The position to align to.
10204 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10205 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10206 * @return {Roo.Element} this
10208 alignTo : function(element, position, offsets, animate){
10209 var xy = this.getAlignToXY(element, position, offsets);
10210 this.setXY(xy, this.preanim(arguments, 3));
10215 * Anchors an element to another element and realigns it when the window is resized.
10216 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10217 * @param {String} position The position to align to.
10218 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10219 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10220 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10221 * is a number, it is used as the buffer delay (defaults to 50ms).
10222 * @param {Function} callback The function to call after the animation finishes
10223 * @return {Roo.Element} this
10225 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10226 var action = function(){
10227 this.alignTo(el, alignment, offsets, animate);
10228 Roo.callback(callback, this);
10230 Roo.EventManager.onWindowResize(action, this);
10231 var tm = typeof monitorScroll;
10232 if(tm != 'undefined'){
10233 Roo.EventManager.on(window, 'scroll', action, this,
10234 {buffer: tm == 'number' ? monitorScroll : 50});
10236 action.call(this); // align immediately
10240 * Clears any opacity settings from this element. Required in some cases for IE.
10241 * @return {Roo.Element} this
10243 clearOpacity : function(){
10244 if (window.ActiveXObject) {
10245 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10246 this.dom.style.filter = "";
10249 this.dom.style.opacity = "";
10250 this.dom.style["-moz-opacity"] = "";
10251 this.dom.style["-khtml-opacity"] = "";
10257 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10258 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10259 * @return {Roo.Element} this
10261 hide : function(animate){
10262 this.setVisible(false, this.preanim(arguments, 0));
10267 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10268 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10269 * @return {Roo.Element} this
10271 show : function(animate){
10272 this.setVisible(true, this.preanim(arguments, 0));
10277 * @private Test if size has a unit, otherwise appends the default
10279 addUnits : function(size){
10280 return Roo.Element.addUnits(size, this.defaultUnit);
10284 * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10285 * @return {Roo.Element} this
10287 beginMeasure : function(){
10289 if(el.offsetWidth || el.offsetHeight){
10290 return this; // offsets work already
10293 var p = this.dom, b = document.body; // start with this element
10294 while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10295 var pe = Roo.get(p);
10296 if(pe.getStyle('display') == 'none'){
10297 changed.push({el: p, visibility: pe.getStyle("visibility")});
10298 p.style.visibility = "hidden";
10299 p.style.display = "block";
10303 this._measureChanged = changed;
10309 * Restores displays to before beginMeasure was called
10310 * @return {Roo.Element} this
10312 endMeasure : function(){
10313 var changed = this._measureChanged;
10315 for(var i = 0, len = changed.length; i < len; i++) {
10316 var r = changed[i];
10317 r.el.style.visibility = r.visibility;
10318 r.el.style.display = "none";
10320 this._measureChanged = null;
10326 * Update the innerHTML of this element, optionally searching for and processing scripts
10327 * @param {String} html The new HTML
10328 * @param {Boolean} loadScripts (optional) true to look for and process scripts
10329 * @param {Function} callback For async script loading you can be noticed when the update completes
10330 * @return {Roo.Element} this
10332 update : function(html, loadScripts, callback){
10333 if(typeof html == "undefined"){
10336 if(loadScripts !== true){
10337 this.dom.innerHTML = html;
10338 if(typeof callback == "function"){
10344 var dom = this.dom;
10346 html += '<span id="' + id + '"></span>';
10348 E.onAvailable(id, function(){
10349 var hd = document.getElementsByTagName("head")[0];
10350 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10351 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10352 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10355 while(match = re.exec(html)){
10356 var attrs = match[1];
10357 var srcMatch = attrs ? attrs.match(srcRe) : false;
10358 if(srcMatch && srcMatch[2]){
10359 var s = document.createElement("script");
10360 s.src = srcMatch[2];
10361 var typeMatch = attrs.match(typeRe);
10362 if(typeMatch && typeMatch[2]){
10363 s.type = typeMatch[2];
10366 }else if(match[2] && match[2].length > 0){
10367 if(window.execScript) {
10368 window.execScript(match[2]);
10376 window.eval(match[2]);
10380 var el = document.getElementById(id);
10381 if(el){el.parentNode.removeChild(el);}
10382 if(typeof callback == "function"){
10386 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10391 * Direct access to the UpdateManager update() method (takes the same parameters).
10392 * @param {String/Function} url The url for this request or a function to call to get the url
10393 * @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}
10394 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10395 * @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.
10396 * @return {Roo.Element} this
10399 var um = this.getUpdateManager();
10400 um.update.apply(um, arguments);
10405 * Gets this element's UpdateManager
10406 * @return {Roo.UpdateManager} The UpdateManager
10408 getUpdateManager : function(){
10409 if(!this.updateManager){
10410 this.updateManager = new Roo.UpdateManager(this);
10412 return this.updateManager;
10416 * Disables text selection for this element (normalized across browsers)
10417 * @return {Roo.Element} this
10419 unselectable : function(){
10420 this.dom.unselectable = "on";
10421 this.swallowEvent("selectstart", true);
10422 this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10423 this.addClass("x-unselectable");
10428 * Calculates the x, y to center this element on the screen
10429 * @return {Array} The x, y values [x, y]
10431 getCenterXY : function(){
10432 return this.getAlignToXY(document, 'c-c');
10436 * Centers the Element in either the viewport, or another Element.
10437 * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10439 center : function(centerIn){
10440 this.alignTo(centerIn || document, 'c-c');
10445 * Tests various css rules/browsers to determine if this element uses a border box
10446 * @return {Boolean}
10448 isBorderBox : function(){
10449 return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10453 * Return a box {x, y, width, height} that can be used to set another elements
10454 * size/location to match this element.
10455 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10456 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10457 * @return {Object} box An object in the format {x, y, width, height}
10459 getBox : function(contentBox, local){
10464 var left = parseInt(this.getStyle("left"), 10) || 0;
10465 var top = parseInt(this.getStyle("top"), 10) || 0;
10468 var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10470 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10472 var l = this.getBorderWidth("l")+this.getPadding("l");
10473 var r = this.getBorderWidth("r")+this.getPadding("r");
10474 var t = this.getBorderWidth("t")+this.getPadding("t");
10475 var b = this.getBorderWidth("b")+this.getPadding("b");
10476 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)};
10478 bx.right = bx.x + bx.width;
10479 bx.bottom = bx.y + bx.height;
10484 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10485 for more information about the sides.
10486 * @param {String} sides
10489 getFrameWidth : function(sides, onlyContentBox){
10490 return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10494 * 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.
10495 * @param {Object} box The box to fill {x, y, width, height}
10496 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10497 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10498 * @return {Roo.Element} this
10500 setBox : function(box, adjust, animate){
10501 var w = box.width, h = box.height;
10502 if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10503 w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10504 h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10506 this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10511 * Forces the browser to repaint this element
10512 * @return {Roo.Element} this
10514 repaint : function(){
10515 var dom = this.dom;
10516 this.addClass("x-repaint");
10517 setTimeout(function(){
10518 Roo.get(dom).removeClass("x-repaint");
10524 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10525 * then it returns the calculated width of the sides (see getPadding)
10526 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10527 * @return {Object/Number}
10529 getMargins : function(side){
10532 top: parseInt(this.getStyle("margin-top"), 10) || 0,
10533 left: parseInt(this.getStyle("margin-left"), 10) || 0,
10534 bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10535 right: parseInt(this.getStyle("margin-right"), 10) || 0
10538 return this.addStyles(side, El.margins);
10543 addStyles : function(sides, styles){
10545 for(var i = 0, len = sides.length; i < len; i++){
10546 v = this.getStyle(styles[sides.charAt(i)]);
10548 w = parseInt(v, 10);
10556 * Creates a proxy element of this element
10557 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10558 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10559 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10560 * @return {Roo.Element} The new proxy element
10562 createProxy : function(config, renderTo, matchBox){
10564 renderTo = Roo.getDom(renderTo);
10566 renderTo = document.body;
10568 config = typeof config == "object" ?
10569 config : {tag : "div", cls: config};
10570 var proxy = Roo.DomHelper.append(renderTo, config, true);
10572 proxy.setBox(this.getBox());
10578 * Puts a mask over this element to disable user interaction. Requires core.css.
10579 * This method can only be applied to elements which accept child nodes.
10580 * @param {String} msg (optional) A message to display in the mask
10581 * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10582 * @return {Element} The mask element
10584 mask : function(msg, msgCls)
10586 if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10587 this.setStyle("position", "relative");
10590 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10593 this.addClass("x-masked");
10594 this._mask.setDisplayed(true);
10598 var dom = this.dom;
10599 while (dom && dom.style) {
10600 if (!isNaN(parseInt(dom.style.zIndex))) {
10601 z = Math.max(z, parseInt(dom.style.zIndex));
10603 dom = dom.parentNode;
10605 // if we are masking the body - then it hides everything..
10606 if (this.dom == document.body) {
10608 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10609 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10612 if(typeof msg == 'string'){
10613 if(!this._maskMsg){
10614 this._maskMsg = Roo.DomHelper.append(this.dom, {
10615 cls: "roo-el-mask-msg",
10619 cls: 'fa fa-spinner fa-spin'
10627 var mm = this._maskMsg;
10628 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10629 if (mm.dom.lastChild) { // weird IE issue?
10630 mm.dom.lastChild.innerHTML = msg;
10632 mm.setDisplayed(true);
10634 mm.setStyle('z-index', z + 102);
10636 if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10637 this._mask.setHeight(this.getHeight());
10639 this._mask.setStyle('z-index', z + 100);
10645 * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10646 * it is cached for reuse.
10648 unmask : function(removeEl){
10650 if(removeEl === true){
10651 this._mask.remove();
10654 this._maskMsg.remove();
10655 delete this._maskMsg;
10658 this._mask.setDisplayed(false);
10660 this._maskMsg.setDisplayed(false);
10664 this.removeClass("x-masked");
10668 * Returns true if this element is masked
10669 * @return {Boolean}
10671 isMasked : function(){
10672 return this._mask && this._mask.isVisible();
10676 * Creates an iframe shim for this element to keep selects and other windowed objects from
10678 * @return {Roo.Element} The new shim element
10680 createShim : function(){
10681 var el = document.createElement('iframe');
10682 el.frameBorder = 'no';
10683 el.className = 'roo-shim';
10684 if(Roo.isIE && Roo.isSecure){
10685 el.src = Roo.SSL_SECURE_URL;
10687 var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10688 shim.autoBoxAdjust = false;
10693 * Removes this element from the DOM and deletes it from the cache
10695 remove : function(){
10696 if(this.dom.parentNode){
10697 this.dom.parentNode.removeChild(this.dom);
10699 delete El.cache[this.dom.id];
10703 * Sets up event handlers to add and remove a css class when the mouse is over this element
10704 * @param {String} className
10705 * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10706 * mouseout events for children elements
10707 * @return {Roo.Element} this
10709 addClassOnOver : function(className, preventFlicker){
10710 this.on("mouseover", function(){
10711 Roo.fly(this, '_internal').addClass(className);
10713 var removeFn = function(e){
10714 if(preventFlicker !== true || !e.within(this, true)){
10715 Roo.fly(this, '_internal').removeClass(className);
10718 this.on("mouseout", removeFn, this.dom);
10723 * Sets up event handlers to add and remove a css class when this element has the focus
10724 * @param {String} className
10725 * @return {Roo.Element} this
10727 addClassOnFocus : function(className){
10728 this.on("focus", function(){
10729 Roo.fly(this, '_internal').addClass(className);
10731 this.on("blur", function(){
10732 Roo.fly(this, '_internal').removeClass(className);
10737 * 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)
10738 * @param {String} className
10739 * @return {Roo.Element} this
10741 addClassOnClick : function(className){
10742 var dom = this.dom;
10743 this.on("mousedown", function(){
10744 Roo.fly(dom, '_internal').addClass(className);
10745 var d = Roo.get(document);
10746 var fn = function(){
10747 Roo.fly(dom, '_internal').removeClass(className);
10748 d.removeListener("mouseup", fn);
10750 d.on("mouseup", fn);
10756 * Stops the specified event from bubbling and optionally prevents the default action
10757 * @param {String} eventName
10758 * @param {Boolean} preventDefault (optional) true to prevent the default action too
10759 * @return {Roo.Element} this
10761 swallowEvent : function(eventName, preventDefault){
10762 var fn = function(e){
10763 e.stopPropagation();
10764 if(preventDefault){
10765 e.preventDefault();
10768 if(eventName instanceof Array){
10769 for(var i = 0, len = eventName.length; i < len; i++){
10770 this.on(eventName[i], fn);
10774 this.on(eventName, fn);
10781 fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10784 * Sizes this element to its parent element's dimensions performing
10785 * neccessary box adjustments.
10786 * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10787 * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10788 * @return {Roo.Element} this
10790 fitToParent : function(monitorResize, targetParent) {
10791 Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10792 this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10793 if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10796 var p = Roo.get(targetParent || this.dom.parentNode);
10797 this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10798 if (monitorResize === true) {
10799 this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10800 Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10806 * Gets the next sibling, skipping text nodes
10807 * @return {HTMLElement} The next sibling or null
10809 getNextSibling : function(){
10810 var n = this.dom.nextSibling;
10811 while(n && n.nodeType != 1){
10818 * Gets the previous sibling, skipping text nodes
10819 * @return {HTMLElement} The previous sibling or null
10821 getPrevSibling : function(){
10822 var n = this.dom.previousSibling;
10823 while(n && n.nodeType != 1){
10824 n = n.previousSibling;
10831 * Appends the passed element(s) to this element
10832 * @param {String/HTMLElement/Array/Element/CompositeElement} el
10833 * @return {Roo.Element} this
10835 appendChild: function(el){
10842 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10843 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
10844 * automatically generated with the specified attributes.
10845 * @param {HTMLElement} insertBefore (optional) a child element of this element
10846 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10847 * @return {Roo.Element} The new child element
10849 createChild: function(config, insertBefore, returnDom){
10850 config = config || {tag:'div'};
10852 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10854 return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
10858 * Appends this element to the passed element
10859 * @param {String/HTMLElement/Element} el The new parent element
10860 * @return {Roo.Element} this
10862 appendTo: function(el){
10863 el = Roo.getDom(el);
10864 el.appendChild(this.dom);
10869 * Inserts this element before the passed element in the DOM
10870 * @param {String/HTMLElement/Element} el The element to insert before
10871 * @return {Roo.Element} this
10873 insertBefore: function(el){
10874 el = Roo.getDom(el);
10875 el.parentNode.insertBefore(this.dom, el);
10880 * Inserts this element after the passed element in the DOM
10881 * @param {String/HTMLElement/Element} el The element to insert after
10882 * @return {Roo.Element} this
10884 insertAfter: function(el){
10885 el = Roo.getDom(el);
10886 el.parentNode.insertBefore(this.dom, el.nextSibling);
10891 * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10892 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10893 * @return {Roo.Element} The new child
10895 insertFirst: function(el, returnDom){
10897 if(typeof el == 'object' && !el.nodeType){ // dh config
10898 return this.createChild(el, this.dom.firstChild, returnDom);
10900 el = Roo.getDom(el);
10901 this.dom.insertBefore(el, this.dom.firstChild);
10902 return !returnDom ? Roo.get(el) : el;
10907 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10908 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10909 * @param {String} where (optional) 'before' or 'after' defaults to before
10910 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10911 * @return {Roo.Element} the inserted Element
10913 insertSibling: function(el, where, returnDom){
10914 where = where ? where.toLowerCase() : 'before';
10916 var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10918 if(typeof el == 'object' && !el.nodeType){ // dh config
10919 if(where == 'after' && !this.dom.nextSibling){
10920 rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10922 rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10926 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10927 where == 'before' ? this.dom : this.dom.nextSibling);
10936 * Creates and wraps this element with another element
10937 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10938 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10939 * @return {HTMLElement/Element} The newly created wrapper element
10941 wrap: function(config, returnDom){
10943 config = {tag: "div"};
10945 var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10946 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10951 * Replaces the passed element with this element
10952 * @param {String/HTMLElement/Element} el The element to replace
10953 * @return {Roo.Element} this
10955 replace: function(el){
10957 this.insertBefore(el);
10963 * Inserts an html fragment into this element
10964 * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10965 * @param {String} html The HTML fragment
10966 * @param {Boolean} returnEl True to return an Roo.Element
10967 * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10969 insertHtml : function(where, html, returnEl){
10970 var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10971 return returnEl ? Roo.get(el) : el;
10975 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10976 * @param {Object} o The object with the attributes
10977 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10978 * @return {Roo.Element} this
10980 set : function(o, useSet){
10982 useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10983 for(var attr in o){
10984 if(attr == "style" || typeof o[attr] == "function") { continue; }
10986 el.className = o["cls"];
10989 el.setAttribute(attr, o[attr]);
10991 el[attr] = o[attr];
10996 Roo.DomHelper.applyStyles(el, o.style);
11002 * Convenience method for constructing a KeyMap
11003 * @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:
11004 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11005 * @param {Function} fn The function to call
11006 * @param {Object} scope (optional) The scope of the function
11007 * @return {Roo.KeyMap} The KeyMap created
11009 addKeyListener : function(key, fn, scope){
11011 if(typeof key != "object" || key instanceof Array){
11027 return new Roo.KeyMap(this, config);
11031 * Creates a KeyMap for this element
11032 * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11033 * @return {Roo.KeyMap} The KeyMap created
11035 addKeyMap : function(config){
11036 return new Roo.KeyMap(this, config);
11040 * Returns true if this element is scrollable.
11041 * @return {Boolean}
11043 isScrollable : function(){
11044 var dom = this.dom;
11045 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11049 * 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().
11050 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11051 * @param {Number} value The new scroll value
11052 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11053 * @return {Element} this
11056 scrollTo : function(side, value, animate){
11057 var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11058 if(!animate || !A){
11059 this.dom[prop] = value;
11061 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11062 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11068 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11069 * within this element's scrollable range.
11070 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11071 * @param {Number} distance How far to scroll the element in pixels
11072 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11073 * @return {Boolean} Returns true if a scroll was triggered or false if the element
11074 * was scrolled as far as it could go.
11076 scroll : function(direction, distance, animate){
11077 if(!this.isScrollable()){
11081 var l = el.scrollLeft, t = el.scrollTop;
11082 var w = el.scrollWidth, h = el.scrollHeight;
11083 var cw = el.clientWidth, ch = el.clientHeight;
11084 direction = direction.toLowerCase();
11085 var scrolled = false;
11086 var a = this.preanim(arguments, 2);
11091 var v = Math.min(l + distance, w-cw);
11092 this.scrollTo("left", v, a);
11099 var v = Math.max(l - distance, 0);
11100 this.scrollTo("left", v, a);
11108 var v = Math.max(t - distance, 0);
11109 this.scrollTo("top", v, a);
11117 var v = Math.min(t + distance, h-ch);
11118 this.scrollTo("top", v, a);
11127 * Translates the passed page coordinates into left/top css values for this element
11128 * @param {Number/Array} x The page x or an array containing [x, y]
11129 * @param {Number} y The page y
11130 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11132 translatePoints : function(x, y){
11133 if(typeof x == 'object' || x instanceof Array){
11134 y = x[1]; x = x[0];
11136 var p = this.getStyle('position');
11137 var o = this.getXY();
11139 var l = parseInt(this.getStyle('left'), 10);
11140 var t = parseInt(this.getStyle('top'), 10);
11143 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11146 t = (p == "relative") ? 0 : this.dom.offsetTop;
11149 return {left: (x - o[0] + l), top: (y - o[1] + t)};
11153 * Returns the current scroll position of the element.
11154 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11156 getScroll : function(){
11157 var d = this.dom, doc = document;
11158 if(d == doc || d == doc.body){
11159 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11160 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11161 return {left: l, top: t};
11163 return {left: d.scrollLeft, top: d.scrollTop};
11168 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11169 * are convert to standard 6 digit hex color.
11170 * @param {String} attr The css attribute
11171 * @param {String} defaultValue The default value to use when a valid color isn't found
11172 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11175 getColor : function(attr, defaultValue, prefix){
11176 var v = this.getStyle(attr);
11177 if(!v || v == "transparent" || v == "inherit") {
11178 return defaultValue;
11180 var color = typeof prefix == "undefined" ? "#" : prefix;
11181 if(v.substr(0, 4) == "rgb("){
11182 var rvs = v.slice(4, v.length -1).split(",");
11183 for(var i = 0; i < 3; i++){
11184 var h = parseInt(rvs[i]).toString(16);
11191 if(v.substr(0, 1) == "#"){
11192 if(v.length == 4) {
11193 for(var i = 1; i < 4; i++){
11194 var c = v.charAt(i);
11197 }else if(v.length == 7){
11198 color += v.substr(1);
11202 return(color.length > 5 ? color.toLowerCase() : defaultValue);
11206 * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11207 * gradient background, rounded corners and a 4-way shadow.
11208 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11209 * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11210 * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11211 * @return {Roo.Element} this
11213 boxWrap : function(cls){
11214 cls = cls || 'x-box';
11215 var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11216 el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11221 * Returns the value of a namespaced attribute from the element's underlying DOM node.
11222 * @param {String} namespace The namespace in which to look for the attribute
11223 * @param {String} name The attribute name
11224 * @return {String} The attribute value
11226 getAttributeNS : Roo.isIE ? function(ns, name){
11228 var type = typeof d[ns+":"+name];
11229 if(type != 'undefined' && type != 'unknown'){
11230 return d[ns+":"+name];
11233 } : function(ns, name){
11235 return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11240 * Sets or Returns the value the dom attribute value
11241 * @param {String|Object} name The attribute name (or object to set multiple attributes)
11242 * @param {String} value (optional) The value to set the attribute to
11243 * @return {String} The attribute value
11245 attr : function(name){
11246 if (arguments.length > 1) {
11247 this.dom.setAttribute(name, arguments[1]);
11248 return arguments[1];
11250 if (typeof(name) == 'object') {
11251 for(var i in name) {
11252 this.attr(i, name[i]);
11258 if (!this.dom.hasAttribute(name)) {
11261 return this.dom.getAttribute(name);
11268 var ep = El.prototype;
11271 * Appends an event handler (Shorthand for addListener)
11272 * @param {String} eventName The type of event to append
11273 * @param {Function} fn The method the event invokes
11274 * @param {Object} scope (optional) The scope (this object) of the fn
11275 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
11278 ep.on = ep.addListener;
11279 // backwards compat
11280 ep.mon = ep.addListener;
11283 * Removes an event handler from this element (shorthand for removeListener)
11284 * @param {String} eventName the type of event to remove
11285 * @param {Function} fn the method the event invokes
11286 * @return {Roo.Element} this
11289 ep.un = ep.removeListener;
11292 * true to automatically adjust width and height settings for box-model issues (default to true)
11294 ep.autoBoxAdjust = true;
11297 El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11300 El.addUnits = function(v, defaultUnit){
11301 if(v === "" || v == "auto"){
11304 if(v === undefined){
11307 if(typeof v == "number" || !El.unitPattern.test(v)){
11308 return v + (defaultUnit || 'px');
11313 // special markup used throughout Roo when box wrapping elements
11314 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>';
11316 * Visibility mode constant - Use visibility to hide element
11322 * Visibility mode constant - Use display to hide element
11328 El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11329 El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11330 El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11342 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11343 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11344 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11345 * @return {Element} The Element object
11348 El.get = function(el){
11350 if(!el){ return null; }
11351 if(typeof el == "string"){ // element id
11352 if(!(elm = document.getElementById(el))){
11355 if(ex = El.cache[el]){
11358 ex = El.cache[el] = new El(elm);
11361 }else if(el.tagName){ // dom element
11365 if(ex = El.cache[id]){
11368 ex = El.cache[id] = new El(el);
11371 }else if(el instanceof El){
11373 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11374 // catch case where it hasn't been appended
11375 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11378 }else if(el.isComposite){
11380 }else if(el instanceof Array){
11381 return El.select(el);
11382 }else if(el == document){
11383 // create a bogus element object representing the document object
11385 var f = function(){};
11386 f.prototype = El.prototype;
11388 docEl.dom = document;
11396 El.uncache = function(el){
11397 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11399 delete El.cache[a[i].id || a[i]];
11405 // Garbage collection - uncache elements/purge listeners on orphaned elements
11406 // so we don't hold a reference and cause the browser to retain them
11407 El.garbageCollect = function(){
11408 if(!Roo.enableGarbageCollector){
11409 clearInterval(El.collectorThread);
11412 for(var eid in El.cache){
11413 var el = El.cache[eid], d = el.dom;
11414 // -------------------------------------------------------
11415 // Determining what is garbage:
11416 // -------------------------------------------------------
11418 // dom node is null, definitely garbage
11419 // -------------------------------------------------------
11421 // no parentNode == direct orphan, definitely garbage
11422 // -------------------------------------------------------
11423 // !d.offsetParent && !document.getElementById(eid)
11424 // display none elements have no offsetParent so we will
11425 // also try to look it up by it's id. However, check
11426 // offsetParent first so we don't do unneeded lookups.
11427 // This enables collection of elements that are not orphans
11428 // directly, but somewhere up the line they have an orphan
11430 // -------------------------------------------------------
11431 if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11432 delete El.cache[eid];
11433 if(d && Roo.enableListenerCollection){
11439 El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11443 El.Flyweight = function(dom){
11446 El.Flyweight.prototype = El.prototype;
11448 El._flyweights = {};
11450 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11451 * the dom node can be overwritten by other code.
11452 * @param {String/HTMLElement} el The dom node or id
11453 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11454 * prevent conflicts (e.g. internally Roo uses "_internal")
11456 * @return {Element} The shared Element object
11458 El.fly = function(el, named){
11459 named = named || '_global';
11460 el = Roo.getDom(el);
11464 if(!El._flyweights[named]){
11465 El._flyweights[named] = new El.Flyweight();
11467 El._flyweights[named].dom = el;
11468 return El._flyweights[named];
11472 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11473 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11474 * Shorthand of {@link Roo.Element#get}
11475 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11476 * @return {Element} The Element object
11482 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11483 * the dom node can be overwritten by other code.
11484 * Shorthand of {@link Roo.Element#fly}
11485 * @param {String/HTMLElement} el The dom node or id
11486 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11487 * prevent conflicts (e.g. internally Roo uses "_internal")
11489 * @return {Element} The shared Element object
11495 // speedy lookup for elements never to box adjust
11496 var noBoxAdjust = Roo.isStrict ? {
11499 input:1, select:1, textarea:1
11501 if(Roo.isIE || Roo.isGecko){
11502 noBoxAdjust['button'] = 1;
11506 Roo.EventManager.on(window, 'unload', function(){
11508 delete El._flyweights;
11516 Roo.Element.selectorFunction = Roo.DomQuery.select;
11519 Roo.Element.select = function(selector, unique, root){
11521 if(typeof selector == "string"){
11522 els = Roo.Element.selectorFunction(selector, root);
11523 }else if(selector.length !== undefined){
11526 throw "Invalid selector";
11528 if(unique === true){
11529 return new Roo.CompositeElement(els);
11531 return new Roo.CompositeElementLite(els);
11535 * Selects elements based on the passed CSS selector to enable working on them as 1.
11536 * @param {String/Array} selector The CSS selector or an array of elements
11537 * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11538 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11539 * @return {CompositeElementLite/CompositeElement}
11543 Roo.select = Roo.Element.select;
11560 * Ext JS Library 1.1.1
11561 * Copyright(c) 2006-2007, Ext JS, LLC.
11563 * Originally Released Under LGPL - original licence link has changed is not relivant.
11566 * <script type="text/javascript">
11571 //Notifies Element that fx methods are available
11572 Roo.enableFx = true;
11576 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
11577 * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11578 * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the
11579 * Element effects to work.</p><br/>
11581 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11582 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11583 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11584 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
11585 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11586 * expected results and should be done with care.</p><br/>
11588 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11589 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
11592 ----- -----------------------------
11593 tl The top left corner
11594 t The center of the top edge
11595 tr The top right corner
11596 l The center of the left edge
11597 r The center of the right edge
11598 bl The bottom left corner
11599 b The center of the bottom edge
11600 br The bottom right corner
11602 * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11603 * below are common options that can be passed to any Fx method.</b>
11604 * @cfg {Function} callback A function called when the effect is finished
11605 * @cfg {Object} scope The scope of the effect function
11606 * @cfg {String} easing A valid Easing value for the effect
11607 * @cfg {String} afterCls A css class to apply after the effect
11608 * @cfg {Number} duration The length of time (in seconds) that the effect should last
11609 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11610 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
11611 * effects that end with the element being visually hidden, ignored otherwise)
11612 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11613 * a function which returns such a specification that will be applied to the Element after the effect finishes
11614 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11615 * @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
11616 * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11620 * Slides the element into view. An anchor point can be optionally passed to set the point of
11621 * origin for the slide effect. This function automatically handles wrapping the element with
11622 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11625 // default: slide the element in from the top
11628 // custom: slide the element in from the right with a 2-second duration
11629 el.slideIn('r', { duration: 2 });
11631 // common config options shown with default values
11637 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11638 * @param {Object} options (optional) Object literal with any of the Fx config options
11639 * @return {Roo.Element} The Element
11641 slideIn : function(anchor, o){
11642 var el = this.getFxEl();
11645 el.queueFx(o, function(){
11647 anchor = anchor || "t";
11649 // fix display to visibility
11652 // restore values after effect
11653 var r = this.getFxRestore();
11654 var b = this.getBox();
11655 // fixed size for slide
11659 var wrap = this.fxWrap(r.pos, o, "hidden");
11661 var st = this.dom.style;
11662 st.visibility = "visible";
11663 st.position = "absolute";
11665 // clear out temp styles after slide and unwrap
11666 var after = function(){
11667 el.fxUnwrap(wrap, r.pos, o);
11668 st.width = r.width;
11669 st.height = r.height;
11672 // time to calc the positions
11673 var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11675 switch(anchor.toLowerCase()){
11677 wrap.setSize(b.width, 0);
11678 st.left = st.bottom = "0";
11682 wrap.setSize(0, b.height);
11683 st.right = st.top = "0";
11687 wrap.setSize(0, b.height);
11688 wrap.setX(b.right);
11689 st.left = st.top = "0";
11690 a = {width: bw, points: pt};
11693 wrap.setSize(b.width, 0);
11694 wrap.setY(b.bottom);
11695 st.left = st.top = "0";
11696 a = {height: bh, points: pt};
11699 wrap.setSize(0, 0);
11700 st.right = st.bottom = "0";
11701 a = {width: bw, height: bh};
11704 wrap.setSize(0, 0);
11705 wrap.setY(b.y+b.height);
11706 st.right = st.top = "0";
11707 a = {width: bw, height: bh, points: pt};
11710 wrap.setSize(0, 0);
11711 wrap.setXY([b.right, b.bottom]);
11712 st.left = st.top = "0";
11713 a = {width: bw, height: bh, points: pt};
11716 wrap.setSize(0, 0);
11717 wrap.setX(b.x+b.width);
11718 st.left = st.bottom = "0";
11719 a = {width: bw, height: bh, points: pt};
11722 this.dom.style.visibility = "visible";
11725 arguments.callee.anim = wrap.fxanim(a,
11735 * Slides the element out of view. An anchor point can be optionally passed to set the end point
11736 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
11737 * 'hidden') but block elements will still take up space in the document. The element must be removed
11738 * from the DOM using the 'remove' config option if desired. This function automatically handles
11739 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11742 // default: slide the element out to the top
11745 // custom: slide the element out to the right with a 2-second duration
11746 el.slideOut('r', { duration: 2 });
11748 // common config options shown with default values
11756 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11757 * @param {Object} options (optional) Object literal with any of the Fx config options
11758 * @return {Roo.Element} The Element
11760 slideOut : function(anchor, o){
11761 var el = this.getFxEl();
11764 el.queueFx(o, function(){
11766 anchor = anchor || "t";
11768 // restore values after effect
11769 var r = this.getFxRestore();
11771 var b = this.getBox();
11772 // fixed size for slide
11776 var wrap = this.fxWrap(r.pos, o, "visible");
11778 var st = this.dom.style;
11779 st.visibility = "visible";
11780 st.position = "absolute";
11784 var after = function(){
11786 el.setDisplayed(false);
11791 el.fxUnwrap(wrap, r.pos, o);
11793 st.width = r.width;
11794 st.height = r.height;
11799 var a, zero = {to: 0};
11800 switch(anchor.toLowerCase()){
11802 st.left = st.bottom = "0";
11803 a = {height: zero};
11806 st.right = st.top = "0";
11810 st.left = st.top = "0";
11811 a = {width: zero, points: {to:[b.right, b.y]}};
11814 st.left = st.top = "0";
11815 a = {height: zero, points: {to:[b.x, b.bottom]}};
11818 st.right = st.bottom = "0";
11819 a = {width: zero, height: zero};
11822 st.right = st.top = "0";
11823 a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11826 st.left = st.top = "0";
11827 a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11830 st.left = st.bottom = "0";
11831 a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11835 arguments.callee.anim = wrap.fxanim(a,
11845 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
11846 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
11847 * The element must be removed from the DOM using the 'remove' config option if desired.
11853 // common config options shown with default values
11861 * @param {Object} options (optional) Object literal with any of the Fx config options
11862 * @return {Roo.Element} The Element
11864 puff : function(o){
11865 var el = this.getFxEl();
11868 el.queueFx(o, function(){
11869 this.clearOpacity();
11872 // restore values after effect
11873 var r = this.getFxRestore();
11874 var st = this.dom.style;
11876 var after = function(){
11878 el.setDisplayed(false);
11885 el.setPositioning(r.pos);
11886 st.width = r.width;
11887 st.height = r.height;
11892 var width = this.getWidth();
11893 var height = this.getHeight();
11895 arguments.callee.anim = this.fxanim({
11896 width : {to: this.adjustWidth(width * 2)},
11897 height : {to: this.adjustHeight(height * 2)},
11898 points : {by: [-(width * .5), -(height * .5)]},
11900 fontSize: {to:200, unit: "%"}
11911 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11912 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
11913 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11919 // all config options shown with default values
11927 * @param {Object} options (optional) Object literal with any of the Fx config options
11928 * @return {Roo.Element} The Element
11930 switchOff : function(o){
11931 var el = this.getFxEl();
11934 el.queueFx(o, function(){
11935 this.clearOpacity();
11938 // restore values after effect
11939 var r = this.getFxRestore();
11940 var st = this.dom.style;
11942 var after = function(){
11944 el.setDisplayed(false);
11950 el.setPositioning(r.pos);
11951 st.width = r.width;
11952 st.height = r.height;
11957 this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11958 this.clearOpacity();
11962 points:{by:[0, this.getHeight() * .5]}
11963 }, o, 'motion', 0.3, 'easeIn', after);
11964 }).defer(100, this);
11971 * Highlights the Element by setting a color (applies to the background-color by default, but can be
11972 * changed using the "attr" config option) and then fading back to the original color. If no original
11973 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11976 // default: highlight background to yellow
11979 // custom: highlight foreground text to blue for 2 seconds
11980 el.highlight("0000ff", { attr: 'color', duration: 2 });
11982 // common config options shown with default values
11983 el.highlight("ffff9c", {
11984 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11985 endColor: (current color) or "ffffff",
11990 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11991 * @param {Object} options (optional) Object literal with any of the Fx config options
11992 * @return {Roo.Element} The Element
11994 highlight : function(color, o){
11995 var el = this.getFxEl();
11998 el.queueFx(o, function(){
11999 color = color || "ffff9c";
12000 attr = o.attr || "backgroundColor";
12002 this.clearOpacity();
12005 var origColor = this.getColor(attr);
12006 var restoreColor = this.dom.style[attr];
12007 endColor = (o.endColor || origColor) || "ffffff";
12009 var after = function(){
12010 el.dom.style[attr] = restoreColor;
12015 a[attr] = {from: color, to: endColor};
12016 arguments.callee.anim = this.fxanim(a,
12026 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12029 // default: a single light blue ripple
12032 // custom: 3 red ripples lasting 3 seconds total
12033 el.frame("ff0000", 3, { duration: 3 });
12035 // common config options shown with default values
12036 el.frame("C3DAF9", 1, {
12037 duration: 1 //duration of entire animation (not each individual ripple)
12038 // Note: Easing is not configurable and will be ignored if included
12041 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12042 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12043 * @param {Object} options (optional) Object literal with any of the Fx config options
12044 * @return {Roo.Element} The Element
12046 frame : function(color, count, o){
12047 var el = this.getFxEl();
12050 el.queueFx(o, function(){
12051 color = color || "#C3DAF9";
12052 if(color.length == 6){
12053 color = "#" + color;
12055 count = count || 1;
12056 duration = o.duration || 1;
12059 var b = this.getBox();
12060 var animFn = function(){
12061 var proxy = this.createProxy({
12064 visbility:"hidden",
12065 position:"absolute",
12066 "z-index":"35000", // yee haw
12067 border:"0px solid " + color
12070 var scale = Roo.isBorderBox ? 2 : 1;
12072 top:{from:b.y, to:b.y - 20},
12073 left:{from:b.x, to:b.x - 20},
12074 borderWidth:{from:0, to:10},
12075 opacity:{from:1, to:0},
12076 height:{from:b.height, to:(b.height + (20*scale))},
12077 width:{from:b.width, to:(b.width + (20*scale))}
12078 }, duration, function(){
12082 animFn.defer((duration/2)*1000, this);
12093 * Creates a pause before any subsequent queued effects begin. If there are
12094 * no effects queued after the pause it will have no effect.
12099 * @param {Number} seconds The length of time to pause (in seconds)
12100 * @return {Roo.Element} The Element
12102 pause : function(seconds){
12103 var el = this.getFxEl();
12106 el.queueFx(o, function(){
12107 setTimeout(function(){
12109 }, seconds * 1000);
12115 * Fade an element in (from transparent to opaque). The ending opacity can be specified
12116 * using the "endOpacity" config option.
12119 // default: fade in from opacity 0 to 100%
12122 // custom: fade in from opacity 0 to 75% over 2 seconds
12123 el.fadeIn({ endOpacity: .75, duration: 2});
12125 // common config options shown with default values
12127 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12132 * @param {Object} options (optional) Object literal with any of the Fx config options
12133 * @return {Roo.Element} The Element
12135 fadeIn : function(o){
12136 var el = this.getFxEl();
12138 el.queueFx(o, function(){
12139 this.setOpacity(0);
12141 this.dom.style.visibility = 'visible';
12142 var to = o.endOpacity || 1;
12143 arguments.callee.anim = this.fxanim({opacity:{to:to}},
12144 o, null, .5, "easeOut", function(){
12146 this.clearOpacity();
12155 * Fade an element out (from opaque to transparent). The ending opacity can be specified
12156 * using the "endOpacity" config option.
12159 // default: fade out from the element's current opacity to 0
12162 // custom: fade out from the element's current opacity to 25% over 2 seconds
12163 el.fadeOut({ endOpacity: .25, duration: 2});
12165 // common config options shown with default values
12167 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12174 * @param {Object} options (optional) Object literal with any of the Fx config options
12175 * @return {Roo.Element} The Element
12177 fadeOut : function(o){
12178 var el = this.getFxEl();
12180 el.queueFx(o, function(){
12181 arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12182 o, null, .5, "easeOut", function(){
12183 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12184 this.dom.style.display = "none";
12186 this.dom.style.visibility = "hidden";
12188 this.clearOpacity();
12196 * Animates the transition of an element's dimensions from a starting height/width
12197 * to an ending height/width.
12200 // change height and width to 100x100 pixels
12201 el.scale(100, 100);
12203 // common config options shown with default values. The height and width will default to
12204 // the element's existing values if passed as null.
12207 [element's height], {
12212 * @param {Number} width The new width (pass undefined to keep the original width)
12213 * @param {Number} height The new height (pass undefined to keep the original height)
12214 * @param {Object} options (optional) Object literal with any of the Fx config options
12215 * @return {Roo.Element} The Element
12217 scale : function(w, h, o){
12218 this.shift(Roo.apply({}, o, {
12226 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12227 * Any of these properties not specified in the config object will not be changed. This effect
12228 * requires that at least one new dimension, position or opacity setting must be passed in on
12229 * the config object in order for the function to have any effect.
12232 // slide the element horizontally to x position 200 while changing the height and opacity
12233 el.shift({ x: 200, height: 50, opacity: .8 });
12235 // common config options shown with default values.
12237 width: [element's width],
12238 height: [element's height],
12239 x: [element's x position],
12240 y: [element's y position],
12241 opacity: [element's opacity],
12246 * @param {Object} options Object literal with any of the Fx config options
12247 * @return {Roo.Element} The Element
12249 shift : function(o){
12250 var el = this.getFxEl();
12252 el.queueFx(o, function(){
12253 var a = {}, w = o.width, h = o.height, x = o.x, y = o.y, op = o.opacity;
12254 if(w !== undefined){
12255 a.width = {to: this.adjustWidth(w)};
12257 if(h !== undefined){
12258 a.height = {to: this.adjustHeight(h)};
12260 if(x !== undefined || y !== undefined){
12262 x !== undefined ? x : this.getX(),
12263 y !== undefined ? y : this.getY()
12266 if(op !== undefined){
12267 a.opacity = {to: op};
12269 if(o.xy !== undefined){
12270 a.points = {to: o.xy};
12272 arguments.callee.anim = this.fxanim(a,
12273 o, 'motion', .35, "easeOut", function(){
12281 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
12282 * ending point of the effect.
12285 // default: slide the element downward while fading out
12288 // custom: slide the element out to the right with a 2-second duration
12289 el.ghost('r', { duration: 2 });
12291 // common config options shown with default values
12299 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12300 * @param {Object} options (optional) Object literal with any of the Fx config options
12301 * @return {Roo.Element} The Element
12303 ghost : function(anchor, o){
12304 var el = this.getFxEl();
12307 el.queueFx(o, function(){
12308 anchor = anchor || "b";
12310 // restore values after effect
12311 var r = this.getFxRestore();
12312 var w = this.getWidth(),
12313 h = this.getHeight();
12315 var st = this.dom.style;
12317 var after = function(){
12319 el.setDisplayed(false);
12325 el.setPositioning(r.pos);
12326 st.width = r.width;
12327 st.height = r.height;
12332 var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12333 switch(anchor.toLowerCase()){
12360 arguments.callee.anim = this.fxanim(a,
12370 * Ensures that all effects queued after syncFx is called on the element are
12371 * run concurrently. This is the opposite of {@link #sequenceFx}.
12372 * @return {Roo.Element} The Element
12374 syncFx : function(){
12375 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12384 * Ensures that all effects queued after sequenceFx is called on the element are
12385 * run in sequence. This is the opposite of {@link #syncFx}.
12386 * @return {Roo.Element} The Element
12388 sequenceFx : function(){
12389 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12391 concurrent : false,
12398 nextFx : function(){
12399 var ef = this.fxQueue[0];
12406 * Returns true if the element has any effects actively running or queued, else returns false.
12407 * @return {Boolean} True if element has active effects, else false
12409 hasActiveFx : function(){
12410 return this.fxQueue && this.fxQueue[0];
12414 * Stops any running effects and clears the element's internal effects queue if it contains
12415 * any additional effects that haven't started yet.
12416 * @return {Roo.Element} The Element
12418 stopFx : function(){
12419 if(this.hasActiveFx()){
12420 var cur = this.fxQueue[0];
12421 if(cur && cur.anim && cur.anim.isAnimated()){
12422 this.fxQueue = [cur]; // clear out others
12423 cur.anim.stop(true);
12430 beforeFx : function(o){
12431 if(this.hasActiveFx() && !o.concurrent){
12442 * Returns true if the element is currently blocking so that no other effect can be queued
12443 * until this effect is finished, else returns false if blocking is not set. This is commonly
12444 * used to ensure that an effect initiated by a user action runs to completion prior to the
12445 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12446 * @return {Boolean} True if blocking, else false
12448 hasFxBlock : function(){
12449 var q = this.fxQueue;
12450 return q && q[0] && q[0].block;
12454 queueFx : function(o, fn){
12458 if(!this.hasFxBlock()){
12459 Roo.applyIf(o, this.fxDefaults);
12461 var run = this.beforeFx(o);
12462 fn.block = o.block;
12463 this.fxQueue.push(fn);
12475 fxWrap : function(pos, o, vis){
12477 if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12480 wrapXY = this.getXY();
12482 var div = document.createElement("div");
12483 div.style.visibility = vis;
12484 wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12485 wrap.setPositioning(pos);
12486 if(wrap.getStyle("position") == "static"){
12487 wrap.position("relative");
12489 this.clearPositioning('auto');
12491 wrap.dom.appendChild(this.dom);
12493 wrap.setXY(wrapXY);
12500 fxUnwrap : function(wrap, pos, o){
12501 this.clearPositioning();
12502 this.setPositioning(pos);
12504 wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12510 getFxRestore : function(){
12511 var st = this.dom.style;
12512 return {pos: this.getPositioning(), width: st.width, height : st.height};
12516 afterFx : function(o){
12518 this.applyStyles(o.afterStyle);
12521 this.addClass(o.afterCls);
12523 if(o.remove === true){
12526 Roo.callback(o.callback, o.scope, [this]);
12528 this.fxQueue.shift();
12534 getFxEl : function(){ // support for composite element fx
12535 return Roo.get(this.dom);
12539 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12540 animType = animType || 'run';
12542 var anim = Roo.lib.Anim[animType](
12544 (opt.duration || defaultDur) || .35,
12545 (opt.easing || defaultEase) || 'easeOut',
12547 Roo.callback(cb, this);
12556 // backwords compat
12557 Roo.Fx.resize = Roo.Fx.scale;
12559 //When included, Roo.Fx is automatically applied to Element so that all basic
12560 //effects are available directly via the Element API
12561 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12563 * Ext JS Library 1.1.1
12564 * Copyright(c) 2006-2007, Ext JS, LLC.
12566 * Originally Released Under LGPL - original licence link has changed is not relivant.
12569 * <script type="text/javascript">
12574 * @class Roo.CompositeElement
12575 * Standard composite class. Creates a Roo.Element for every element in the collection.
12577 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12578 * actions will be performed on all the elements in this collection.</b>
12580 * All methods return <i>this</i> and can be chained.
12582 var els = Roo.select("#some-el div.some-class", true);
12583 // or select directly from an existing element
12584 var el = Roo.get('some-el');
12585 el.select('div.some-class', true);
12587 els.setWidth(100); // all elements become 100 width
12588 els.hide(true); // all elements fade out and hide
12590 els.setWidth(100).hide(true);
12593 Roo.CompositeElement = function(els){
12594 this.elements = [];
12595 this.addElements(els);
12597 Roo.CompositeElement.prototype = {
12599 addElements : function(els){
12603 if(typeof els == "string"){
12604 els = Roo.Element.selectorFunction(els);
12606 var yels = this.elements;
12607 var index = yels.length-1;
12608 for(var i = 0, len = els.length; i < len; i++) {
12609 yels[++index] = Roo.get(els[i]);
12615 * Clears this composite and adds the elements returned by the passed selector.
12616 * @param {String/Array} els A string CSS selector, an array of elements or an element
12617 * @return {CompositeElement} this
12619 fill : function(els){
12620 this.elements = [];
12626 * Filters this composite to only elements that match the passed selector.
12627 * @param {String} selector A string CSS selector
12628 * @param {Boolean} inverse return inverse filter (not matches)
12629 * @return {CompositeElement} this
12631 filter : function(selector, inverse){
12633 inverse = inverse || false;
12634 this.each(function(el){
12635 var match = inverse ? !el.is(selector) : el.is(selector);
12637 els[els.length] = el.dom;
12644 invoke : function(fn, args){
12645 var els = this.elements;
12646 for(var i = 0, len = els.length; i < len; i++) {
12647 Roo.Element.prototype[fn].apply(els[i], args);
12652 * Adds elements to this composite.
12653 * @param {String/Array} els A string CSS selector, an array of elements or an element
12654 * @return {CompositeElement} this
12656 add : function(els){
12657 if(typeof els == "string"){
12658 this.addElements(Roo.Element.selectorFunction(els));
12659 }else if(els.length !== undefined){
12660 this.addElements(els);
12662 this.addElements([els]);
12667 * Calls the passed function passing (el, this, index) for each element in this composite.
12668 * @param {Function} fn The function to call
12669 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12670 * @return {CompositeElement} this
12672 each : function(fn, scope){
12673 var els = this.elements;
12674 for(var i = 0, len = els.length; i < len; i++){
12675 if(fn.call(scope || els[i], els[i], this, i) === false) {
12683 * Returns the Element object at the specified index
12684 * @param {Number} index
12685 * @return {Roo.Element}
12687 item : function(index){
12688 return this.elements[index] || null;
12692 * Returns the first Element
12693 * @return {Roo.Element}
12695 first : function(){
12696 return this.item(0);
12700 * Returns the last Element
12701 * @return {Roo.Element}
12704 return this.item(this.elements.length-1);
12708 * Returns the number of elements in this composite
12711 getCount : function(){
12712 return this.elements.length;
12716 * Returns true if this composite contains the passed element
12719 contains : function(el){
12720 return this.indexOf(el) !== -1;
12724 * Returns true if this composite contains the passed element
12727 indexOf : function(el){
12728 return this.elements.indexOf(Roo.get(el));
12733 * Removes the specified element(s).
12734 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12735 * or an array of any of those.
12736 * @param {Boolean} removeDom (optional) True to also remove the element from the document
12737 * @return {CompositeElement} this
12739 removeElement : function(el, removeDom){
12740 if(el instanceof Array){
12741 for(var i = 0, len = el.length; i < len; i++){
12742 this.removeElement(el[i]);
12746 var index = typeof el == 'number' ? el : this.indexOf(el);
12749 var d = this.elements[index];
12753 d.parentNode.removeChild(d);
12756 this.elements.splice(index, 1);
12762 * Replaces the specified element with the passed element.
12763 * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12765 * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12766 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12767 * @return {CompositeElement} this
12769 replaceElement : function(el, replacement, domReplace){
12770 var index = typeof el == 'number' ? el : this.indexOf(el);
12773 this.elements[index].replaceWith(replacement);
12775 this.elements.splice(index, 1, Roo.get(replacement))
12782 * Removes all elements.
12784 clear : function(){
12785 this.elements = [];
12789 Roo.CompositeElement.createCall = function(proto, fnName){
12790 if(!proto[fnName]){
12791 proto[fnName] = function(){
12792 return this.invoke(fnName, arguments);
12796 for(var fnName in Roo.Element.prototype){
12797 if(typeof Roo.Element.prototype[fnName] == "function"){
12798 Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12804 * Ext JS Library 1.1.1
12805 * Copyright(c) 2006-2007, Ext JS, LLC.
12807 * Originally Released Under LGPL - original licence link has changed is not relivant.
12810 * <script type="text/javascript">
12814 * @class Roo.CompositeElementLite
12815 * @extends Roo.CompositeElement
12816 * Flyweight composite class. Reuses the same Roo.Element for element operations.
12818 var els = Roo.select("#some-el div.some-class");
12819 // or select directly from an existing element
12820 var el = Roo.get('some-el');
12821 el.select('div.some-class');
12823 els.setWidth(100); // all elements become 100 width
12824 els.hide(true); // all elements fade out and hide
12826 els.setWidth(100).hide(true);
12827 </code></pre><br><br>
12828 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12829 * actions will be performed on all the elements in this collection.</b>
12831 Roo.CompositeElementLite = function(els){
12832 Roo.CompositeElementLite.superclass.constructor.call(this, els);
12833 this.el = new Roo.Element.Flyweight();
12835 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12836 addElements : function(els){
12838 if(els instanceof Array){
12839 this.elements = this.elements.concat(els);
12841 var yels = this.elements;
12842 var index = yels.length-1;
12843 for(var i = 0, len = els.length; i < len; i++) {
12844 yels[++index] = els[i];
12850 invoke : function(fn, args){
12851 var els = this.elements;
12853 for(var i = 0, len = els.length; i < len; i++) {
12855 Roo.Element.prototype[fn].apply(el, args);
12860 * Returns a flyweight Element of the dom element object at the specified index
12861 * @param {Number} index
12862 * @return {Roo.Element}
12864 item : function(index){
12865 if(!this.elements[index]){
12868 this.el.dom = this.elements[index];
12872 // fixes scope with flyweight
12873 addListener : function(eventName, handler, scope, opt){
12874 var els = this.elements;
12875 for(var i = 0, len = els.length; i < len; i++) {
12876 Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12882 * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12883 * passed is the flyweight (shared) Roo.Element instance, so if you require a
12884 * a reference to the dom node, use el.dom.</b>
12885 * @param {Function} fn The function to call
12886 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12887 * @return {CompositeElement} this
12889 each : function(fn, scope){
12890 var els = this.elements;
12892 for(var i = 0, len = els.length; i < len; i++){
12894 if(fn.call(scope || el, el, this, i) === false){
12901 indexOf : function(el){
12902 return this.elements.indexOf(Roo.getDom(el));
12905 replaceElement : function(el, replacement, domReplace){
12906 var index = typeof el == 'number' ? el : this.indexOf(el);
12908 replacement = Roo.getDom(replacement);
12910 var d = this.elements[index];
12911 d.parentNode.insertBefore(replacement, d);
12912 d.parentNode.removeChild(d);
12914 this.elements.splice(index, 1, replacement);
12919 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12923 * Ext JS Library 1.1.1
12924 * Copyright(c) 2006-2007, Ext JS, LLC.
12926 * Originally Released Under LGPL - original licence link has changed is not relivant.
12929 * <script type="text/javascript">
12935 * @class Roo.data.Connection
12936 * @extends Roo.util.Observable
12937 * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12938 * either to a configured URL, or to a URL specified at request time.
12940 * Requests made by this class are asynchronous, and will return immediately. No data from
12941 * the server will be available to the statement immediately following the {@link #request} call.
12942 * To process returned data, use a callback in the request options object, or an event listener.
12944 * Note: If you are doing a file upload, you will not get a normal response object sent back to
12945 * your callback or event handler. Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12946 * The response object is created using the innerHTML of the IFRAME's document as the responseText
12947 * property and, if present, the IFRAME's XML document as the responseXML property.
12949 * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12950 * that it be placed either inside a <textarea> in an HTML document and retrieved from the responseText
12951 * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12952 * standard DOM methods.
12954 * @param {Object} config a configuration object.
12956 Roo.data.Connection = function(config){
12957 Roo.apply(this, config);
12960 * @event beforerequest
12961 * Fires before a network request is made to retrieve a data object.
12962 * @param {Connection} conn This Connection object.
12963 * @param {Object} options The options config object passed to the {@link #request} method.
12965 "beforerequest" : true,
12967 * @event requestcomplete
12968 * Fires if the request was successfully completed.
12969 * @param {Connection} conn This Connection object.
12970 * @param {Object} response The XHR object containing the response data.
12971 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12972 * @param {Object} options The options config object passed to the {@link #request} method.
12974 "requestcomplete" : true,
12976 * @event requestexception
12977 * Fires if an error HTTP status was returned from the server.
12978 * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12979 * @param {Connection} conn This Connection object.
12980 * @param {Object} response The XHR object containing the response data.
12981 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12982 * @param {Object} options The options config object passed to the {@link #request} method.
12984 "requestexception" : true
12986 Roo.data.Connection.superclass.constructor.call(this);
12989 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12991 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12994 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12995 * extra parameters to each request made by this object. (defaults to undefined)
12998 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12999 * to each request made by this object. (defaults to undefined)
13002 * @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)
13005 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13009 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13015 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13018 disableCaching: true,
13021 * Sends an HTTP request to a remote server.
13022 * @param {Object} options An object which may contain the following properties:<ul>
13023 * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13024 * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13025 * request, a url encoded string or a function to call to get either.</li>
13026 * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13027 * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13028 * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13029 * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13030 * <li>options {Object} The parameter to the request call.</li>
13031 * <li>success {Boolean} True if the request succeeded.</li>
13032 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13034 * <li><b>success</b> {Function} (Optional) The function to be called upon success 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>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13040 * The callback is passed the following parameters:<ul>
13041 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13042 * <li>options {Object} The parameter to the request call.</li>
13044 * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13045 * for the callback function. Defaults to the browser window.</li>
13046 * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13047 * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13048 * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13049 * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13050 * params for the post data. Any params will be appended to the URL.</li>
13051 * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13053 * @return {Number} transactionId
13055 request : function(o){
13056 if(this.fireEvent("beforerequest", this, o) !== false){
13059 if(typeof p == "function"){
13060 p = p.call(o.scope||window, o);
13062 if(typeof p == "object"){
13063 p = Roo.urlEncode(o.params);
13065 if(this.extraParams){
13066 var extras = Roo.urlEncode(this.extraParams);
13067 p = p ? (p + '&' + extras) : extras;
13070 var url = o.url || this.url;
13071 if(typeof url == 'function'){
13072 url = url.call(o.scope||window, o);
13076 var form = Roo.getDom(o.form);
13077 url = url || form.action;
13079 var enctype = form.getAttribute("enctype");
13082 return this.doFormDataUpload(o, url);
13085 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13086 return this.doFormUpload(o, p, url);
13088 var f = Roo.lib.Ajax.serializeForm(form);
13089 p = p ? (p + '&' + f) : f;
13092 if (!o.form && o.formData) {
13093 o.formData = o.formData === true ? new FormData() : o.formData;
13094 for (var k in o.params) {
13095 o.formData.append(k,o.params[k]);
13098 return this.doFormDataUpload(o, url);
13102 var hs = o.headers;
13103 if(this.defaultHeaders){
13104 hs = Roo.apply(hs || {}, this.defaultHeaders);
13111 success: this.handleResponse,
13112 failure: this.handleFailure,
13114 argument: {options: o},
13115 timeout : o.timeout || this.timeout
13118 var method = o.method||this.method||(p ? "POST" : "GET");
13120 if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13121 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13124 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13128 }else if(this.autoAbort !== false){
13132 if((method == 'GET' && p) || o.xmlData){
13133 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13136 Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13137 this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13138 Roo.lib.Ajax.useDefaultHeader == true;
13139 return this.transId;
13141 Roo.callback(o.callback, o.scope, [o, null, null]);
13147 * Determine whether this object has a request outstanding.
13148 * @param {Number} transactionId (Optional) defaults to the last transaction
13149 * @return {Boolean} True if there is an outstanding request.
13151 isLoading : function(transId){
13153 return Roo.lib.Ajax.isCallInProgress(transId);
13155 return this.transId ? true : false;
13160 * Aborts any outstanding request.
13161 * @param {Number} transactionId (Optional) defaults to the last transaction
13163 abort : function(transId){
13164 if(transId || this.isLoading()){
13165 Roo.lib.Ajax.abort(transId || this.transId);
13170 handleResponse : function(response){
13171 this.transId = false;
13172 var options = response.argument.options;
13173 response.argument = options ? options.argument : null;
13174 this.fireEvent("requestcomplete", this, response, options);
13175 Roo.callback(options.success, options.scope, [response, options]);
13176 Roo.callback(options.callback, options.scope, [options, true, response]);
13180 handleFailure : function(response, e){
13181 this.transId = false;
13182 var options = response.argument.options;
13183 response.argument = options ? options.argument : null;
13184 this.fireEvent("requestexception", this, response, options, e);
13185 Roo.callback(options.failure, options.scope, [response, options]);
13186 Roo.callback(options.callback, options.scope, [options, false, response]);
13190 doFormUpload : function(o, ps, url){
13192 var frame = document.createElement('iframe');
13195 frame.className = 'x-hidden';
13197 frame.src = Roo.SSL_SECURE_URL;
13199 document.body.appendChild(frame);
13202 document.frames[id].name = id;
13205 var form = Roo.getDom(o.form);
13207 form.method = 'POST';
13208 form.enctype = form.encoding = 'multipart/form-data';
13214 if(ps){ // add dynamic params
13216 ps = Roo.urlDecode(ps, false);
13218 if(ps.hasOwnProperty(k)){
13219 hd = document.createElement('input');
13220 hd.type = 'hidden';
13223 form.appendChild(hd);
13230 var r = { // bogus response object
13235 r.argument = o ? o.argument : null;
13240 doc = frame.contentWindow.document;
13242 doc = (frame.contentDocument || window.frames[id].document);
13244 if(doc && doc.body){
13245 r.responseText = doc.body.innerHTML;
13247 if(doc && doc.XMLDocument){
13248 r.responseXML = doc.XMLDocument;
13250 r.responseXML = doc;
13257 Roo.EventManager.removeListener(frame, 'load', cb, this);
13259 this.fireEvent("requestcomplete", this, r, o);
13260 Roo.callback(o.success, o.scope, [r, o]);
13261 Roo.callback(o.callback, o.scope, [o, true, r]);
13263 setTimeout(function(){document.body.removeChild(frame);}, 100);
13266 Roo.EventManager.on(frame, 'load', cb, this);
13269 if(hiddens){ // remove dynamic params
13270 for(var i = 0, len = hiddens.length; i < len; i++){
13271 form.removeChild(hiddens[i]);
13275 // this is a 'formdata version???'
13278 doFormDataUpload : function(o, url)
13282 var form = Roo.getDom(o.form);
13283 form.enctype = form.encoding = 'multipart/form-data';
13284 formData = o.formData === true ? new FormData(form) : o.formData;
13286 formData = o.formData === true ? new FormData() : o.formData;
13291 success: this.handleResponse,
13292 failure: this.handleFailure,
13294 argument: {options: o},
13295 timeout : o.timeout || this.timeout
13298 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13302 }else if(this.autoAbort !== false){
13306 //Roo.lib.Ajax.defaultPostHeader = null;
13307 Roo.lib.Ajax.useDefaultHeader = false;
13308 this.transId = Roo.lib.Ajax.request( "POST", url, cb, formData, o);
13309 Roo.lib.Ajax.useDefaultHeader = true;
13317 * Ext JS Library 1.1.1
13318 * Copyright(c) 2006-2007, Ext JS, LLC.
13320 * Originally Released Under LGPL - original licence link has changed is not relivant.
13323 * <script type="text/javascript">
13327 * Global Ajax request class.
13330 * @extends Roo.data.Connection
13333 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
13334 * @cfg {Object} extraParams An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13335 * @cfg {Object} defaultHeaders An object containing request headers which are added to each request made by this object. (defaults to undefined)
13336 * @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)
13337 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13338 * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13339 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13341 Roo.Ajax = new Roo.data.Connection({
13350 * Serialize the passed form into a url encoded string
13352 * @param {String/HTMLElement} form
13355 serializeForm : function(form){
13356 return Roo.lib.Ajax.serializeForm(form);
13360 * Ext JS Library 1.1.1
13361 * Copyright(c) 2006-2007, Ext JS, LLC.
13363 * Originally Released Under LGPL - original licence link has changed is not relivant.
13366 * <script type="text/javascript">
13371 * @class Roo.UpdateManager
13372 * @extends Roo.util.Observable
13373 * Provides AJAX-style update for Element object.<br><br>
13376 * // Get it from a Roo.Element object
13377 * var el = Roo.get("foo");
13378 * var mgr = el.getUpdateManager();
13379 * mgr.update("http://myserver.com/index.php", "param1=1&param2=2");
13381 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13383 * // or directly (returns the same UpdateManager instance)
13384 * var mgr = new Roo.UpdateManager("myElementId");
13385 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13386 * mgr.on("update", myFcnNeedsToKnow);
13388 // short handed call directly from the element object
13389 Roo.get("foo").load({
13393 text: "Loading Foo..."
13397 * Create new UpdateManager directly.
13398 * @param {String/HTMLElement/Roo.Element} el The element to update
13399 * @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).
13401 Roo.UpdateManager = function(el, forceNew){
13403 if(!forceNew && el.updateManager){
13404 return el.updateManager;
13407 * The Element object
13408 * @type Roo.Element
13412 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13415 this.defaultUrl = null;
13419 * @event beforeupdate
13420 * Fired before an update is made, return false from your handler and the update is cancelled.
13421 * @param {Roo.Element} el
13422 * @param {String/Object/Function} url
13423 * @param {String/Object} params
13425 "beforeupdate": true,
13428 * Fired after successful update is made.
13429 * @param {Roo.Element} el
13430 * @param {Object} oResponseObject The response Object
13435 * Fired on update failure.
13436 * @param {Roo.Element} el
13437 * @param {Object} oResponseObject The response Object
13441 var d = Roo.UpdateManager.defaults;
13443 * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13446 this.sslBlankUrl = d.sslBlankUrl;
13448 * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13451 this.disableCaching = d.disableCaching;
13453 * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '<div class="loading-indicator">Loading...</div>').
13456 this.indicatorText = d.indicatorText;
13458 * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13461 this.showLoadIndicator = d.showLoadIndicator;
13463 * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13466 this.timeout = d.timeout;
13469 * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13472 this.loadScripts = d.loadScripts;
13475 * Transaction object of current executing transaction
13477 this.transaction = null;
13482 this.autoRefreshProcId = null;
13484 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13487 this.refreshDelegate = this.refresh.createDelegate(this);
13489 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13492 this.updateDelegate = this.update.createDelegate(this);
13494 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13497 this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13501 this.successDelegate = this.processSuccess.createDelegate(this);
13505 this.failureDelegate = this.processFailure.createDelegate(this);
13507 if(!this.renderer){
13509 * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13511 this.renderer = new Roo.UpdateManager.BasicRenderer();
13514 Roo.UpdateManager.superclass.constructor.call(this);
13517 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13519 * Get the Element this UpdateManager is bound to
13520 * @return {Roo.Element} The element
13522 getEl : function(){
13526 * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13527 * @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:
13530 url: "your-url.php",<br/>
13531 params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13532 callback: yourFunction,<br/>
13533 scope: yourObject, //(optional scope) <br/>
13534 discardUrl: false, <br/>
13535 nocache: false,<br/>
13536 text: "Loading...",<br/>
13538 scripts: false<br/>
13541 * The only required property is url. The optional properties nocache, text and scripts
13542 * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13543 * @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}
13544 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13545 * @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.
13547 update : function(url, params, callback, discardUrl){
13548 if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13549 var method = this.method,
13551 if(typeof url == "object"){ // must be config object
13554 params = params || cfg.params;
13555 callback = callback || cfg.callback;
13556 discardUrl = discardUrl || cfg.discardUrl;
13557 if(callback && cfg.scope){
13558 callback = callback.createDelegate(cfg.scope);
13560 if(typeof cfg.method != "undefined"){method = cfg.method;};
13561 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13562 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13563 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13564 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13566 this.showLoading();
13568 this.defaultUrl = url;
13570 if(typeof url == "function"){
13571 url = url.call(this);
13574 method = method || (params ? "POST" : "GET");
13575 if(method == "GET"){
13576 url = this.prepareUrl(url);
13579 var o = Roo.apply(cfg ||{}, {
13582 success: this.successDelegate,
13583 failure: this.failureDelegate,
13584 callback: undefined,
13585 timeout: (this.timeout*1000),
13586 argument: {"url": url, "form": null, "callback": callback, "params": params}
13588 Roo.log("updated manager called with timeout of " + o.timeout);
13589 this.transaction = Roo.Ajax.request(o);
13594 * 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.
13595 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13596 * @param {String/HTMLElement} form The form Id or form element
13597 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13598 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13599 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13601 formUpdate : function(form, url, reset, callback){
13602 if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13603 if(typeof url == "function"){
13604 url = url.call(this);
13606 form = Roo.getDom(form);
13607 this.transaction = Roo.Ajax.request({
13610 success: this.successDelegate,
13611 failure: this.failureDelegate,
13612 timeout: (this.timeout*1000),
13613 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13615 this.showLoading.defer(1, this);
13620 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13621 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13623 refresh : function(callback){
13624 if(this.defaultUrl == null){
13627 this.update(this.defaultUrl, null, callback, true);
13631 * Set this element to auto refresh.
13632 * @param {Number} interval How often to update (in seconds).
13633 * @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)
13634 * @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}
13635 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13636 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13638 startAutoRefresh : function(interval, url, params, callback, refreshNow){
13640 this.update(url || this.defaultUrl, params, callback, true);
13642 if(this.autoRefreshProcId){
13643 clearInterval(this.autoRefreshProcId);
13645 this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13649 * Stop auto refresh on this element.
13651 stopAutoRefresh : function(){
13652 if(this.autoRefreshProcId){
13653 clearInterval(this.autoRefreshProcId);
13654 delete this.autoRefreshProcId;
13658 isAutoRefreshing : function(){
13659 return this.autoRefreshProcId ? true : false;
13662 * Called to update the element to "Loading" state. Override to perform custom action.
13664 showLoading : function(){
13665 if(this.showLoadIndicator){
13666 this.el.update(this.indicatorText);
13671 * Adds unique parameter to query string if disableCaching = true
13674 prepareUrl : function(url){
13675 if(this.disableCaching){
13676 var append = "_dc=" + (new Date().getTime());
13677 if(url.indexOf("?") !== -1){
13678 url += "&" + append;
13680 url += "?" + append;
13689 processSuccess : function(response){
13690 this.transaction = null;
13691 if(response.argument.form && response.argument.reset){
13692 try{ // put in try/catch since some older FF releases had problems with this
13693 response.argument.form.reset();
13696 if(this.loadScripts){
13697 this.renderer.render(this.el, response, this,
13698 this.updateComplete.createDelegate(this, [response]));
13700 this.renderer.render(this.el, response, this);
13701 this.updateComplete(response);
13705 updateComplete : function(response){
13706 this.fireEvent("update", this.el, response);
13707 if(typeof response.argument.callback == "function"){
13708 response.argument.callback(this.el, true, response);
13715 processFailure : function(response){
13716 this.transaction = null;
13717 this.fireEvent("failure", this.el, response);
13718 if(typeof response.argument.callback == "function"){
13719 response.argument.callback(this.el, false, response);
13724 * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13725 * @param {Object} renderer The object implementing the render() method
13727 setRenderer : function(renderer){
13728 this.renderer = renderer;
13731 getRenderer : function(){
13732 return this.renderer;
13736 * Set the defaultUrl used for updates
13737 * @param {String/Function} defaultUrl The url or a function to call to get the url
13739 setDefaultUrl : function(defaultUrl){
13740 this.defaultUrl = defaultUrl;
13744 * Aborts the executing transaction
13746 abort : function(){
13747 if(this.transaction){
13748 Roo.Ajax.abort(this.transaction);
13753 * Returns true if an update is in progress
13754 * @return {Boolean}
13756 isUpdating : function(){
13757 if(this.transaction){
13758 return Roo.Ajax.isLoading(this.transaction);
13765 * @class Roo.UpdateManager.defaults
13766 * @static (not really - but it helps the doc tool)
13767 * The defaults collection enables customizing the default properties of UpdateManager
13769 Roo.UpdateManager.defaults = {
13771 * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13777 * True to process scripts by default (Defaults to false).
13780 loadScripts : false,
13783 * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13786 sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13788 * Whether to append unique parameter on get request to disable caching (Defaults to false).
13791 disableCaching : false,
13793 * Whether to show indicatorText when loading (Defaults to true).
13796 showLoadIndicator : true,
13798 * Text for loading indicator (Defaults to '<div class="loading-indicator">Loading...</div>').
13801 indicatorText : '<div class="loading-indicator">Loading...</div>'
13805 * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13807 * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13808 * @param {String/HTMLElement/Roo.Element} el The element to update
13809 * @param {String} url The url
13810 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13811 * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13814 * @member Roo.UpdateManager
13816 Roo.UpdateManager.updateElement = function(el, url, params, options){
13817 var um = Roo.get(el, true).getUpdateManager();
13818 Roo.apply(um, options);
13819 um.update(url, params, options ? options.callback : null);
13821 // alias for backwards compat
13822 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13824 * @class Roo.UpdateManager.BasicRenderer
13825 * Default Content renderer. Updates the elements innerHTML with the responseText.
13827 Roo.UpdateManager.BasicRenderer = function(){};
13829 Roo.UpdateManager.BasicRenderer.prototype = {
13831 * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13832 * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13833 * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13834 * @param {Roo.Element} el The element being rendered
13835 * @param {Object} response The YUI Connect response object
13836 * @param {UpdateManager} updateManager The calling update manager
13837 * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13839 render : function(el, response, updateManager, callback){
13840 el.update(response.responseText, updateManager.loadScripts, callback);
13846 * (c)) Alan Knowles
13852 * @class Roo.DomTemplate
13853 * @extends Roo.Template
13854 * An effort at a dom based template engine..
13856 * Similar to XTemplate, except it uses dom parsing to create the template..
13858 * Supported features:
13863 {a_variable} - output encoded.
13864 {a_variable.format:("Y-m-d")} - call a method on the variable
13865 {a_variable:raw} - unencoded output
13866 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13867 {a_variable:this.method_on_template(...)} - call a method on the template object.
13872 <div roo-for="a_variable or condition.."></div>
13873 <div roo-if="a_variable or condition"></div>
13874 <div roo-exec="some javascript"></div>
13875 <div roo-name="named_template"></div>
13880 Roo.DomTemplate = function()
13882 Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13889 Roo.extend(Roo.DomTemplate, Roo.Template, {
13891 * id counter for sub templates.
13895 * flag to indicate if dom parser is inside a pre,
13896 * it will strip whitespace if not.
13901 * The various sub templates
13909 * basic tag replacing syntax
13912 * // you can fake an object call by doing this
13916 re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13917 //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13919 iterChild : function (node, method) {
13921 var oldPre = this.inPre;
13922 if (node.tagName == 'PRE') {
13925 for( var i = 0; i < node.childNodes.length; i++) {
13926 method.call(this, node.childNodes[i]);
13928 this.inPre = oldPre;
13934 * compile the template
13936 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13939 compile: function()
13943 // covert the html into DOM...
13947 doc = document.implementation.createHTMLDocument("");
13948 doc.documentElement.innerHTML = this.html ;
13949 div = doc.documentElement;
13951 // old IE... - nasty -- it causes all sorts of issues.. with
13952 // images getting pulled from server..
13953 div = document.createElement('div');
13954 div.innerHTML = this.html;
13956 //doc.documentElement.innerHTML = htmlBody
13962 this.iterChild(div, function(n) {_t.compileNode(n, true); });
13964 var tpls = this.tpls;
13966 // create a top level template from the snippet..
13968 //Roo.log(div.innerHTML);
13975 body : div.innerHTML,
13988 Roo.each(tpls, function(tp){
13989 this.compileTpl(tp);
13990 this.tpls[tp.id] = tp;
13993 this.master = tpls[0];
13999 compileNode : function(node, istop) {
14004 // skip anything not a tag..
14005 if (node.nodeType != 1) {
14006 if (node.nodeType == 3 && !this.inPre) {
14007 // reduce white space..
14008 node.nodeValue = node.nodeValue.replace(/\s+/g, ' ');
14031 case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14032 case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14033 case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14034 case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14040 // just itterate children..
14041 this.iterChild(node,this.compileNode);
14044 tpl.uid = this.id++;
14045 tpl.value = node.getAttribute('roo-' + tpl.attr);
14046 node.removeAttribute('roo-'+ tpl.attr);
14047 if (tpl.attr != 'name') {
14048 var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14049 node.parentNode.replaceChild(placeholder, node);
14052 var placeholder = document.createElement('span');
14053 placeholder.className = 'roo-tpl-' + tpl.value;
14054 node.parentNode.replaceChild(placeholder, node);
14057 // parent now sees '{domtplXXXX}
14058 this.iterChild(node,this.compileNode);
14060 // we should now have node body...
14061 var div = document.createElement('div');
14062 div.appendChild(node);
14064 // this has the unfortunate side effect of converting tagged attributes
14065 // eg. href="{...}" into %7C...%7D
14066 // this has been fixed by searching for those combo's although it's a bit hacky..
14069 tpl.body = div.innerHTML;
14076 switch (tpl.value) {
14077 case '.': tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14078 case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14079 default: tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14084 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14088 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14092 tpl.id = tpl.value; // replace non characters???
14098 this.tpls.push(tpl);
14108 * Compile a segment of the template into a 'sub-template'
14114 compileTpl : function(tpl)
14116 var fm = Roo.util.Format;
14117 var useF = this.disableFormats !== true;
14119 var sep = Roo.isGecko ? "+\n" : ",\n";
14121 var undef = function(str) {
14122 Roo.debug && Roo.log("Property not found :" + str);
14126 //Roo.log(tpl.body);
14130 var fn = function(m, lbrace, name, format, args)
14133 //Roo.log(arguments);
14134 args = args ? args.replace(/\\'/g,"'") : args;
14135 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14136 if (typeof(format) == 'undefined') {
14137 format = 'htmlEncode';
14139 if (format == 'raw' ) {
14143 if(name.substr(0, 6) == 'domtpl'){
14144 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14147 // build an array of options to determine if value is undefined..
14149 // basically get 'xxxx.yyyy' then do
14150 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14151 // (function () { Roo.log("Property not found"); return ''; })() :
14156 Roo.each(name.split('.'), function(st) {
14157 lookfor += (lookfor.length ? '.': '') + st;
14158 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
14161 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14164 if(format && useF){
14166 args = args ? ',' + args : "";
14168 if(format.substr(0, 5) != "this."){
14169 format = "fm." + format + '(';
14171 format = 'this.call("'+ format.substr(5) + '", ';
14175 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
14178 if (args && args.length) {
14179 // called with xxyx.yuu:(test,test)
14181 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
14183 // raw.. - :raw modifier..
14184 return "'"+ sep + udef_st + name + ")"+sep+"'";
14188 // branched to use + in gecko and [].join() in others
14190 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
14191 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14194 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
14195 body.push(tpl.body.replace(/(\r\n|\n)/g,
14196 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14197 body.push("'].join('');};};");
14198 body = body.join('');
14201 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14203 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
14210 * same as applyTemplate, except it's done to one of the subTemplates
14211 * when using named templates, you can do:
14213 * var str = pl.applySubTemplate('your-name', values);
14216 * @param {Number} id of the template
14217 * @param {Object} values to apply to template
14218 * @param {Object} parent (normaly the instance of this object)
14220 applySubTemplate : function(id, values, parent)
14224 var t = this.tpls[id];
14228 if(t.ifCall && !t.ifCall.call(this, values, parent)){
14229 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14233 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14240 if(t.execCall && t.execCall.call(this, values, parent)){
14244 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14250 var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14251 parent = t.target ? values : parent;
14252 if(t.forCall && vs instanceof Array){
14254 for(var i = 0, len = vs.length; i < len; i++){
14256 buf[buf.length] = t.compiled.call(this, vs[i], parent);
14258 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14260 //Roo.log(t.compiled);
14264 return buf.join('');
14267 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14272 return t.compiled.call(this, vs, parent);
14274 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14276 //Roo.log(t.compiled);
14284 applyTemplate : function(values){
14285 return this.master.compiled.call(this, values, {});
14286 //var s = this.subs;
14289 apply : function(){
14290 return this.applyTemplate.apply(this, arguments);
14295 Roo.DomTemplate.from = function(el){
14296 el = Roo.getDom(el);
14297 return new Roo.Domtemplate(el.value || el.innerHTML);
14300 * Ext JS Library 1.1.1
14301 * Copyright(c) 2006-2007, Ext JS, LLC.
14303 * Originally Released Under LGPL - original licence link has changed is not relivant.
14306 * <script type="text/javascript">
14310 * @class Roo.util.DelayedTask
14311 * Provides a convenient method of performing setTimeout where a new
14312 * timeout cancels the old timeout. An example would be performing validation on a keypress.
14313 * You can use this class to buffer
14314 * the keypress events for a certain number of milliseconds, and perform only if they stop
14315 * for that amount of time.
14316 * @constructor The parameters to this constructor serve as defaults and are not required.
14317 * @param {Function} fn (optional) The default function to timeout
14318 * @param {Object} scope (optional) The default scope of that timeout
14319 * @param {Array} args (optional) The default Array of arguments
14321 Roo.util.DelayedTask = function(fn, scope, args){
14322 var id = null, d, t;
14324 var call = function(){
14325 var now = new Date().getTime();
14329 fn.apply(scope, args || []);
14333 * Cancels any pending timeout and queues a new one
14334 * @param {Number} delay The milliseconds to delay
14335 * @param {Function} newFn (optional) Overrides function passed to constructor
14336 * @param {Object} newScope (optional) Overrides scope passed to constructor
14337 * @param {Array} newArgs (optional) Overrides args passed to constructor
14339 this.delay = function(delay, newFn, newScope, newArgs){
14340 if(id && delay != d){
14344 t = new Date().getTime();
14346 scope = newScope || scope;
14347 args = newArgs || args;
14349 id = setInterval(call, d);
14354 * Cancel the last queued timeout
14356 this.cancel = function(){
14364 * Ext JS Library 1.1.1
14365 * Copyright(c) 2006-2007, Ext JS, LLC.
14367 * Originally Released Under LGPL - original licence link has changed is not relivant.
14370 * <script type="text/javascript">
14373 * @class Roo.util.TaskRunner
14374 * Manage background tasks - not sure why this is better that setInterval?
14379 Roo.util.TaskRunner = function(interval){
14380 interval = interval || 10;
14381 var tasks = [], removeQueue = [];
14383 var running = false;
14385 var stopThread = function(){
14391 var startThread = function(){
14394 id = setInterval(runTasks, interval);
14398 var removeTask = function(task){
14399 removeQueue.push(task);
14405 var runTasks = function(){
14406 if(removeQueue.length > 0){
14407 for(var i = 0, len = removeQueue.length; i < len; i++){
14408 tasks.remove(removeQueue[i]);
14411 if(tasks.length < 1){
14416 var now = new Date().getTime();
14417 for(var i = 0, len = tasks.length; i < len; ++i){
14419 var itime = now - t.taskRunTime;
14420 if(t.interval <= itime){
14421 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14422 t.taskRunTime = now;
14423 if(rt === false || t.taskRunCount === t.repeat){
14428 if(t.duration && t.duration <= (now - t.taskStartTime)){
14435 * Queues a new task.
14436 * @param {Object} task
14438 * Task property : interval = how frequent to run.
14439 * Task object should implement
14441 * Task object may implement
14442 * function onStop()
14444 this.start = function(task){
14446 task.taskStartTime = new Date().getTime();
14447 task.taskRunTime = 0;
14448 task.taskRunCount = 0;
14454 * @param {Object} task
14456 this.stop = function(task){
14463 this.stopAll = function(){
14465 for(var i = 0, len = tasks.length; i < len; i++){
14466 if(tasks[i].onStop){
14475 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14477 * Ext JS Library 1.1.1
14478 * Copyright(c) 2006-2007, Ext JS, LLC.
14480 * Originally Released Under LGPL - original licence link has changed is not relivant.
14483 * <script type="text/javascript">
14488 * @class Roo.util.MixedCollection
14489 * @extends Roo.util.Observable
14490 * A Collection class that maintains both numeric indexes and keys and exposes events.
14492 * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14493 * collection (defaults to false)
14494 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14495 * and return the key value for that item. This is used when available to look up the key on items that
14496 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
14497 * equivalent to providing an implementation for the {@link #getKey} method.
14499 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14507 * Fires when the collection is cleared.
14512 * Fires when an item is added to the collection.
14513 * @param {Number} index The index at which the item was added.
14514 * @param {Object} o The item added.
14515 * @param {String} key The key associated with the added item.
14520 * Fires when an item is replaced in the collection.
14521 * @param {String} key he key associated with the new added.
14522 * @param {Object} old The item being replaced.
14523 * @param {Object} new The new item.
14528 * Fires when an item is removed from the collection.
14529 * @param {Object} o The item being removed.
14530 * @param {String} key (optional) The key associated with the removed item.
14535 this.allowFunctions = allowFunctions === true;
14537 this.getKey = keyFn;
14539 Roo.util.MixedCollection.superclass.constructor.call(this);
14542 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14543 allowFunctions : false,
14546 * Adds an item to the collection.
14547 * @param {String} key The key to associate with the item
14548 * @param {Object} o The item to add.
14549 * @return {Object} The item added.
14551 add : function(key, o){
14552 if(arguments.length == 1){
14554 key = this.getKey(o);
14556 if(typeof key == "undefined" || key === null){
14558 this.items.push(o);
14559 this.keys.push(null);
14561 var old = this.map[key];
14563 return this.replace(key, o);
14566 this.items.push(o);
14568 this.keys.push(key);
14570 this.fireEvent("add", this.length-1, o, key);
14575 * MixedCollection has a generic way to fetch keys if you implement getKey.
14578 var mc = new Roo.util.MixedCollection();
14579 mc.add(someEl.dom.id, someEl);
14580 mc.add(otherEl.dom.id, otherEl);
14584 var mc = new Roo.util.MixedCollection();
14585 mc.getKey = function(el){
14591 // or via the constructor
14592 var mc = new Roo.util.MixedCollection(false, function(el){
14598 * @param o {Object} The item for which to find the key.
14599 * @return {Object} The key for the passed item.
14601 getKey : function(o){
14606 * Replaces an item in the collection.
14607 * @param {String} key The key associated with the item to replace, or the item to replace.
14608 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14609 * @return {Object} The new item.
14611 replace : function(key, o){
14612 if(arguments.length == 1){
14614 key = this.getKey(o);
14616 var old = this.item(key);
14617 if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14618 return this.add(key, o);
14620 var index = this.indexOfKey(key);
14621 this.items[index] = o;
14623 this.fireEvent("replace", key, old, o);
14628 * Adds all elements of an Array or an Object to the collection.
14629 * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14630 * an Array of values, each of which are added to the collection.
14632 addAll : function(objs){
14633 if(arguments.length > 1 || objs instanceof Array){
14634 var args = arguments.length > 1 ? arguments : objs;
14635 for(var i = 0, len = args.length; i < len; i++){
14639 for(var key in objs){
14640 if(this.allowFunctions || typeof objs[key] != "function"){
14641 this.add(key, objs[key]);
14648 * Executes the specified function once for every item in the collection, passing each
14649 * item as the first and only parameter. returning false from the function will stop the iteration.
14650 * @param {Function} fn The function to execute for each item.
14651 * @param {Object} scope (optional) The scope in which to execute the function.
14653 each : function(fn, scope){
14654 var items = [].concat(this.items); // each safe for removal
14655 for(var i = 0, len = items.length; i < len; i++){
14656 if(fn.call(scope || items[i], items[i], i, len) === false){
14663 * Executes the specified function once for every key in the collection, passing each
14664 * key, and its associated item as the first two parameters.
14665 * @param {Function} fn The function to execute for each item.
14666 * @param {Object} scope (optional) The scope in which to execute the function.
14668 eachKey : function(fn, scope){
14669 for(var i = 0, len = this.keys.length; i < len; i++){
14670 fn.call(scope || window, this.keys[i], this.items[i], i, len);
14675 * Returns the first item in the collection which elicits a true return value from the
14676 * passed selection function.
14677 * @param {Function} fn The selection function to execute for each item.
14678 * @param {Object} scope (optional) The scope in which to execute the function.
14679 * @return {Object} The first item in the collection which returned true from the selection function.
14681 find : function(fn, scope){
14682 for(var i = 0, len = this.items.length; i < len; i++){
14683 if(fn.call(scope || window, this.items[i], this.keys[i])){
14684 return this.items[i];
14691 * Inserts an item at the specified index in the collection.
14692 * @param {Number} index The index to insert the item at.
14693 * @param {String} key The key to associate with the new item, or the item itself.
14694 * @param {Object} o (optional) If the second parameter was a key, the new item.
14695 * @return {Object} The item inserted.
14697 insert : function(index, key, o){
14698 if(arguments.length == 2){
14700 key = this.getKey(o);
14702 if(index >= this.length){
14703 return this.add(key, o);
14706 this.items.splice(index, 0, o);
14707 if(typeof key != "undefined" && key != null){
14710 this.keys.splice(index, 0, key);
14711 this.fireEvent("add", index, o, key);
14716 * Removed an item from the collection.
14717 * @param {Object} o The item to remove.
14718 * @return {Object} The item removed.
14720 remove : function(o){
14721 return this.removeAt(this.indexOf(o));
14725 * Remove an item from a specified index in the collection.
14726 * @param {Number} index The index within the collection of the item to remove.
14728 removeAt : function(index){
14729 if(index < this.length && index >= 0){
14731 var o = this.items[index];
14732 this.items.splice(index, 1);
14733 var key = this.keys[index];
14734 if(typeof key != "undefined"){
14735 delete this.map[key];
14737 this.keys.splice(index, 1);
14738 this.fireEvent("remove", o, key);
14743 * Removed an item associated with the passed key fom the collection.
14744 * @param {String} key The key of the item to remove.
14746 removeKey : function(key){
14747 return this.removeAt(this.indexOfKey(key));
14751 * Returns the number of items in the collection.
14752 * @return {Number} the number of items in the collection.
14754 getCount : function(){
14755 return this.length;
14759 * Returns index within the collection of the passed Object.
14760 * @param {Object} o The item to find the index of.
14761 * @return {Number} index of the item.
14763 indexOf : function(o){
14764 if(!this.items.indexOf){
14765 for(var i = 0, len = this.items.length; i < len; i++){
14766 if(this.items[i] == o) {
14772 return this.items.indexOf(o);
14777 * Returns index within the collection of the passed key.
14778 * @param {String} key The key to find the index of.
14779 * @return {Number} index of the key.
14781 indexOfKey : function(key){
14782 if(!this.keys.indexOf){
14783 for(var i = 0, len = this.keys.length; i < len; i++){
14784 if(this.keys[i] == key) {
14790 return this.keys.indexOf(key);
14795 * Returns the item associated with the passed key OR index. Key has priority over index.
14796 * @param {String/Number} key The key or index of the item.
14797 * @return {Object} The item associated with the passed key.
14799 item : function(key){
14800 if (key === 'length') {
14803 var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14804 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14808 * Returns the item at the specified index.
14809 * @param {Number} index The index of the item.
14812 itemAt : function(index){
14813 return this.items[index];
14817 * Returns the item associated with the passed key.
14818 * @param {String/Number} key The key of the item.
14819 * @return {Object} The item associated with the passed key.
14821 key : function(key){
14822 return this.map[key];
14826 * Returns true if the collection contains the passed Object as an item.
14827 * @param {Object} o The Object to look for in the collection.
14828 * @return {Boolean} True if the collection contains the Object as an item.
14830 contains : function(o){
14831 return this.indexOf(o) != -1;
14835 * Returns true if the collection contains the passed Object as a key.
14836 * @param {String} key The key to look for in the collection.
14837 * @return {Boolean} True if the collection contains the Object as a key.
14839 containsKey : function(key){
14840 return typeof this.map[key] != "undefined";
14844 * Removes all items from the collection.
14846 clear : function(){
14851 this.fireEvent("clear");
14855 * Returns the first item in the collection.
14856 * @return {Object} the first item in the collection..
14858 first : function(){
14859 return this.items[0];
14863 * Returns the last item in the collection.
14864 * @return {Object} the last item in the collection..
14867 return this.items[this.length-1];
14870 _sort : function(property, dir, fn){
14871 var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14872 fn = fn || function(a, b){
14875 var c = [], k = this.keys, items = this.items;
14876 for(var i = 0, len = items.length; i < len; i++){
14877 c[c.length] = {key: k[i], value: items[i], index: i};
14879 c.sort(function(a, b){
14880 var v = fn(a[property], b[property]) * dsc;
14882 v = (a.index < b.index ? -1 : 1);
14886 for(var i = 0, len = c.length; i < len; i++){
14887 items[i] = c[i].value;
14890 this.fireEvent("sort", this);
14894 * Sorts this collection with the passed comparison function
14895 * @param {String} direction (optional) "ASC" or "DESC"
14896 * @param {Function} fn (optional) comparison function
14898 sort : function(dir, fn){
14899 this._sort("value", dir, fn);
14903 * Sorts this collection by keys
14904 * @param {String} direction (optional) "ASC" or "DESC"
14905 * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14907 keySort : function(dir, fn){
14908 this._sort("key", dir, fn || function(a, b){
14909 return String(a).toUpperCase()-String(b).toUpperCase();
14914 * Returns a range of items in this collection
14915 * @param {Number} startIndex (optional) defaults to 0
14916 * @param {Number} endIndex (optional) default to the last item
14917 * @return {Array} An array of items
14919 getRange : function(start, end){
14920 var items = this.items;
14921 if(items.length < 1){
14924 start = start || 0;
14925 end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14928 for(var i = start; i <= end; i++) {
14929 r[r.length] = items[i];
14932 for(var i = start; i >= end; i--) {
14933 r[r.length] = items[i];
14940 * Filter the <i>objects</i> in this collection by a specific property.
14941 * Returns a new collection that has been filtered.
14942 * @param {String} property A property on your objects
14943 * @param {String/RegExp} value Either string that the property values
14944 * should start with or a RegExp to test against the property
14945 * @return {MixedCollection} The new filtered collection
14947 filter : function(property, value){
14948 if(!value.exec){ // not a regex
14949 value = String(value);
14950 if(value.length == 0){
14951 return this.clone();
14953 value = new RegExp("^" + Roo.escapeRe(value), "i");
14955 return this.filterBy(function(o){
14956 return o && value.test(o[property]);
14961 * Filter by a function. * Returns a new collection that has been filtered.
14962 * The passed function will be called with each
14963 * object in the collection. If the function returns true, the value is included
14964 * otherwise it is filtered.
14965 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14966 * @param {Object} scope (optional) The scope of the function (defaults to this)
14967 * @return {MixedCollection} The new filtered collection
14969 filterBy : function(fn, scope){
14970 var r = new Roo.util.MixedCollection();
14971 r.getKey = this.getKey;
14972 var k = this.keys, it = this.items;
14973 for(var i = 0, len = it.length; i < len; i++){
14974 if(fn.call(scope||this, it[i], k[i])){
14975 r.add(k[i], it[i]);
14982 * Creates a duplicate of this collection
14983 * @return {MixedCollection}
14985 clone : function(){
14986 var r = new Roo.util.MixedCollection();
14987 var k = this.keys, it = this.items;
14988 for(var i = 0, len = it.length; i < len; i++){
14989 r.add(k[i], it[i]);
14991 r.getKey = this.getKey;
14996 * Returns the item associated with the passed key or index.
14998 * @param {String/Number} key The key or index of the item.
14999 * @return {Object} The item associated with the passed key.
15001 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
15003 * Ext JS Library 1.1.1
15004 * Copyright(c) 2006-2007, Ext JS, LLC.
15006 * Originally Released Under LGPL - original licence link has changed is not relivant.
15009 * <script type="text/javascript">
15012 * @class Roo.util.JSON
15013 * Modified version of Douglas Crockford"s json.js that doesn"t
15014 * mess with the Object prototype
15015 * http://www.json.org/js.html
15018 Roo.util.JSON = new (function(){
15019 var useHasOwn = {}.hasOwnProperty ? true : false;
15021 // crashes Safari in some instances
15022 //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15024 var pad = function(n) {
15025 return n < 10 ? "0" + n : n;
15038 var encodeString = function(s){
15039 if (/["\\\x00-\x1f]/.test(s)) {
15040 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15045 c = b.charCodeAt();
15047 Math.floor(c / 16).toString(16) +
15048 (c % 16).toString(16);
15051 return '"' + s + '"';
15054 var encodeArray = function(o){
15055 var a = ["["], b, i, l = o.length, v;
15056 for (i = 0; i < l; i += 1) {
15058 switch (typeof v) {
15067 a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15075 var encodeDate = function(o){
15076 return '"' + o.getFullYear() + "-" +
15077 pad(o.getMonth() + 1) + "-" +
15078 pad(o.getDate()) + "T" +
15079 pad(o.getHours()) + ":" +
15080 pad(o.getMinutes()) + ":" +
15081 pad(o.getSeconds()) + '"';
15085 * Encodes an Object, Array or other value
15086 * @param {Mixed} o The variable to encode
15087 * @return {String} The JSON string
15089 this.encode = function(o)
15091 // should this be extended to fully wrap stringify..
15093 if(typeof o == "undefined" || o === null){
15095 }else if(o instanceof Array){
15096 return encodeArray(o);
15097 }else if(o instanceof Date){
15098 return encodeDate(o);
15099 }else if(typeof o == "string"){
15100 return encodeString(o);
15101 }else if(typeof o == "number"){
15102 return isFinite(o) ? String(o) : "null";
15103 }else if(typeof o == "boolean"){
15106 var a = ["{"], b, i, v;
15108 if(!useHasOwn || o.hasOwnProperty(i)) {
15110 switch (typeof v) {
15119 a.push(this.encode(i), ":",
15120 v === null ? "null" : this.encode(v));
15131 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15132 * @param {String} json The JSON string
15133 * @return {Object} The resulting object
15135 this.decode = function(json){
15137 return /** eval:var:json */ eval("(" + json + ')');
15141 * Shorthand for {@link Roo.util.JSON#encode}
15142 * @member Roo encode
15144 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15146 * Shorthand for {@link Roo.util.JSON#decode}
15147 * @member Roo decode
15149 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15152 * Ext JS Library 1.1.1
15153 * Copyright(c) 2006-2007, Ext JS, LLC.
15155 * Originally Released Under LGPL - original licence link has changed is not relivant.
15158 * <script type="text/javascript">
15162 * @class Roo.util.Format
15163 * Reusable data formatting functions
15166 Roo.util.Format = function(){
15167 var trimRe = /^\s+|\s+$/g;
15170 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15171 * @param {String} value The string to truncate
15172 * @param {Number} length The maximum length to allow before truncating
15173 * @return {String} The converted text
15175 ellipsis : function(value, len){
15176 if(value && value.length > len){
15177 return value.substr(0, len-3)+"...";
15183 * Checks a reference and converts it to empty string if it is undefined
15184 * @param {Mixed} value Reference to check
15185 * @return {Mixed} Empty string if converted, otherwise the original value
15187 undef : function(value){
15188 return typeof value != "undefined" ? value : "";
15192 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15193 * @param {String} value The string to encode
15194 * @return {String} The encoded text
15196 htmlEncode : function(value){
15197 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
15201 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15202 * @param {String} value The string to decode
15203 * @return {String} The decoded text
15205 htmlDecode : function(value){
15206 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"');
15210 * Trims any whitespace from either side of a string
15211 * @param {String} value The text to trim
15212 * @return {String} The trimmed text
15214 trim : function(value){
15215 return String(value).replace(trimRe, "");
15219 * Returns a substring from within an original string
15220 * @param {String} value The original text
15221 * @param {Number} start The start index of the substring
15222 * @param {Number} length The length of the substring
15223 * @return {String} The substring
15225 substr : function(value, start, length){
15226 return String(value).substr(start, length);
15230 * Converts a string to all lower case letters
15231 * @param {String} value The text to convert
15232 * @return {String} The converted text
15234 lowercase : function(value){
15235 return String(value).toLowerCase();
15239 * Converts a string to all upper case letters
15240 * @param {String} value The text to convert
15241 * @return {String} The converted text
15243 uppercase : function(value){
15244 return String(value).toUpperCase();
15248 * Converts the first character only of a string to upper case
15249 * @param {String} value The text to convert
15250 * @return {String} The converted text
15252 capitalize : function(value){
15253 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15257 call : function(value, fn){
15258 if(arguments.length > 2){
15259 var args = Array.prototype.slice.call(arguments, 2);
15260 args.unshift(value);
15262 return /** eval:var:value */ eval(fn).apply(window, args);
15264 /** eval:var:value */
15265 return /** eval:var:value */ eval(fn).call(window, value);
15271 * safer version of Math.toFixed..??/
15272 * @param {Number/String} value The numeric value to format
15273 * @param {Number/String} value Decimal places
15274 * @return {String} The formatted currency string
15276 toFixed : function(v, n)
15278 // why not use to fixed - precision is buggered???
15280 return Math.round(v-0);
15282 var fact = Math.pow(10,n+1);
15283 v = (Math.round((v-0)*fact))/fact;
15284 var z = (''+fact).substring(2);
15285 if (v == Math.floor(v)) {
15286 return Math.floor(v) + '.' + z;
15289 // now just padd decimals..
15290 var ps = String(v).split('.');
15291 var fd = (ps[1] + z);
15292 var r = fd.substring(0,n);
15293 var rm = fd.substring(n);
15295 return ps[0] + '.' + r;
15297 r*=1; // turn it into a number;
15299 if (String(r).length != n) {
15302 r = String(r).substring(1); // chop the end off.
15305 return ps[0] + '.' + r;
15310 * Format a number as US currency
15311 * @param {Number/String} value The numeric value to format
15312 * @return {String} The formatted currency string
15314 usMoney : function(v){
15315 return '$' + Roo.util.Format.number(v);
15320 * eventually this should probably emulate php's number_format
15321 * @param {Number/String} value The numeric value to format
15322 * @param {Number} decimals number of decimal places
15323 * @param {String} delimiter for thousands (default comma)
15324 * @return {String} The formatted currency string
15326 number : function(v, decimals, thousandsDelimiter)
15328 // multiply and round.
15329 decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15330 thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15332 var mul = Math.pow(10, decimals);
15333 var zero = String(mul).substring(1);
15334 v = (Math.round((v-0)*mul))/mul;
15336 // if it's '0' number.. then
15338 //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15340 var ps = v.split('.');
15343 var r = /(\d+)(\d{3})/;
15346 if(thousandsDelimiter.length != 0) {
15347 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15352 (decimals ? ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15353 // does not have decimals
15354 (decimals ? ('.' + zero) : '');
15357 return whole + sub ;
15361 * Parse a value into a formatted date using the specified format pattern.
15362 * @param {Mixed} value The value to format
15363 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15364 * @return {String} The formatted date string
15366 date : function(v, format){
15370 if(!(v instanceof Date)){
15371 v = new Date(Date.parse(v));
15373 return v.dateFormat(format || Roo.util.Format.defaults.date);
15377 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15378 * @param {String} format Any valid date format string
15379 * @return {Function} The date formatting function
15381 dateRenderer : function(format){
15382 return function(v){
15383 return Roo.util.Format.date(v, format);
15388 stripTagsRE : /<\/?[^>]+>/gi,
15391 * Strips all HTML tags
15392 * @param {Mixed} value The text from which to strip tags
15393 * @return {String} The stripped text
15395 stripTags : function(v){
15396 return !v ? v : String(v).replace(this.stripTagsRE, "");
15400 * Size in Mb,Gb etc.
15401 * @param {Number} value The number to be formated
15402 * @param {number} decimals how many decimal places
15403 * @return {String} the formated string
15405 size : function(value, decimals)
15407 var sizes = ['b', 'k', 'M', 'G', 'T'];
15411 var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15412 return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals) + sizes[i];
15419 Roo.util.Format.defaults = {
15423 * Ext JS Library 1.1.1
15424 * Copyright(c) 2006-2007, Ext JS, LLC.
15426 * Originally Released Under LGPL - original licence link has changed is not relivant.
15429 * <script type="text/javascript">
15436 * @class Roo.MasterTemplate
15437 * @extends Roo.Template
15438 * Provides a template that can have child templates. The syntax is:
15440 var t = new Roo.MasterTemplate(
15441 '<select name="{name}">',
15442 '<tpl name="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
15445 t.add('options', {value: 'foo', text: 'bar'});
15446 // or you can add multiple child elements in one shot
15447 t.addAll('options', [
15448 {value: 'foo', text: 'bar'},
15449 {value: 'foo2', text: 'bar2'},
15450 {value: 'foo3', text: 'bar3'}
15452 // then append, applying the master template values
15453 t.append('my-form', {name: 'my-select'});
15455 * A name attribute for the child template is not required if you have only one child
15456 * template or you want to refer to them by index.
15458 Roo.MasterTemplate = function(){
15459 Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15460 this.originalHtml = this.html;
15462 var m, re = this.subTemplateRe;
15465 while(m = re.exec(this.html)){
15466 var name = m[1], content = m[2];
15471 tpl : new Roo.Template(content)
15474 st[name] = st[subIndex];
15476 st[subIndex].tpl.compile();
15477 st[subIndex].tpl.call = this.call.createDelegate(this);
15480 this.subCount = subIndex;
15483 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15485 * The regular expression used to match sub templates
15489 subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15492 * Applies the passed values to a child template.
15493 * @param {String/Number} name (optional) The name or index of the child template
15494 * @param {Array/Object} values The values to be applied to the template
15495 * @return {MasterTemplate} this
15497 add : function(name, values){
15498 if(arguments.length == 1){
15499 values = arguments[0];
15502 var s = this.subs[name];
15503 s.buffer[s.buffer.length] = s.tpl.apply(values);
15508 * Applies all the passed values to a child template.
15509 * @param {String/Number} name (optional) The name or index of the child template
15510 * @param {Array} values The values to be applied to the template, this should be an array of objects.
15511 * @param {Boolean} reset (optional) True to reset the template first
15512 * @return {MasterTemplate} this
15514 fill : function(name, values, reset){
15516 if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15524 for(var i = 0, len = values.length; i < len; i++){
15525 this.add(name, values[i]);
15531 * Resets the template for reuse
15532 * @return {MasterTemplate} this
15534 reset : function(){
15536 for(var i = 0; i < this.subCount; i++){
15542 applyTemplate : function(values){
15544 var replaceIndex = -1;
15545 this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15546 return s[++replaceIndex].buffer.join("");
15548 return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15551 apply : function(){
15552 return this.applyTemplate.apply(this, arguments);
15555 compile : function(){return this;}
15559 * Alias for fill().
15562 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15564 * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15565 * var tpl = Roo.MasterTemplate.from('element-id');
15566 * @param {String/HTMLElement} el
15567 * @param {Object} config
15570 Roo.MasterTemplate.from = function(el, config){
15571 el = Roo.getDom(el);
15572 return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15575 * Ext JS Library 1.1.1
15576 * Copyright(c) 2006-2007, Ext JS, LLC.
15578 * Originally Released Under LGPL - original licence link has changed is not relivant.
15581 * <script type="text/javascript">
15586 * @class Roo.util.CSS
15587 * Utility class for manipulating CSS rules
15591 Roo.util.CSS = function(){
15593 var doc = document;
15595 var camelRe = /(-[a-z])/gi;
15596 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15600 * Very simple dynamic creation of stylesheets from a text blob of rules. The text will wrapped in a style
15601 * tag and appended to the HEAD of the document.
15602 * @param {String|Object} cssText The text containing the css rules
15603 * @param {String} id An id to add to the stylesheet for later removal
15604 * @return {StyleSheet}
15606 createStyleSheet : function(cssText, id){
15608 var head = doc.getElementsByTagName("head")[0];
15609 var nrules = doc.createElement("style");
15610 nrules.setAttribute("type", "text/css");
15612 nrules.setAttribute("id", id);
15614 if (typeof(cssText) != 'string') {
15615 // support object maps..
15616 // not sure if this a good idea..
15617 // perhaps it should be merged with the general css handling
15618 // and handle js style props.
15619 var cssTextNew = [];
15620 for(var n in cssText) {
15622 for(var k in cssText[n]) {
15623 citems.push( k + ' : ' +cssText[n][k] + ';' );
15625 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15628 cssText = cssTextNew.join("\n");
15634 head.appendChild(nrules);
15635 ss = nrules.styleSheet;
15636 ss.cssText = cssText;
15639 nrules.appendChild(doc.createTextNode(cssText));
15641 nrules.cssText = cssText;
15643 head.appendChild(nrules);
15644 ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15646 this.cacheStyleSheet(ss);
15651 * Removes a style or link tag by id
15652 * @param {String} id The id of the tag
15654 removeStyleSheet : function(id){
15655 var existing = doc.getElementById(id);
15657 existing.parentNode.removeChild(existing);
15662 * Dynamically swaps an existing stylesheet reference for a new one
15663 * @param {String} id The id of an existing link tag to remove
15664 * @param {String} url The href of the new stylesheet to include
15666 swapStyleSheet : function(id, url){
15667 this.removeStyleSheet(id);
15668 var ss = doc.createElement("link");
15669 ss.setAttribute("rel", "stylesheet");
15670 ss.setAttribute("type", "text/css");
15671 ss.setAttribute("id", id);
15672 ss.setAttribute("href", url);
15673 doc.getElementsByTagName("head")[0].appendChild(ss);
15677 * Refresh the rule cache if you have dynamically added stylesheets
15678 * @return {Object} An object (hash) of rules indexed by selector
15680 refreshCache : function(){
15681 return this.getRules(true);
15685 cacheStyleSheet : function(stylesheet){
15689 try{// try catch for cross domain access issue
15690 var ssRules = stylesheet.cssRules || stylesheet.rules;
15691 for(var j = ssRules.length-1; j >= 0; --j){
15692 rules[ssRules[j].selectorText] = ssRules[j];
15698 * Gets all css rules for the document
15699 * @param {Boolean} refreshCache true to refresh the internal cache
15700 * @return {Object} An object (hash) of rules indexed by selector
15702 getRules : function(refreshCache){
15703 if(rules == null || refreshCache){
15705 var ds = doc.styleSheets;
15706 for(var i =0, len = ds.length; i < len; i++){
15708 this.cacheStyleSheet(ds[i]);
15716 * Gets an an individual CSS rule by selector(s)
15717 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15718 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15719 * @return {CSSRule} The CSS rule or null if one is not found
15721 getRule : function(selector, refreshCache){
15722 var rs = this.getRules(refreshCache);
15723 if(!(selector instanceof Array)){
15724 return rs[selector];
15726 for(var i = 0; i < selector.length; i++){
15727 if(rs[selector[i]]){
15728 return rs[selector[i]];
15736 * Updates a rule property
15737 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15738 * @param {String} property The css property
15739 * @param {String} value The new value for the property
15740 * @return {Boolean} true If a rule was found and updated
15742 updateRule : function(selector, property, value){
15743 if(!(selector instanceof Array)){
15744 var rule = this.getRule(selector);
15746 rule.style[property.replace(camelRe, camelFn)] = value;
15750 for(var i = 0; i < selector.length; i++){
15751 if(this.updateRule(selector[i], property, value)){
15761 * Ext JS Library 1.1.1
15762 * Copyright(c) 2006-2007, Ext JS, LLC.
15764 * Originally Released Under LGPL - original licence link has changed is not relivant.
15767 * <script type="text/javascript">
15773 * @class Roo.util.ClickRepeater
15774 * @extends Roo.util.Observable
15776 * A wrapper class which can be applied to any element. Fires a "click" event while the
15777 * mouse is pressed. The interval between firings may be specified in the config but
15778 * defaults to 10 milliseconds.
15780 * Optionally, a CSS class may be applied to the element during the time it is pressed.
15782 * @cfg {String/HTMLElement/Element} el The element to act as a button.
15783 * @cfg {Number} delay The initial delay before the repeating event begins firing.
15784 * Similar to an autorepeat key delay.
15785 * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15786 * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15787 * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15788 * "interval" and "delay" are ignored. "immediate" is honored.
15789 * @cfg {Boolean} preventDefault True to prevent the default click event
15790 * @cfg {Boolean} stopDefault True to stop the default click event
15793 * 2007-02-02 jvs Original code contributed by Nige "Animal" White
15794 * 2007-02-02 jvs Renamed to ClickRepeater
15795 * 2007-02-03 jvs Modifications for FF Mac and Safari
15798 * @param {String/HTMLElement/Element} el The element to listen on
15799 * @param {Object} config
15801 Roo.util.ClickRepeater = function(el, config)
15803 this.el = Roo.get(el);
15804 this.el.unselectable();
15806 Roo.apply(this, config);
15811 * Fires when the mouse button is depressed.
15812 * @param {Roo.util.ClickRepeater} this
15814 "mousedown" : true,
15817 * Fires on a specified interval during the time the element is pressed.
15818 * @param {Roo.util.ClickRepeater} this
15823 * Fires when the mouse key is released.
15824 * @param {Roo.util.ClickRepeater} this
15829 this.el.on("mousedown", this.handleMouseDown, this);
15830 if(this.preventDefault || this.stopDefault){
15831 this.el.on("click", function(e){
15832 if(this.preventDefault){
15833 e.preventDefault();
15835 if(this.stopDefault){
15841 // allow inline handler
15843 this.on("click", this.handler, this.scope || this);
15846 Roo.util.ClickRepeater.superclass.constructor.call(this);
15849 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15852 preventDefault : true,
15853 stopDefault : false,
15857 handleMouseDown : function(){
15858 clearTimeout(this.timer);
15860 if(this.pressClass){
15861 this.el.addClass(this.pressClass);
15863 this.mousedownTime = new Date();
15865 Roo.get(document).on("mouseup", this.handleMouseUp, this);
15866 this.el.on("mouseout", this.handleMouseOut, this);
15868 this.fireEvent("mousedown", this);
15869 this.fireEvent("click", this);
15871 this.timer = this.click.defer(this.delay || this.interval, this);
15875 click : function(){
15876 this.fireEvent("click", this);
15877 this.timer = this.click.defer(this.getInterval(), this);
15881 getInterval: function(){
15882 if(!this.accelerate){
15883 return this.interval;
15885 var pressTime = this.mousedownTime.getElapsed();
15886 if(pressTime < 500){
15888 }else if(pressTime < 1700){
15890 }else if(pressTime < 2600){
15892 }else if(pressTime < 3500){
15894 }else if(pressTime < 4400){
15896 }else if(pressTime < 5300){
15898 }else if(pressTime < 6200){
15906 handleMouseOut : function(){
15907 clearTimeout(this.timer);
15908 if(this.pressClass){
15909 this.el.removeClass(this.pressClass);
15911 this.el.on("mouseover", this.handleMouseReturn, this);
15915 handleMouseReturn : function(){
15916 this.el.un("mouseover", this.handleMouseReturn);
15917 if(this.pressClass){
15918 this.el.addClass(this.pressClass);
15924 handleMouseUp : function(){
15925 clearTimeout(this.timer);
15926 this.el.un("mouseover", this.handleMouseReturn);
15927 this.el.un("mouseout", this.handleMouseOut);
15928 Roo.get(document).un("mouseup", this.handleMouseUp);
15929 this.el.removeClass(this.pressClass);
15930 this.fireEvent("mouseup", this);
15933 * @class Roo.util.Clipboard
15939 Roo.util.Clipboard = {
15941 * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15942 * @param {String} text to copy to clipboard
15944 write : function(text) {
15945 // navigator clipboard api needs a secure context (https)
15946 if (navigator.clipboard && window.isSecureContext) {
15947 // navigator clipboard api method'
15948 navigator.clipboard.writeText(text);
15951 // text area method
15952 var ta = document.createElement("textarea");
15954 // make the textarea out of viewport
15955 ta.style.position = "fixed";
15956 ta.style.left = "-999999px";
15957 ta.style.top = "-999999px";
15958 document.body.appendChild(ta);
15961 document.execCommand('copy');
15971 * Ext JS Library 1.1.1
15972 * Copyright(c) 2006-2007, Ext JS, LLC.
15974 * Originally Released Under LGPL - original licence link has changed is not relivant.
15977 * <script type="text/javascript">
15982 * @class Roo.KeyNav
15983 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
15984 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15985 * way to implement custom navigation schemes for any UI component.</p>
15986 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15987 * pageUp, pageDown, del, home, end. Usage:</p>
15989 var nav = new Roo.KeyNav("my-element", {
15990 "left" : function(e){
15991 this.moveLeft(e.ctrlKey);
15993 "right" : function(e){
15994 this.moveRight(e.ctrlKey);
15996 "enter" : function(e){
16003 * @param {String/HTMLElement/Roo.Element} el The element to bind to
16004 * @param {Object} config The config
16006 Roo.KeyNav = function(el, config){
16007 this.el = Roo.get(el);
16008 Roo.apply(this, config);
16009 if(!this.disabled){
16010 this.disabled = true;
16015 Roo.KeyNav.prototype = {
16017 * @cfg {Boolean} disabled
16018 * True to disable this KeyNav instance (defaults to false)
16022 * @cfg {String} defaultEventAction
16023 * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key. Valid values are
16024 * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16025 * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16027 defaultEventAction: "stopEvent",
16029 * @cfg {Boolean} forceKeyDown
16030 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
16031 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16032 * handle keydown instead of keypress.
16034 forceKeyDown : false,
16037 prepareEvent : function(e){
16038 var k = e.getKey();
16039 var h = this.keyToHandler[k];
16040 //if(h && this[h]){
16041 // e.stopPropagation();
16043 if(Roo.isSafari && h && k >= 37 && k <= 40){
16049 relay : function(e){
16050 var k = e.getKey();
16051 var h = this.keyToHandler[k];
16053 if(this.doRelay(e, this[h], h) !== true){
16054 e[this.defaultEventAction]();
16060 doRelay : function(e, h, hname){
16061 return h.call(this.scope || this, e);
16064 // possible handlers
16078 // quick lookup hash
16095 * Enable this KeyNav
16097 enable: function(){
16099 // ie won't do special keys on keypress, no one else will repeat keys with keydown
16100 // the EventObject will normalize Safari automatically
16101 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16102 this.el.on("keydown", this.relay, this);
16104 this.el.on("keydown", this.prepareEvent, this);
16105 this.el.on("keypress", this.relay, this);
16107 this.disabled = false;
16112 * Disable this KeyNav
16114 disable: function(){
16115 if(!this.disabled){
16116 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16117 this.el.un("keydown", this.relay);
16119 this.el.un("keydown", this.prepareEvent);
16120 this.el.un("keypress", this.relay);
16122 this.disabled = true;
16127 * Ext JS Library 1.1.1
16128 * Copyright(c) 2006-2007, Ext JS, LLC.
16130 * Originally Released Under LGPL - original licence link has changed is not relivant.
16133 * <script type="text/javascript">
16138 * @class Roo.KeyMap
16139 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16140 * The constructor accepts the same config object as defined by {@link #addBinding}.
16141 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16142 * combination it will call the function with this signature (if the match is a multi-key
16143 * combination the callback will still be called only once): (String key, Roo.EventObject e)
16144 * A KeyMap can also handle a string representation of keys.<br />
16147 // map one key by key code
16148 var map = new Roo.KeyMap("my-element", {
16149 key: 13, // or Roo.EventObject.ENTER
16154 // map multiple keys to one action by string
16155 var map = new Roo.KeyMap("my-element", {
16161 // map multiple keys to multiple actions by strings and array of codes
16162 var map = new Roo.KeyMap("my-element", [
16165 fn: function(){ alert("Return was pressed"); }
16168 fn: function(){ alert('a, b or c was pressed'); }
16173 fn: function(){ alert('Control + shift + tab was pressed.'); }
16177 * <b>Note: A KeyMap starts enabled</b>
16179 * @param {String/HTMLElement/Roo.Element} el The element to bind to
16180 * @param {Object} config The config (see {@link #addBinding})
16181 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16183 Roo.KeyMap = function(el, config, eventName){
16184 this.el = Roo.get(el);
16185 this.eventName = eventName || "keydown";
16186 this.bindings = [];
16188 this.addBinding(config);
16193 Roo.KeyMap.prototype = {
16195 * True to stop the event from bubbling and prevent the default browser action if the
16196 * key was handled by the KeyMap (defaults to false)
16202 * Add a new binding to this KeyMap. The following config object properties are supported:
16204 Property Type Description
16205 ---------- --------------- ----------------------------------------------------------------------
16206 key String/Array A single keycode or an array of keycodes to handle
16207 shift Boolean True to handle key only when shift is pressed (defaults to false)
16208 ctrl Boolean True to handle key only when ctrl is pressed (defaults to false)
16209 alt Boolean True to handle key only when alt is pressed (defaults to false)
16210 fn Function The function to call when KeyMap finds the expected key combination
16211 scope Object The scope of the callback function
16217 var map = new Roo.KeyMap(document, {
16218 key: Roo.EventObject.ENTER,
16223 //Add a new binding to the existing KeyMap later
16231 * @param {Object/Array} config A single KeyMap config or an array of configs
16233 addBinding : function(config){
16234 if(config instanceof Array){
16235 for(var i = 0, len = config.length; i < len; i++){
16236 this.addBinding(config[i]);
16240 var keyCode = config.key,
16241 shift = config.shift,
16242 ctrl = config.ctrl,
16245 scope = config.scope;
16246 if(typeof keyCode == "string"){
16248 var keyString = keyCode.toUpperCase();
16249 for(var j = 0, len = keyString.length; j < len; j++){
16250 ks.push(keyString.charCodeAt(j));
16254 var keyArray = keyCode instanceof Array;
16255 var handler = function(e){
16256 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
16257 var k = e.getKey();
16259 for(var i = 0, len = keyCode.length; i < len; i++){
16260 if(keyCode[i] == k){
16261 if(this.stopEvent){
16264 fn.call(scope || window, k, e);
16270 if(this.stopEvent){
16273 fn.call(scope || window, k, e);
16278 this.bindings.push(handler);
16282 * Shorthand for adding a single key listener
16283 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16284 * following options:
16285 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16286 * @param {Function} fn The function to call
16287 * @param {Object} scope (optional) The scope of the function
16289 on : function(key, fn, scope){
16290 var keyCode, shift, ctrl, alt;
16291 if(typeof key == "object" && !(key instanceof Array)){
16310 handleKeyDown : function(e){
16311 if(this.enabled){ //just in case
16312 var b = this.bindings;
16313 for(var i = 0, len = b.length; i < len; i++){
16314 b[i].call(this, e);
16320 * Returns true if this KeyMap is enabled
16321 * @return {Boolean}
16323 isEnabled : function(){
16324 return this.enabled;
16328 * Enables this KeyMap
16330 enable: function(){
16332 this.el.on(this.eventName, this.handleKeyDown, this);
16333 this.enabled = true;
16338 * Disable this KeyMap
16340 disable: function(){
16342 this.el.removeListener(this.eventName, this.handleKeyDown, this);
16343 this.enabled = false;
16348 * Ext JS Library 1.1.1
16349 * Copyright(c) 2006-2007, Ext JS, LLC.
16351 * Originally Released Under LGPL - original licence link has changed is not relivant.
16354 * <script type="text/javascript">
16359 * @class Roo.util.TextMetrics
16360 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16361 * wide, in pixels, a given block of text will be.
16364 Roo.util.TextMetrics = function(){
16368 * Measures the size of the specified text
16369 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16370 * that can affect the size of the rendered text
16371 * @param {String} text The text to measure
16372 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16373 * in order to accurately measure the text height
16374 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16376 measure : function(el, text, fixedWidth){
16378 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16381 shared.setFixedWidth(fixedWidth || 'auto');
16382 return shared.getSize(text);
16386 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
16387 * the overhead of multiple calls to initialize the style properties on each measurement.
16388 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16389 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16390 * in order to accurately measure the text height
16391 * @return {Roo.util.TextMetrics.Instance} instance The new instance
16393 createInstance : function(el, fixedWidth){
16394 return Roo.util.TextMetrics.Instance(el, fixedWidth);
16400 * @class Roo.util.TextMetrics.Instance
16401 * Instance of TextMetrics Calcuation
16403 * Create a new TextMetrics Instance
16404 * @param {Object} bindto
16405 * @param {Boolean} fixedWidth
16408 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16410 var ml = new Roo.Element(document.createElement('div'));
16411 document.body.appendChild(ml.dom);
16412 ml.position('absolute');
16413 ml.setLeftTop(-1000, -1000);
16417 ml.setWidth(fixedWidth);
16422 * Returns the size of the specified text based on the internal element's style and width properties
16423 * @param {String} text The text to measure
16424 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16426 getSize : function(text){
16428 var s = ml.getSize();
16434 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16435 * that can affect the size of the rendered text
16436 * @param {String/HTMLElement} el The element, dom node or id
16438 bind : function(el){
16440 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16445 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
16446 * to set a fixed width in order to accurately measure the text height.
16447 * @param {Number} width The width to set on the element
16449 setFixedWidth : function(width){
16450 ml.setWidth(width);
16454 * Returns the measured width of the specified text
16455 * @param {String} text The text to measure
16456 * @return {Number} width The width in pixels
16458 getWidth : function(text){
16459 ml.dom.style.width = 'auto';
16460 return this.getSize(text).width;
16464 * Returns the measured height of the specified text. For multiline text, be sure to call
16465 * {@link #setFixedWidth} if necessary.
16466 * @param {String} text The text to measure
16467 * @return {Number} height The height in pixels
16469 getHeight : function(text){
16470 return this.getSize(text).height;
16474 instance.bind(bindTo);
16479 // backwards compat
16480 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16482 * Ext JS Library 1.1.1
16483 * Copyright(c) 2006-2007, Ext JS, LLC.
16485 * Originally Released Under LGPL - original licence link has changed is not relivant.
16488 * <script type="text/javascript">
16492 * @class Roo.state.Provider
16493 * Abstract base class for state provider implementations. This class provides methods
16494 * for encoding and decoding <b>typed</b> variables including dates and defines the
16495 * Provider interface.
16497 Roo.state.Provider = function(){
16499 * @event statechange
16500 * Fires when a state change occurs.
16501 * @param {Provider} this This state provider
16502 * @param {String} key The state key which was changed
16503 * @param {String} value The encoded value for the state
16506 "statechange": true
16509 Roo.state.Provider.superclass.constructor.call(this);
16511 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16513 * Returns the current value for a key
16514 * @param {String} name The key name
16515 * @param {Mixed} defaultValue A default value to return if the key's value is not found
16516 * @return {Mixed} The state data
16518 get : function(name, defaultValue){
16519 return typeof this.state[name] == "undefined" ?
16520 defaultValue : this.state[name];
16524 * Clears a value from the state
16525 * @param {String} name The key name
16527 clear : function(name){
16528 delete this.state[name];
16529 this.fireEvent("statechange", this, name, null);
16533 * Sets the value for a key
16534 * @param {String} name The key name
16535 * @param {Mixed} value The value to set
16537 set : function(name, value){
16538 this.state[name] = value;
16539 this.fireEvent("statechange", this, name, value);
16543 * Decodes a string previously encoded with {@link #encodeValue}.
16544 * @param {String} value The value to decode
16545 * @return {Mixed} The decoded value
16547 decodeValue : function(cookie){
16548 var re = /^(a|n|d|b|s|o)\:(.*)$/;
16549 var matches = re.exec(unescape(cookie));
16550 if(!matches || !matches[1]) {
16551 return; // non state cookie
16553 var type = matches[1];
16554 var v = matches[2];
16557 return parseFloat(v);
16559 return new Date(Date.parse(v));
16564 var values = v.split("^");
16565 for(var i = 0, len = values.length; i < len; i++){
16566 all.push(this.decodeValue(values[i]));
16571 var values = v.split("^");
16572 for(var i = 0, len = values.length; i < len; i++){
16573 var kv = values[i].split("=");
16574 all[kv[0]] = this.decodeValue(kv[1]);
16583 * Encodes a value including type information. Decode with {@link #decodeValue}.
16584 * @param {Mixed} value The value to encode
16585 * @return {String} The encoded value
16587 encodeValue : function(v){
16589 if(typeof v == "number"){
16591 }else if(typeof v == "boolean"){
16592 enc = "b:" + (v ? "1" : "0");
16593 }else if(v instanceof Date){
16594 enc = "d:" + v.toGMTString();
16595 }else if(v instanceof Array){
16597 for(var i = 0, len = v.length; i < len; i++){
16598 flat += this.encodeValue(v[i]);
16604 }else if(typeof v == "object"){
16607 if(typeof v[key] != "function"){
16608 flat += key + "=" + this.encodeValue(v[key]) + "^";
16611 enc = "o:" + flat.substring(0, flat.length-1);
16615 return escape(enc);
16621 * Ext JS Library 1.1.1
16622 * Copyright(c) 2006-2007, Ext JS, LLC.
16624 * Originally Released Under LGPL - original licence link has changed is not relivant.
16627 * <script type="text/javascript">
16630 * @class Roo.state.Manager
16631 * This is the global state manager. By default all components that are "state aware" check this class
16632 * for state information if you don't pass them a custom state provider. In order for this class
16633 * to be useful, it must be initialized with a provider when your application initializes.
16635 // in your initialization function
16637 Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16639 // supposed you have a {@link Roo.layout.Border}
16640 var layout = new Roo.layout.Border(...);
16641 layout.restoreState();
16642 // or a {Roo.BasicDialog}
16643 var dialog = new Roo.BasicDialog(...);
16644 dialog.restoreState();
16648 Roo.state.Manager = function(){
16649 var provider = new Roo.state.Provider();
16653 * Configures the default state provider for your application
16654 * @param {Provider} stateProvider The state provider to set
16656 setProvider : function(stateProvider){
16657 provider = stateProvider;
16661 * Returns the current value for a key
16662 * @param {String} name The key name
16663 * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16664 * @return {Mixed} The state data
16666 get : function(key, defaultValue){
16667 return provider.get(key, defaultValue);
16671 * Sets the value for a key
16672 * @param {String} name The key name
16673 * @param {Mixed} value The state data
16675 set : function(key, value){
16676 provider.set(key, value);
16680 * Clears a value from the state
16681 * @param {String} name The key name
16683 clear : function(key){
16684 provider.clear(key);
16688 * Gets the currently configured state provider
16689 * @return {Provider} The state provider
16691 getProvider : function(){
16698 * Ext JS Library 1.1.1
16699 * Copyright(c) 2006-2007, Ext JS, LLC.
16701 * Originally Released Under LGPL - original licence link has changed is not relivant.
16704 * <script type="text/javascript">
16707 * @class Roo.state.CookieProvider
16708 * @extends Roo.state.Provider
16709 * The default Provider implementation which saves state via cookies.
16712 var cp = new Roo.state.CookieProvider({
16714 expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16715 domain: "roojs.com"
16717 Roo.state.Manager.setProvider(cp);
16719 * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16720 * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16721 * @cfg {String} domain The domain to save the cookie for. Note that you cannot specify a different domain than
16722 * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16723 * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16724 * domain the page is running on including the 'www' like 'www.roojs.com')
16725 * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16727 * Create a new CookieProvider
16728 * @param {Object} config The configuration object
16730 Roo.state.CookieProvider = function(config){
16731 Roo.state.CookieProvider.superclass.constructor.call(this);
16733 this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16734 this.domain = null;
16735 this.secure = false;
16736 Roo.apply(this, config);
16737 this.state = this.readCookies();
16740 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16742 set : function(name, value){
16743 if(typeof value == "undefined" || value === null){
16747 this.setCookie(name, value);
16748 Roo.state.CookieProvider.superclass.set.call(this, name, value);
16752 clear : function(name){
16753 this.clearCookie(name);
16754 Roo.state.CookieProvider.superclass.clear.call(this, name);
16758 readCookies : function(){
16760 var c = document.cookie + ";";
16761 var re = /\s?(.*?)=(.*?);/g;
16763 while((matches = re.exec(c)) != null){
16764 var name = matches[1];
16765 var value = matches[2];
16766 if(name && name.substring(0,3) == "ys-"){
16767 cookies[name.substr(3)] = this.decodeValue(value);
16774 setCookie : function(name, value){
16775 document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16776 ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16777 ((this.path == null) ? "" : ("; path=" + this.path)) +
16778 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16779 ((this.secure == true) ? "; secure" : "");
16783 clearCookie : function(name){
16784 document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16785 ((this.path == null) ? "" : ("; path=" + this.path)) +
16786 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16787 ((this.secure == true) ? "; secure" : "");
16791 * Ext JS Library 1.1.1
16792 * Copyright(c) 2006-2007, Ext JS, LLC.
16794 * Originally Released Under LGPL - original licence link has changed is not relivant.
16797 * <script type="text/javascript">
16802 * @class Roo.ComponentMgr
16803 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16806 Roo.ComponentMgr = function(){
16807 var all = new Roo.util.MixedCollection();
16811 * Registers a component.
16812 * @param {Roo.Component} c The component
16814 register : function(c){
16819 * Unregisters a component.
16820 * @param {Roo.Component} c The component
16822 unregister : function(c){
16827 * Returns a component by id
16828 * @param {String} id The component id
16830 get : function(id){
16831 return all.get(id);
16835 * Registers a function that will be called when a specified component is added to ComponentMgr
16836 * @param {String} id The component id
16837 * @param {Funtction} fn The callback function
16838 * @param {Object} scope The scope of the callback
16840 onAvailable : function(id, fn, scope){
16841 all.on("add", function(index, o){
16843 fn.call(scope || o, o);
16844 all.un("add", fn, scope);
16851 * Ext JS Library 1.1.1
16852 * Copyright(c) 2006-2007, Ext JS, LLC.
16854 * Originally Released Under LGPL - original licence link has changed is not relivant.
16857 * <script type="text/javascript">
16861 * @class Roo.Component
16862 * @extends Roo.util.Observable
16863 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
16864 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
16865 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16866 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16867 * All visual components (widgets) that require rendering into a layout should subclass Component.
16869 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
16870 * 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
16871 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
16873 Roo.Component = function(config){
16874 config = config || {};
16875 if(config.tagName || config.dom || typeof config == "string"){ // element object
16876 config = {el: config, id: config.id || config};
16878 this.initialConfig = config;
16880 Roo.apply(this, config);
16884 * Fires after the component is disabled.
16885 * @param {Roo.Component} this
16890 * Fires after the component is enabled.
16891 * @param {Roo.Component} this
16895 * @event beforeshow
16896 * Fires before the component is shown. Return false to stop the show.
16897 * @param {Roo.Component} this
16902 * Fires after the component is shown.
16903 * @param {Roo.Component} this
16907 * @event beforehide
16908 * Fires before the component is hidden. Return false to stop the hide.
16909 * @param {Roo.Component} this
16914 * Fires after the component is hidden.
16915 * @param {Roo.Component} this
16919 * @event beforerender
16920 * Fires before the component is rendered. Return false to stop the render.
16921 * @param {Roo.Component} this
16923 beforerender : true,
16926 * Fires after the component is rendered.
16927 * @param {Roo.Component} this
16931 * @event beforedestroy
16932 * Fires before the component is destroyed. Return false to stop the destroy.
16933 * @param {Roo.Component} this
16935 beforedestroy : true,
16938 * Fires after the component is destroyed.
16939 * @param {Roo.Component} this
16944 this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16946 Roo.ComponentMgr.register(this);
16947 Roo.Component.superclass.constructor.call(this);
16948 this.initComponent();
16949 if(this.renderTo){ // not supported by all components yet. use at your own risk!
16950 this.render(this.renderTo);
16951 delete this.renderTo;
16956 Roo.Component.AUTO_ID = 1000;
16958 Roo.extend(Roo.Component, Roo.util.Observable, {
16960 * @scope Roo.Component.prototype
16962 * true if this component is hidden. Read-only.
16967 * true if this component is disabled. Read-only.
16972 * true if this component has been rendered. Read-only.
16976 /** @cfg {String} disableClass
16977 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16979 disabledClass : "x-item-disabled",
16980 /** @cfg {Boolean} allowDomMove
16981 * Whether the component can move the Dom node when rendering (defaults to true).
16983 allowDomMove : true,
16984 /** @cfg {String} hideMode (display|visibility)
16985 * How this component should hidden. Supported values are
16986 * "visibility" (css visibility), "offsets" (negative offset position) and
16987 * "display" (css display) - defaults to "display".
16989 hideMode: 'display',
16992 ctype : "Roo.Component",
16995 * @cfg {String} actionMode
16996 * which property holds the element that used for hide() / show() / disable() / enable()
16997 * default is 'el' for forms you probably want to set this to fieldEl
17002 * @cfg {String} style
17003 * css styles to add to component
17004 * eg. text-align:right;
17009 getActionEl : function(){
17010 return this[this.actionMode];
17013 initComponent : Roo.emptyFn,
17015 * If this is a lazy rendering component, render it to its container element.
17016 * @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.
17018 render : function(container, position){
17024 if(this.fireEvent("beforerender", this) === false){
17028 if(!container && this.el){
17029 this.el = Roo.get(this.el);
17030 container = this.el.dom.parentNode;
17031 this.allowDomMove = false;
17033 this.container = Roo.get(container);
17034 this.rendered = true;
17035 if(position !== undefined){
17036 if(typeof position == 'number'){
17037 position = this.container.dom.childNodes[position];
17039 position = Roo.getDom(position);
17042 this.onRender(this.container, position || null);
17044 this.el.addClass(this.cls);
17048 this.el.applyStyles(this.style);
17051 this.fireEvent("render", this);
17052 this.afterRender(this.container);
17065 // default function is not really useful
17066 onRender : function(ct, position){
17068 this.el = Roo.get(this.el);
17069 if(this.allowDomMove !== false){
17070 ct.dom.insertBefore(this.el.dom, position);
17076 getAutoCreate : function(){
17077 var cfg = typeof this.autoCreate == "object" ?
17078 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17079 if(this.id && !cfg.id){
17086 afterRender : Roo.emptyFn,
17089 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17090 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17092 destroy : function(){
17093 if(this.fireEvent("beforedestroy", this) !== false){
17094 this.purgeListeners();
17095 this.beforeDestroy();
17097 this.el.removeAllListeners();
17099 if(this.actionMode == "container"){
17100 this.container.remove();
17104 Roo.ComponentMgr.unregister(this);
17105 this.fireEvent("destroy", this);
17110 beforeDestroy : function(){
17115 onDestroy : function(){
17120 * Returns the underlying {@link Roo.Element}.
17121 * @return {Roo.Element} The element
17123 getEl : function(){
17128 * Returns the id of this component.
17131 getId : function(){
17136 * Try to focus this component.
17137 * @param {Boolean} selectText True to also select the text in this component (if applicable)
17138 * @return {Roo.Component} this
17140 focus : function(selectText){
17143 if(selectText === true){
17144 this.el.dom.select();
17159 * Disable this component.
17160 * @return {Roo.Component} this
17162 disable : function(){
17166 this.disabled = true;
17167 this.fireEvent("disable", this);
17172 onDisable : function(){
17173 this.getActionEl().addClass(this.disabledClass);
17174 this.el.dom.disabled = true;
17178 * Enable this component.
17179 * @return {Roo.Component} this
17181 enable : function(){
17185 this.disabled = false;
17186 this.fireEvent("enable", this);
17191 onEnable : function(){
17192 this.getActionEl().removeClass(this.disabledClass);
17193 this.el.dom.disabled = false;
17197 * Convenience function for setting disabled/enabled by boolean.
17198 * @param {Boolean} disabled
17200 setDisabled : function(disabled){
17201 this[disabled ? "disable" : "enable"]();
17205 * Show this component.
17206 * @return {Roo.Component} this
17209 if(this.fireEvent("beforeshow", this) !== false){
17210 this.hidden = false;
17214 this.fireEvent("show", this);
17220 onShow : function(){
17221 var ae = this.getActionEl();
17222 if(this.hideMode == 'visibility'){
17223 ae.dom.style.visibility = "visible";
17224 }else if(this.hideMode == 'offsets'){
17225 ae.removeClass('x-hidden');
17227 ae.dom.style.display = "";
17232 * Hide this component.
17233 * @return {Roo.Component} this
17236 if(this.fireEvent("beforehide", this) !== false){
17237 this.hidden = true;
17241 this.fireEvent("hide", this);
17247 onHide : function(){
17248 var ae = this.getActionEl();
17249 if(this.hideMode == 'visibility'){
17250 ae.dom.style.visibility = "hidden";
17251 }else if(this.hideMode == 'offsets'){
17252 ae.addClass('x-hidden');
17254 ae.dom.style.display = "none";
17259 * Convenience function to hide or show this component by boolean.
17260 * @param {Boolean} visible True to show, false to hide
17261 * @return {Roo.Component} this
17263 setVisible: function(visible){
17273 * Returns true if this component is visible.
17275 isVisible : function(){
17276 return this.getActionEl().isVisible();
17279 cloneConfig : function(overrides){
17280 overrides = overrides || {};
17281 var id = overrides.id || Roo.id();
17282 var cfg = Roo.applyIf(overrides, this.initialConfig);
17283 cfg.id = id; // prevent dup id
17284 return new this.constructor(cfg);
17288 * Ext JS Library 1.1.1
17289 * Copyright(c) 2006-2007, Ext JS, LLC.
17291 * Originally Released Under LGPL - original licence link has changed is not relivant.
17294 * <script type="text/javascript">
17298 * @class Roo.BoxComponent
17299 * @extends Roo.Component
17300 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
17301 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
17302 * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17303 * layout containers.
17305 * @param {Roo.Element/String/Object} config The configuration options.
17307 Roo.BoxComponent = function(config){
17308 Roo.Component.call(this, config);
17312 * Fires after the component is resized.
17313 * @param {Roo.Component} this
17314 * @param {Number} adjWidth The box-adjusted width that was set
17315 * @param {Number} adjHeight The box-adjusted height that was set
17316 * @param {Number} rawWidth The width that was originally specified
17317 * @param {Number} rawHeight The height that was originally specified
17322 * Fires after the component is moved.
17323 * @param {Roo.Component} this
17324 * @param {Number} x The new x position
17325 * @param {Number} y The new y position
17331 Roo.extend(Roo.BoxComponent, Roo.Component, {
17332 // private, set in afterRender to signify that the component has been rendered
17334 // private, used to defer height settings to subclasses
17335 deferHeight: false,
17336 /** @cfg {Number} width
17337 * width (optional) size of component
17339 /** @cfg {Number} height
17340 * height (optional) size of component
17344 * Sets the width and height of the component. This method fires the resize event. This method can accept
17345 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17346 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17347 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17348 * @return {Roo.BoxComponent} this
17350 setSize : function(w, h){
17351 // support for standard size objects
17352 if(typeof w == 'object'){
17357 if(!this.boxReady){
17363 // prevent recalcs when not needed
17364 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17367 this.lastSize = {width: w, height: h};
17369 var adj = this.adjustSize(w, h);
17370 var aw = adj.width, ah = adj.height;
17371 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17372 var rz = this.getResizeEl();
17373 if(!this.deferHeight && aw !== undefined && ah !== undefined){
17374 rz.setSize(aw, ah);
17375 }else if(!this.deferHeight && ah !== undefined){
17377 }else if(aw !== undefined){
17380 this.onResize(aw, ah, w, h);
17381 this.fireEvent('resize', this, aw, ah, w, h);
17387 * Gets the current size of the component's underlying element.
17388 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17390 getSize : function(){
17391 return this.el.getSize();
17395 * Gets the current XY position of the component's underlying element.
17396 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17397 * @return {Array} The XY position of the element (e.g., [100, 200])
17399 getPosition : function(local){
17400 if(local === true){
17401 return [this.el.getLeft(true), this.el.getTop(true)];
17403 return this.xy || this.el.getXY();
17407 * Gets the current box measurements of the component's underlying element.
17408 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17409 * @returns {Object} box An object in the format {x, y, width, height}
17411 getBox : function(local){
17412 var s = this.el.getSize();
17414 s.x = this.el.getLeft(true);
17415 s.y = this.el.getTop(true);
17417 var xy = this.xy || this.el.getXY();
17425 * Sets the current box measurements of the component's underlying element.
17426 * @param {Object} box An object in the format {x, y, width, height}
17427 * @returns {Roo.BoxComponent} this
17429 updateBox : function(box){
17430 this.setSize(box.width, box.height);
17431 this.setPagePosition(box.x, box.y);
17436 getResizeEl : function(){
17437 return this.resizeEl || this.el;
17441 getPositionEl : function(){
17442 return this.positionEl || this.el;
17446 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
17447 * This method fires the move event.
17448 * @param {Number} left The new left
17449 * @param {Number} top The new top
17450 * @returns {Roo.BoxComponent} this
17452 setPosition : function(x, y){
17455 if(!this.boxReady){
17458 var adj = this.adjustPosition(x, y);
17459 var ax = adj.x, ay = adj.y;
17461 var el = this.getPositionEl();
17462 if(ax !== undefined || ay !== undefined){
17463 if(ax !== undefined && ay !== undefined){
17464 el.setLeftTop(ax, ay);
17465 }else if(ax !== undefined){
17467 }else if(ay !== undefined){
17470 this.onPosition(ax, ay);
17471 this.fireEvent('move', this, ax, ay);
17477 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
17478 * This method fires the move event.
17479 * @param {Number} x The new x position
17480 * @param {Number} y The new y position
17481 * @returns {Roo.BoxComponent} this
17483 setPagePosition : function(x, y){
17486 if(!this.boxReady){
17489 if(x === undefined || y === undefined){ // cannot translate undefined points
17492 var p = this.el.translatePoints(x, y);
17493 this.setPosition(p.left, p.top);
17498 onRender : function(ct, position){
17499 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17501 this.resizeEl = Roo.get(this.resizeEl);
17503 if(this.positionEl){
17504 this.positionEl = Roo.get(this.positionEl);
17509 afterRender : function(){
17510 Roo.BoxComponent.superclass.afterRender.call(this);
17511 this.boxReady = true;
17512 this.setSize(this.width, this.height);
17513 if(this.x || this.y){
17514 this.setPosition(this.x, this.y);
17516 if(this.pageX || this.pageY){
17517 this.setPagePosition(this.pageX, this.pageY);
17522 * Force the component's size to recalculate based on the underlying element's current height and width.
17523 * @returns {Roo.BoxComponent} this
17525 syncSize : function(){
17526 delete this.lastSize;
17527 this.setSize(this.el.getWidth(), this.el.getHeight());
17532 * Called after the component is resized, this method is empty by default but can be implemented by any
17533 * subclass that needs to perform custom logic after a resize occurs.
17534 * @param {Number} adjWidth The box-adjusted width that was set
17535 * @param {Number} adjHeight The box-adjusted height that was set
17536 * @param {Number} rawWidth The width that was originally specified
17537 * @param {Number} rawHeight The height that was originally specified
17539 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17544 * Called after the component is moved, this method is empty by default but can be implemented by any
17545 * subclass that needs to perform custom logic after a move occurs.
17546 * @param {Number} x The new x position
17547 * @param {Number} y The new y position
17549 onPosition : function(x, y){
17554 adjustSize : function(w, h){
17555 if(this.autoWidth){
17558 if(this.autoHeight){
17561 return {width : w, height: h};
17565 adjustPosition : function(x, y){
17566 return {x : x, y: y};
17570 * Ext JS Library 1.1.1
17571 * Copyright(c) 2006-2007, Ext JS, LLC.
17573 * Originally Released Under LGPL - original licence link has changed is not relivant.
17576 * <script type="text/javascript">
17581 * @extends Roo.Element
17582 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17583 * automatic maintaining of shadow/shim positions.
17584 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17585 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17586 * you can pass a string with a CSS class name. False turns off the shadow.
17587 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17588 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17589 * @cfg {String} cls CSS class to add to the element
17590 * @cfg {Number} zindex Starting z-index (defaults to 11000)
17591 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17593 * @param {Object} config An object with config options.
17594 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17597 Roo.Layer = function(config, existingEl){
17598 config = config || {};
17599 var dh = Roo.DomHelper;
17600 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17602 this.dom = Roo.getDom(existingEl);
17605 var o = config.dh || {tag: "div", cls: "x-layer"};
17606 this.dom = dh.append(pel, o);
17609 this.addClass(config.cls);
17611 this.constrain = config.constrain !== false;
17612 this.visibilityMode = Roo.Element.VISIBILITY;
17614 this.id = this.dom.id = config.id;
17616 this.id = Roo.id(this.dom);
17618 this.zindex = config.zindex || this.getZIndex();
17619 this.position("absolute", this.zindex);
17621 this.shadowOffset = config.shadowOffset || 4;
17622 this.shadow = new Roo.Shadow({
17623 offset : this.shadowOffset,
17624 mode : config.shadow
17627 this.shadowOffset = 0;
17629 this.useShim = config.shim !== false && Roo.useShims;
17630 this.useDisplay = config.useDisplay;
17634 var supr = Roo.Element.prototype;
17636 // shims are shared among layer to keep from having 100 iframes
17639 Roo.extend(Roo.Layer, Roo.Element, {
17641 getZIndex : function(){
17642 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17645 getShim : function(){
17652 var shim = shims.shift();
17654 shim = this.createShim();
17655 shim.enableDisplayMode('block');
17656 shim.dom.style.display = 'none';
17657 shim.dom.style.visibility = 'visible';
17659 var pn = this.dom.parentNode;
17660 if(shim.dom.parentNode != pn){
17661 pn.insertBefore(shim.dom, this.dom);
17663 shim.setStyle('z-index', this.getZIndex()-2);
17668 hideShim : function(){
17670 this.shim.setDisplayed(false);
17671 shims.push(this.shim);
17676 disableShadow : function(){
17678 this.shadowDisabled = true;
17679 this.shadow.hide();
17680 this.lastShadowOffset = this.shadowOffset;
17681 this.shadowOffset = 0;
17685 enableShadow : function(show){
17687 this.shadowDisabled = false;
17688 this.shadowOffset = this.lastShadowOffset;
17689 delete this.lastShadowOffset;
17697 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17698 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17699 sync : function(doShow){
17700 var sw = this.shadow;
17701 if(!this.updating && this.isVisible() && (sw || this.useShim)){
17702 var sh = this.getShim();
17704 var w = this.getWidth(),
17705 h = this.getHeight();
17707 var l = this.getLeft(true),
17708 t = this.getTop(true);
17710 if(sw && !this.shadowDisabled){
17711 if(doShow && !sw.isVisible()){
17714 sw.realign(l, t, w, h);
17720 // fit the shim behind the shadow, so it is shimmed too
17721 var a = sw.adjusts, s = sh.dom.style;
17722 s.left = (Math.min(l, l+a.l))+"px";
17723 s.top = (Math.min(t, t+a.t))+"px";
17724 s.width = (w+a.w)+"px";
17725 s.height = (h+a.h)+"px";
17732 sh.setLeftTop(l, t);
17739 destroy : function(){
17742 this.shadow.hide();
17744 this.removeAllListeners();
17745 var pn = this.dom.parentNode;
17747 pn.removeChild(this.dom);
17749 Roo.Element.uncache(this.id);
17752 remove : function(){
17757 beginUpdate : function(){
17758 this.updating = true;
17762 endUpdate : function(){
17763 this.updating = false;
17768 hideUnders : function(negOffset){
17770 this.shadow.hide();
17776 constrainXY : function(){
17777 if(this.constrain){
17778 var vw = Roo.lib.Dom.getViewWidth(),
17779 vh = Roo.lib.Dom.getViewHeight();
17780 var s = Roo.get(document).getScroll();
17782 var xy = this.getXY();
17783 var x = xy[0], y = xy[1];
17784 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17785 // only move it if it needs it
17787 // first validate right/bottom
17788 if((x + w) > vw+s.left){
17789 x = vw - w - this.shadowOffset;
17792 if((y + h) > vh+s.top){
17793 y = vh - h - this.shadowOffset;
17796 // then make sure top/left isn't negative
17807 var ay = this.avoidY;
17808 if(y <= ay && (y+h) >= ay){
17814 supr.setXY.call(this, xy);
17820 isVisible : function(){
17821 return this.visible;
17825 showAction : function(){
17826 this.visible = true; // track visibility to prevent getStyle calls
17827 if(this.useDisplay === true){
17828 this.setDisplayed("");
17829 }else if(this.lastXY){
17830 supr.setXY.call(this, this.lastXY);
17831 }else if(this.lastLT){
17832 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17837 hideAction : function(){
17838 this.visible = false;
17839 if(this.useDisplay === true){
17840 this.setDisplayed(false);
17842 this.setLeftTop(-10000,-10000);
17846 // overridden Element method
17847 setVisible : function(v, a, d, c, e){
17852 var cb = function(){
17857 }.createDelegate(this);
17858 supr.setVisible.call(this, true, true, d, cb, e);
17861 this.hideUnders(true);
17870 }.createDelegate(this);
17872 supr.setVisible.call(this, v, a, d, cb, e);
17881 storeXY : function(xy){
17882 delete this.lastLT;
17886 storeLeftTop : function(left, top){
17887 delete this.lastXY;
17888 this.lastLT = [left, top];
17892 beforeFx : function(){
17893 this.beforeAction();
17894 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17898 afterFx : function(){
17899 Roo.Layer.superclass.afterFx.apply(this, arguments);
17900 this.sync(this.isVisible());
17904 beforeAction : function(){
17905 if(!this.updating && this.shadow){
17906 this.shadow.hide();
17910 // overridden Element method
17911 setLeft : function(left){
17912 this.storeLeftTop(left, this.getTop(true));
17913 supr.setLeft.apply(this, arguments);
17917 setTop : function(top){
17918 this.storeLeftTop(this.getLeft(true), top);
17919 supr.setTop.apply(this, arguments);
17923 setLeftTop : function(left, top){
17924 this.storeLeftTop(left, top);
17925 supr.setLeftTop.apply(this, arguments);
17929 setXY : function(xy, a, d, c, e){
17931 this.beforeAction();
17933 var cb = this.createCB(c);
17934 supr.setXY.call(this, xy, a, d, cb, e);
17941 createCB : function(c){
17952 // overridden Element method
17953 setX : function(x, a, d, c, e){
17954 this.setXY([x, this.getY()], a, d, c, e);
17957 // overridden Element method
17958 setY : function(y, a, d, c, e){
17959 this.setXY([this.getX(), y], a, d, c, e);
17962 // overridden Element method
17963 setSize : function(w, h, a, d, c, e){
17964 this.beforeAction();
17965 var cb = this.createCB(c);
17966 supr.setSize.call(this, w, h, a, d, cb, e);
17972 // overridden Element method
17973 setWidth : function(w, a, d, c, e){
17974 this.beforeAction();
17975 var cb = this.createCB(c);
17976 supr.setWidth.call(this, w, a, d, cb, e);
17982 // overridden Element method
17983 setHeight : function(h, a, d, c, e){
17984 this.beforeAction();
17985 var cb = this.createCB(c);
17986 supr.setHeight.call(this, h, a, d, cb, e);
17992 // overridden Element method
17993 setBounds : function(x, y, w, h, a, d, c, e){
17994 this.beforeAction();
17995 var cb = this.createCB(c);
17997 this.storeXY([x, y]);
17998 supr.setXY.call(this, [x, y]);
17999 supr.setSize.call(this, w, h, a, d, cb, e);
18002 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
18008 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18009 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18010 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18011 * @param {Number} zindex The new z-index to set
18012 * @return {this} The Layer
18014 setZIndex : function(zindex){
18015 this.zindex = zindex;
18016 this.setStyle("z-index", zindex + 2);
18018 this.shadow.setZIndex(zindex + 1);
18021 this.shim.setStyle("z-index", zindex);
18026 * Original code for Roojs - LGPL
18027 * <script type="text/javascript">
18031 * @class Roo.XComponent
18032 * A delayed Element creator...
18033 * Or a way to group chunks of interface together.
18034 * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18035 * used in conjunction with XComponent.build() it will create an instance of each element,
18036 * then call addxtype() to build the User interface.
18038 * Mypart.xyx = new Roo.XComponent({
18040 parent : 'Mypart.xyz', // empty == document.element.!!
18044 disabled : function() {}
18046 tree : function() { // return an tree of xtype declared components
18050 xtype : 'NestedLayoutPanel',
18057 * It can be used to build a big heiracy, with parent etc.
18058 * or you can just use this to render a single compoent to a dom element
18059 * MYPART.render(Roo.Element | String(id) | dom_element )
18066 * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18067 * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18069 * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18071 * When the top level is false, a 'Roo.layout.Border' is created and the element is flagged as 'topModule'
18072 * - if mulitple topModules exist, the last one is defined as the top module.
18076 * When the top level or multiple modules are to embedded into a existing HTML page,
18077 * the parent element can container '#id' of the element where the module will be drawn.
18081 * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18082 * it relies more on a include mechanism, where sub modules are included into an outer page.
18083 * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18085 * Bootstrap Roo Included elements
18087 * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18088 * hence confusing the component builder as it thinks there are multiple top level elements.
18090 * String Over-ride & Translations
18092 * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18093 * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18094 * are needed. @see Roo.XComponent.overlayString
18098 * @extends Roo.util.Observable
18100 * @param cfg {Object} configuration of component
18103 Roo.XComponent = function(cfg) {
18104 Roo.apply(this, cfg);
18108 * Fires when this the componnt is built
18109 * @param {Roo.XComponent} c the component
18114 this.region = this.region || 'center'; // default..
18115 Roo.XComponent.register(this);
18116 this.modules = false;
18117 this.el = false; // where the layout goes..
18121 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18124 * The created element (with Roo.factory())
18125 * @type {Roo.Layout}
18131 * for BC - use el in new code
18132 * @type {Roo.Layout}
18138 * for BC - use el in new code
18139 * @type {Roo.Layout}
18144 * @cfg {Function|boolean} disabled
18145 * If this module is disabled by some rule, return true from the funtion
18150 * @cfg {String} parent
18151 * Name of parent element which it get xtype added to..
18156 * @cfg {String} order
18157 * Used to set the order in which elements are created (usefull for multiple tabs)
18162 * @cfg {String} name
18163 * String to display while loading.
18167 * @cfg {String} region
18168 * Region to render component to (defaults to center)
18173 * @cfg {Array} items
18174 * A single item array - the first element is the root of the tree..
18175 * It's done this way to stay compatible with the Xtype system...
18181 * The method that retuns the tree of parts that make up this compoennt
18188 * render element to dom or tree
18189 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18192 render : function(el)
18196 var hp = this.parent ? 1 : 0;
18197 Roo.debug && Roo.log(this);
18199 var tree = this._tree ? this._tree() : this.tree();
18202 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18203 // if parent is a '#.....' string, then let's use that..
18204 var ename = this.parent.substr(1);
18205 this.parent = false;
18206 Roo.debug && Roo.log(ename);
18208 case 'bootstrap-body':
18209 if (typeof(tree.el) != 'undefined' && tree.el == document.body) {
18210 // this is the BorderLayout standard?
18211 this.parent = { el : true };
18214 if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype) > -1) {
18215 // need to insert stuff...
18217 el : new Roo.bootstrap.layout.Border({
18218 el : document.body,
18224 tabPosition: 'top',
18225 //resizeTabs: true,
18226 alwaysShowTabs: true,
18236 if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18237 this.parent = { el : new Roo.bootstrap.Body() };
18238 Roo.debug && Roo.log("setting el to doc body");
18241 throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18245 this.parent = { el : true};
18248 el = Roo.get(ename);
18249 if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18250 this.parent = { el : true};
18257 if (!el && !this.parent) {
18258 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18263 Roo.debug && Roo.log("EL:");
18264 Roo.debug && Roo.log(el);
18265 Roo.debug && Roo.log("this.parent.el:");
18266 Roo.debug && Roo.log(this.parent.el);
18269 // altertive root elements ??? - we need a better way to indicate these.
18270 var is_alt = Roo.XComponent.is_alt ||
18271 (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18272 (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18273 (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18277 if (!this.parent && is_alt) {
18278 //el = Roo.get(document.body);
18279 this.parent = { el : true };
18284 if (!this.parent) {
18286 Roo.debug && Roo.log("no parent - creating one");
18288 el = el ? Roo.get(el) : false;
18290 if (typeof(Roo.layout.Border) == 'undefined' ) {
18293 el : new Roo.bootstrap.layout.Border({
18294 el: el || document.body,
18300 tabPosition: 'top',
18301 //resizeTabs: true,
18302 alwaysShowTabs: false,
18305 overflow: 'visible'
18311 // it's a top level one..
18313 el : new Roo.layout.Border(el || document.body, {
18318 tabPosition: 'top',
18319 //resizeTabs: true,
18320 alwaysShowTabs: el && hp? false : true,
18321 hideTabs: el || !hp ? true : false,
18329 if (!this.parent.el) {
18330 // probably an old style ctor, which has been disabled.
18334 // The 'tree' method is '_tree now'
18336 tree.region = tree.region || this.region;
18337 var is_body = false;
18338 if (this.parent.el === true) {
18339 // bootstrap... - body..
18343 this.parent.el = Roo.factory(tree);
18347 this.el = this.parent.el.addxtype(tree, undefined, is_body);
18348 this.fireEvent('built', this);
18350 this.panel = this.el;
18351 this.layout = this.panel.layout;
18352 this.parentLayout = this.parent.layout || false;
18358 Roo.apply(Roo.XComponent, {
18360 * @property hideProgress
18361 * true to disable the building progress bar.. usefull on single page renders.
18364 hideProgress : false,
18366 * @property buildCompleted
18367 * True when the builder has completed building the interface.
18370 buildCompleted : false,
18373 * @property topModule
18374 * the upper most module - uses document.element as it's constructor.
18381 * @property modules
18382 * array of modules to be created by registration system.
18383 * @type {Array} of Roo.XComponent
18388 * @property elmodules
18389 * array of modules to be created by which use #ID
18390 * @type {Array} of Roo.XComponent
18397 * Is an alternative Root - normally used by bootstrap or other systems,
18398 * where the top element in the tree can wrap 'body'
18399 * @type {boolean} (default false)
18404 * @property build_from_html
18405 * Build elements from html - used by bootstrap HTML stuff
18406 * - this is cleared after build is completed
18407 * @type {boolean} (default false)
18410 build_from_html : false,
18412 * Register components to be built later.
18414 * This solves the following issues
18415 * - Building is not done on page load, but after an authentication process has occured.
18416 * - Interface elements are registered on page load
18417 * - Parent Interface elements may not be loaded before child, so this handles that..
18424 module : 'Pman.Tab.projectMgr',
18426 parent : 'Pman.layout',
18427 disabled : false, // or use a function..
18430 * * @param {Object} details about module
18432 register : function(obj) {
18434 Roo.XComponent.event.fireEvent('register', obj);
18435 switch(typeof(obj.disabled) ) {
18441 if ( obj.disabled() ) {
18447 if (obj.disabled || obj.region == '#disabled') {
18453 this.modules.push(obj);
18457 * convert a string to an object..
18458 * eg. 'AAA.BBB' -> finds AAA.BBB
18462 toObject : function(str)
18464 if (!str || typeof(str) == 'object') {
18467 if (str.substring(0,1) == '#') {
18471 var ar = str.split('.');
18476 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18478 throw "Module not found : " + str;
18482 throw "Module not found : " + str;
18484 Roo.each(ar, function(e) {
18485 if (typeof(o[e]) == 'undefined') {
18486 throw "Module not found : " + str;
18497 * move modules into their correct place in the tree..
18500 preBuild : function ()
18503 Roo.each(this.modules , function (obj)
18505 Roo.XComponent.event.fireEvent('beforebuild', obj);
18507 var opar = obj.parent;
18509 obj.parent = this.toObject(opar);
18511 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18516 Roo.debug && Roo.log("GOT top level module");
18517 Roo.debug && Roo.log(obj);
18518 obj.modules = new Roo.util.MixedCollection(false,
18519 function(o) { return o.order + '' }
18521 this.topModule = obj;
18524 // parent is a string (usually a dom element name..)
18525 if (typeof(obj.parent) == 'string') {
18526 this.elmodules.push(obj);
18529 if (obj.parent.constructor != Roo.XComponent) {
18530 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18532 if (!obj.parent.modules) {
18533 obj.parent.modules = new Roo.util.MixedCollection(false,
18534 function(o) { return o.order + '' }
18537 if (obj.parent.disabled) {
18538 obj.disabled = true;
18540 obj.parent.modules.add(obj);
18545 * make a list of modules to build.
18546 * @return {Array} list of modules.
18549 buildOrder : function()
18552 var cmp = function(a,b) {
18553 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18555 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18556 throw "No top level modules to build";
18559 // make a flat list in order of modules to build.
18560 var mods = this.topModule ? [ this.topModule ] : [];
18563 // elmodules (is a list of DOM based modules )
18564 Roo.each(this.elmodules, function(e) {
18566 if (!this.topModule &&
18567 typeof(e.parent) == 'string' &&
18568 e.parent.substring(0,1) == '#' &&
18569 Roo.get(e.parent.substr(1))
18572 _this.topModule = e;
18578 // add modules to their parents..
18579 var addMod = function(m) {
18580 Roo.debug && Roo.log("build Order: add: " + m.name);
18583 if (m.modules && !m.disabled) {
18584 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18585 m.modules.keySort('ASC', cmp );
18586 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18588 m.modules.each(addMod);
18590 Roo.debug && Roo.log("build Order: no child modules");
18592 // not sure if this is used any more..
18594 m.finalize.name = m.name + " (clean up) ";
18595 mods.push(m.finalize);
18599 if (this.topModule && this.topModule.modules) {
18600 this.topModule.modules.keySort('ASC', cmp );
18601 this.topModule.modules.each(addMod);
18607 * Build the registered modules.
18608 * @param {Object} parent element.
18609 * @param {Function} optional method to call after module has been added.
18613 build : function(opts)
18616 if (typeof(opts) != 'undefined') {
18617 Roo.apply(this,opts);
18621 var mods = this.buildOrder();
18623 //this.allmods = mods;
18624 //Roo.debug && Roo.log(mods);
18626 if (!mods.length) { // should not happen
18627 throw "NO modules!!!";
18631 var msg = "Building Interface...";
18632 // flash it up as modal - so we store the mask!?
18633 if (!this.hideProgress && Roo.MessageBox) {
18634 Roo.MessageBox.show({ title: 'loading' });
18635 Roo.MessageBox.show({
18636 title: "Please wait...",
18646 var total = mods.length;
18649 var progressRun = function() {
18650 if (!mods.length) {
18651 Roo.debug && Roo.log('hide?');
18652 if (!this.hideProgress && Roo.MessageBox) {
18653 Roo.MessageBox.hide();
18655 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18657 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18663 var m = mods.shift();
18666 Roo.debug && Roo.log(m);
18667 // not sure if this is supported any more.. - modules that are are just function
18668 if (typeof(m) == 'function') {
18670 return progressRun.defer(10, _this);
18674 msg = "Building Interface " + (total - mods.length) +
18676 (m.name ? (' - ' + m.name) : '');
18677 Roo.debug && Roo.log(msg);
18678 if (!_this.hideProgress && Roo.MessageBox) {
18679 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
18683 // is the module disabled?
18684 var disabled = (typeof(m.disabled) == 'function') ?
18685 m.disabled.call(m.module.disabled) : m.disabled;
18689 return progressRun(); // we do not update the display!
18697 // it's 10 on top level, and 1 on others??? why...
18698 return progressRun.defer(10, _this);
18701 progressRun.defer(1, _this);
18707 * Overlay a set of modified strings onto a component
18708 * This is dependant on our builder exporting the strings and 'named strings' elements.
18710 * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18711 * @param {Object} associative array of 'named' string and it's new value.
18714 overlayStrings : function( component, strings )
18716 if (typeof(component['_named_strings']) == 'undefined') {
18717 throw "ERROR: component does not have _named_strings";
18719 for ( var k in strings ) {
18720 var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18721 if (md !== false) {
18722 component['_strings'][md] = strings[k];
18724 Roo.log('could not find named string: ' + k + ' in');
18725 Roo.log(component);
18740 * wrapper for event.on - aliased later..
18741 * Typically use to register a event handler for register:
18743 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18752 Roo.XComponent.event = new Roo.util.Observable({
18756 * Fires when an Component is registered,
18757 * set the disable property on the Component to stop registration.
18758 * @param {Roo.XComponent} c the component being registerd.
18763 * @event beforebuild
18764 * Fires before each Component is built
18765 * can be used to apply permissions.
18766 * @param {Roo.XComponent} c the component being registerd.
18769 'beforebuild' : true,
18771 * @event buildcomplete
18772 * Fires on the top level element when all elements have been built
18773 * @param {Roo.XComponent} the top level component.
18775 'buildcomplete' : true
18780 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
18783 * marked - a markdown parser
18784 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18785 * https://github.com/chjj/marked
18791 * Roo.Markdown - is a very crude wrapper around marked..
18795 * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18797 * Note: move the sample code to the bottom of this
18798 * file before uncommenting it.
18803 Roo.Markdown.toHtml = function(text) {
18805 var c = new Roo.Markdown.marked.setOptions({
18806 renderer: new Roo.Markdown.marked.Renderer(),
18817 text = text.replace(/\\\n/g,' ');
18818 return Roo.Markdown.marked(text);
18823 // Wraps all "globals" so that the only thing
18824 // exposed is makeHtml().
18830 * eval:var:unescape
18838 var escape = function (html, encode) {
18840 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18841 .replace(/</g, '<')
18842 .replace(/>/g, '>')
18843 .replace(/"/g, '"')
18844 .replace(/'/g, ''');
18847 var unescape = function (html) {
18848 // explicitly match decimal, hex, and named HTML entities
18849 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18850 n = n.toLowerCase();
18851 if (n === 'colon') { return ':'; }
18852 if (n.charAt(0) === '#') {
18853 return n.charAt(1) === 'x'
18854 ? String.fromCharCode(parseInt(n.substring(2), 16))
18855 : String.fromCharCode(+n.substring(1));
18861 var replace = function (regex, opt) {
18862 regex = regex.source;
18864 return function self(name, val) {
18865 if (!name) { return new RegExp(regex, opt); }
18866 val = val.source || val;
18867 val = val.replace(/(^|[^\[])\^/g, '$1');
18868 regex = regex.replace(name, val);
18877 var noop = function () {}
18883 var merge = function (obj) {
18888 for (; i < arguments.length; i++) {
18889 target = arguments[i];
18890 for (key in target) {
18891 if (Object.prototype.hasOwnProperty.call(target, key)) {
18892 obj[key] = target[key];
18902 * Block-Level Grammar
18910 code: /^( {4}[^\n]+\n*)+/,
18912 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18913 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18915 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18916 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18917 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18918 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18919 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18921 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18925 block.bullet = /(?:[*+-]|\d+\.)/;
18926 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18927 block.item = replace(block.item, 'gm')
18928 (/bull/g, block.bullet)
18931 block.list = replace(block.list)
18932 (/bull/g, block.bullet)
18933 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18934 ('def', '\\n+(?=' + block.def.source + ')')
18937 block.blockquote = replace(block.blockquote)
18941 block._tag = '(?!(?:'
18942 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18943 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18944 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18946 block.html = replace(block.html)
18947 ('comment', /<!--[\s\S]*?-->/)
18948 ('closed', /<(tag)[\s\S]+?<\/\1>/)
18949 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18950 (/tag/g, block._tag)
18953 block.paragraph = replace(block.paragraph)
18955 ('heading', block.heading)
18956 ('lheading', block.lheading)
18957 ('blockquote', block.blockquote)
18958 ('tag', '<' + block._tag)
18963 * Normal Block Grammar
18966 block.normal = merge({}, block);
18969 * GFM Block Grammar
18972 block.gfm = merge({}, block.normal, {
18973 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18975 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18978 block.gfm.paragraph = replace(block.paragraph)
18980 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18981 + block.list.source.replace('\\1', '\\3') + '|')
18985 * GFM + Tables Block Grammar
18988 block.tables = merge({}, block.gfm, {
18989 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18990 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18997 var Lexer = function (options) {
18999 this.tokens.links = {};
19000 this.options = options || marked.defaults;
19001 this.rules = block.normal;
19003 if (this.options.gfm) {
19004 if (this.options.tables) {
19005 this.rules = block.tables;
19007 this.rules = block.gfm;
19013 * Expose Block Rules
19016 Lexer.rules = block;
19019 * Static Lex Method
19022 Lexer.lex = function(src, options) {
19023 var lexer = new Lexer(options);
19024 return lexer.lex(src);
19031 Lexer.prototype.lex = function(src) {
19033 .replace(/\r\n|\r/g, '\n')
19034 .replace(/\t/g, ' ')
19035 .replace(/\u00a0/g, ' ')
19036 .replace(/\u2424/g, '\n');
19038 return this.token(src, true);
19045 Lexer.prototype.token = function(src, top, bq) {
19046 var src = src.replace(/^ +$/gm, '')
19059 if (cap = this.rules.newline.exec(src)) {
19060 src = src.substring(cap[0].length);
19061 if (cap[0].length > 1) {
19069 if (cap = this.rules.code.exec(src)) {
19070 src = src.substring(cap[0].length);
19071 cap = cap[0].replace(/^ {4}/gm, '');
19074 text: !this.options.pedantic
19075 ? cap.replace(/\n+$/, '')
19082 if (cap = this.rules.fences.exec(src)) {
19083 src = src.substring(cap[0].length);
19093 if (cap = this.rules.heading.exec(src)) {
19094 src = src.substring(cap[0].length);
19097 depth: cap[1].length,
19103 // table no leading pipe (gfm)
19104 if (top && (cap = this.rules.nptable.exec(src))) {
19105 src = src.substring(cap[0].length);
19109 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19110 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19111 cells: cap[3].replace(/\n$/, '').split('\n')
19114 for (i = 0; i < item.align.length; i++) {
19115 if (/^ *-+: *$/.test(item.align[i])) {
19116 item.align[i] = 'right';
19117 } else if (/^ *:-+: *$/.test(item.align[i])) {
19118 item.align[i] = 'center';
19119 } else if (/^ *:-+ *$/.test(item.align[i])) {
19120 item.align[i] = 'left';
19122 item.align[i] = null;
19126 for (i = 0; i < item.cells.length; i++) {
19127 item.cells[i] = item.cells[i].split(/ *\| */);
19130 this.tokens.push(item);
19136 if (cap = this.rules.lheading.exec(src)) {
19137 src = src.substring(cap[0].length);
19140 depth: cap[2] === '=' ? 1 : 2,
19147 if (cap = this.rules.hr.exec(src)) {
19148 src = src.substring(cap[0].length);
19156 if (cap = this.rules.blockquote.exec(src)) {
19157 src = src.substring(cap[0].length);
19160 type: 'blockquote_start'
19163 cap = cap[0].replace(/^ *> ?/gm, '');
19165 // Pass `top` to keep the current
19166 // "toplevel" state. This is exactly
19167 // how markdown.pl works.
19168 this.token(cap, top, true);
19171 type: 'blockquote_end'
19178 if (cap = this.rules.list.exec(src)) {
19179 src = src.substring(cap[0].length);
19183 type: 'list_start',
19184 ordered: bull.length > 1
19187 // Get each top-level item.
19188 cap = cap[0].match(this.rules.item);
19194 for (; i < l; i++) {
19197 // Remove the list item's bullet
19198 // so it is seen as the next token.
19199 space = item.length;
19200 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19202 // Outdent whatever the
19203 // list item contains. Hacky.
19204 if (~item.indexOf('\n ')) {
19205 space -= item.length;
19206 item = !this.options.pedantic
19207 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19208 : item.replace(/^ {1,4}/gm, '');
19211 // Determine whether the next list item belongs here.
19212 // Backpedal if it does not belong in this list.
19213 if (this.options.smartLists && i !== l - 1) {
19214 b = block.bullet.exec(cap[i + 1])[0];
19215 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19216 src = cap.slice(i + 1).join('\n') + src;
19221 // Determine whether item is loose or not.
19222 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19223 // for discount behavior.
19224 loose = next || /\n\n(?!\s*$)/.test(item);
19226 next = item.charAt(item.length - 1) === '\n';
19227 if (!loose) { loose = next; }
19232 ? 'loose_item_start'
19233 : 'list_item_start'
19237 this.token(item, false, bq);
19240 type: 'list_item_end'
19252 if (cap = this.rules.html.exec(src)) {
19253 src = src.substring(cap[0].length);
19255 type: this.options.sanitize
19258 pre: !this.options.sanitizer
19259 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19266 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19267 src = src.substring(cap[0].length);
19268 this.tokens.links[cap[1].toLowerCase()] = {
19276 if (top && (cap = this.rules.table.exec(src))) {
19277 src = src.substring(cap[0].length);
19281 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19282 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19283 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19286 for (i = 0; i < item.align.length; i++) {
19287 if (/^ *-+: *$/.test(item.align[i])) {
19288 item.align[i] = 'right';
19289 } else if (/^ *:-+: *$/.test(item.align[i])) {
19290 item.align[i] = 'center';
19291 } else if (/^ *:-+ *$/.test(item.align[i])) {
19292 item.align[i] = 'left';
19294 item.align[i] = null;
19298 for (i = 0; i < item.cells.length; i++) {
19299 item.cells[i] = item.cells[i]
19300 .replace(/^ *\| *| *\| *$/g, '')
19304 this.tokens.push(item);
19309 // top-level paragraph
19310 if (top && (cap = this.rules.paragraph.exec(src))) {
19311 src = src.substring(cap[0].length);
19314 text: cap[1].charAt(cap[1].length - 1) === '\n'
19315 ? cap[1].slice(0, -1)
19322 if (cap = this.rules.text.exec(src)) {
19323 // Top-level should never reach here.
19324 src = src.substring(cap[0].length);
19334 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19338 return this.tokens;
19342 * Inline-Level Grammar
19346 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19347 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19349 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19350 link: /^!?\[(inside)\]\(href\)/,
19351 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19352 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19353 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19354 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19355 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19356 br: /^ {2,}\n(?!\s*$)/,
19358 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19361 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19362 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19364 inline.link = replace(inline.link)
19365 ('inside', inline._inside)
19366 ('href', inline._href)
19369 inline.reflink = replace(inline.reflink)
19370 ('inside', inline._inside)
19374 * Normal Inline Grammar
19377 inline.normal = merge({}, inline);
19380 * Pedantic Inline Grammar
19383 inline.pedantic = merge({}, inline.normal, {
19384 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19385 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19389 * GFM Inline Grammar
19392 inline.gfm = merge({}, inline.normal, {
19393 escape: replace(inline.escape)('])', '~|])')(),
19394 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19395 del: /^~~(?=\S)([\s\S]*?\S)~~/,
19396 text: replace(inline.text)
19398 ('|', '|https?://|')
19403 * GFM + Line Breaks Inline Grammar
19406 inline.breaks = merge({}, inline.gfm, {
19407 br: replace(inline.br)('{2,}', '*')(),
19408 text: replace(inline.gfm.text)('{2,}', '*')()
19412 * Inline Lexer & Compiler
19415 var InlineLexer = function (links, options) {
19416 this.options = options || marked.defaults;
19417 this.links = links;
19418 this.rules = inline.normal;
19419 this.renderer = this.options.renderer || new Renderer;
19420 this.renderer.options = this.options;
19424 Error('Tokens array requires a `links` property.');
19427 if (this.options.gfm) {
19428 if (this.options.breaks) {
19429 this.rules = inline.breaks;
19431 this.rules = inline.gfm;
19433 } else if (this.options.pedantic) {
19434 this.rules = inline.pedantic;
19439 * Expose Inline Rules
19442 InlineLexer.rules = inline;
19445 * Static Lexing/Compiling Method
19448 InlineLexer.output = function(src, links, options) {
19449 var inline = new InlineLexer(links, options);
19450 return inline.output(src);
19457 InlineLexer.prototype.output = function(src) {
19466 if (cap = this.rules.escape.exec(src)) {
19467 src = src.substring(cap[0].length);
19473 if (cap = this.rules.autolink.exec(src)) {
19474 src = src.substring(cap[0].length);
19475 if (cap[2] === '@') {
19476 text = cap[1].charAt(6) === ':'
19477 ? this.mangle(cap[1].substring(7))
19478 : this.mangle(cap[1]);
19479 href = this.mangle('mailto:') + text;
19481 text = escape(cap[1]);
19484 out += this.renderer.link(href, null, text);
19489 if (!this.inLink && (cap = this.rules.url.exec(src))) {
19490 src = src.substring(cap[0].length);
19491 text = escape(cap[1]);
19493 out += this.renderer.link(href, null, text);
19498 if (cap = this.rules.tag.exec(src)) {
19499 if (!this.inLink && /^<a /i.test(cap[0])) {
19500 this.inLink = true;
19501 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19502 this.inLink = false;
19504 src = src.substring(cap[0].length);
19505 out += this.options.sanitize
19506 ? this.options.sanitizer
19507 ? this.options.sanitizer(cap[0])
19514 if (cap = this.rules.link.exec(src)) {
19515 src = src.substring(cap[0].length);
19516 this.inLink = true;
19517 out += this.outputLink(cap, {
19521 this.inLink = false;
19526 if ((cap = this.rules.reflink.exec(src))
19527 || (cap = this.rules.nolink.exec(src))) {
19528 src = src.substring(cap[0].length);
19529 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19530 link = this.links[link.toLowerCase()];
19531 if (!link || !link.href) {
19532 out += cap[0].charAt(0);
19533 src = cap[0].substring(1) + src;
19536 this.inLink = true;
19537 out += this.outputLink(cap, link);
19538 this.inLink = false;
19543 if (cap = this.rules.strong.exec(src)) {
19544 src = src.substring(cap[0].length);
19545 out += this.renderer.strong(this.output(cap[2] || cap[1]));
19550 if (cap = this.rules.em.exec(src)) {
19551 src = src.substring(cap[0].length);
19552 out += this.renderer.em(this.output(cap[2] || cap[1]));
19557 if (cap = this.rules.code.exec(src)) {
19558 src = src.substring(cap[0].length);
19559 out += this.renderer.codespan(escape(cap[2], true));
19564 if (cap = this.rules.br.exec(src)) {
19565 src = src.substring(cap[0].length);
19566 out += this.renderer.br();
19571 if (cap = this.rules.del.exec(src)) {
19572 src = src.substring(cap[0].length);
19573 out += this.renderer.del(this.output(cap[1]));
19578 if (cap = this.rules.text.exec(src)) {
19579 src = src.substring(cap[0].length);
19580 out += this.renderer.text(escape(this.smartypants(cap[0])));
19586 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19597 InlineLexer.prototype.outputLink = function(cap, link) {
19598 var href = escape(link.href)
19599 , title = link.title ? escape(link.title) : null;
19601 return cap[0].charAt(0) !== '!'
19602 ? this.renderer.link(href, title, this.output(cap[1]))
19603 : this.renderer.image(href, title, escape(cap[1]));
19607 * Smartypants Transformations
19610 InlineLexer.prototype.smartypants = function(text) {
19611 if (!this.options.smartypants) { return text; }
19614 .replace(/---/g, '\u2014')
19616 .replace(/--/g, '\u2013')
19618 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19619 // closing singles & apostrophes
19620 .replace(/'/g, '\u2019')
19622 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19624 .replace(/"/g, '\u201d')
19626 .replace(/\.{3}/g, '\u2026');
19633 InlineLexer.prototype.mangle = function(text) {
19634 if (!this.options.mangle) { return text; }
19640 for (; i < l; i++) {
19641 ch = text.charCodeAt(i);
19642 if (Math.random() > 0.5) {
19643 ch = 'x' + ch.toString(16);
19645 out += '&#' + ch + ';';
19656 * eval:var:Renderer
19659 var Renderer = function (options) {
19660 this.options = options || {};
19663 Renderer.prototype.code = function(code, lang, escaped) {
19664 if (this.options.highlight) {
19665 var out = this.options.highlight(code, lang);
19666 if (out != null && out !== code) {
19671 // hack!!! - it's already escapeD?
19676 return '<pre><code>'
19677 + (escaped ? code : escape(code, true))
19678 + '\n</code></pre>';
19681 return '<pre><code class="'
19682 + this.options.langPrefix
19683 + escape(lang, true)
19685 + (escaped ? code : escape(code, true))
19686 + '\n</code></pre>\n';
19689 Renderer.prototype.blockquote = function(quote) {
19690 return '<blockquote>\n' + quote + '</blockquote>\n';
19693 Renderer.prototype.html = function(html) {
19697 Renderer.prototype.heading = function(text, level, raw) {
19701 + this.options.headerPrefix
19702 + raw.toLowerCase().replace(/[^\w]+/g, '-')
19710 Renderer.prototype.hr = function() {
19711 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19714 Renderer.prototype.list = function(body, ordered) {
19715 var type = ordered ? 'ol' : 'ul';
19716 return '<' + type + '>\n' + body + '</' + type + '>\n';
19719 Renderer.prototype.listitem = function(text) {
19720 return '<li>' + text + '</li>\n';
19723 Renderer.prototype.paragraph = function(text) {
19724 return '<p>' + text + '</p>\n';
19727 Renderer.prototype.table = function(header, body) {
19728 return '<table class="table table-striped">\n'
19738 Renderer.prototype.tablerow = function(content) {
19739 return '<tr>\n' + content + '</tr>\n';
19742 Renderer.prototype.tablecell = function(content, flags) {
19743 var type = flags.header ? 'th' : 'td';
19744 var tag = flags.align
19745 ? '<' + type + ' style="text-align:' + flags.align + '">'
19746 : '<' + type + '>';
19747 return tag + content + '</' + type + '>\n';
19750 // span level renderer
19751 Renderer.prototype.strong = function(text) {
19752 return '<strong>' + text + '</strong>';
19755 Renderer.prototype.em = function(text) {
19756 return '<em>' + text + '</em>';
19759 Renderer.prototype.codespan = function(text) {
19760 return '<code>' + text + '</code>';
19763 Renderer.prototype.br = function() {
19764 return this.options.xhtml ? '<br/>' : '<br>';
19767 Renderer.prototype.del = function(text) {
19768 return '<del>' + text + '</del>';
19771 Renderer.prototype.link = function(href, title, text) {
19772 if (this.options.sanitize) {
19774 var prot = decodeURIComponent(unescape(href))
19775 .replace(/[^\w:]/g, '')
19780 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19784 var out = '<a href="' + href + '"';
19786 out += ' title="' + title + '"';
19788 out += '>' + text + '</a>';
19792 Renderer.prototype.image = function(href, title, text) {
19793 var out = '<img src="' + href + '" alt="' + text + '"';
19795 out += ' title="' + title + '"';
19797 out += this.options.xhtml ? '/>' : '>';
19801 Renderer.prototype.text = function(text) {
19806 * Parsing & Compiling
19812 var Parser= function (options) {
19815 this.options = options || marked.defaults;
19816 this.options.renderer = this.options.renderer || new Renderer;
19817 this.renderer = this.options.renderer;
19818 this.renderer.options = this.options;
19822 * Static Parse Method
19825 Parser.parse = function(src, options, renderer) {
19826 var parser = new Parser(options, renderer);
19827 return parser.parse(src);
19834 Parser.prototype.parse = function(src) {
19835 this.inline = new InlineLexer(src.links, this.options, this.renderer);
19836 this.tokens = src.reverse();
19839 while (this.next()) {
19850 Parser.prototype.next = function() {
19851 return this.token = this.tokens.pop();
19855 * Preview Next Token
19858 Parser.prototype.peek = function() {
19859 return this.tokens[this.tokens.length - 1] || 0;
19863 * Parse Text Tokens
19866 Parser.prototype.parseText = function() {
19867 var body = this.token.text;
19869 while (this.peek().type === 'text') {
19870 body += '\n' + this.next().text;
19873 return this.inline.output(body);
19877 * Parse Current Token
19880 Parser.prototype.tok = function() {
19881 switch (this.token.type) {
19886 return this.renderer.hr();
19889 return this.renderer.heading(
19890 this.inline.output(this.token.text),
19895 return this.renderer.code(this.token.text,
19897 this.token.escaped);
19910 for (i = 0; i < this.token.header.length; i++) {
19911 flags = { header: true, align: this.token.align[i] };
19912 cell += this.renderer.tablecell(
19913 this.inline.output(this.token.header[i]),
19914 { header: true, align: this.token.align[i] }
19917 header += this.renderer.tablerow(cell);
19919 for (i = 0; i < this.token.cells.length; i++) {
19920 row = this.token.cells[i];
19923 for (j = 0; j < row.length; j++) {
19924 cell += this.renderer.tablecell(
19925 this.inline.output(row[j]),
19926 { header: false, align: this.token.align[j] }
19930 body += this.renderer.tablerow(cell);
19932 return this.renderer.table(header, body);
19934 case 'blockquote_start': {
19937 while (this.next().type !== 'blockquote_end') {
19938 body += this.tok();
19941 return this.renderer.blockquote(body);
19943 case 'list_start': {
19945 , ordered = this.token.ordered;
19947 while (this.next().type !== 'list_end') {
19948 body += this.tok();
19951 return this.renderer.list(body, ordered);
19953 case 'list_item_start': {
19956 while (this.next().type !== 'list_item_end') {
19957 body += this.token.type === 'text'
19962 return this.renderer.listitem(body);
19964 case 'loose_item_start': {
19967 while (this.next().type !== 'list_item_end') {
19968 body += this.tok();
19971 return this.renderer.listitem(body);
19974 var html = !this.token.pre && !this.options.pedantic
19975 ? this.inline.output(this.token.text)
19977 return this.renderer.html(html);
19979 case 'paragraph': {
19980 return this.renderer.paragraph(this.inline.output(this.token.text));
19983 return this.renderer.paragraph(this.parseText());
19995 var marked = function (src, opt, callback) {
19996 if (callback || typeof opt === 'function') {
20002 opt = merge({}, marked.defaults, opt || {});
20004 var highlight = opt.highlight
20010 tokens = Lexer.lex(src, opt)
20012 return callback(e);
20015 pending = tokens.length;
20019 var done = function(err) {
20021 opt.highlight = highlight;
20022 return callback(err);
20028 out = Parser.parse(tokens, opt);
20033 opt.highlight = highlight;
20037 : callback(null, out);
20040 if (!highlight || highlight.length < 3) {
20044 delete opt.highlight;
20046 if (!pending) { return done(); }
20048 for (; i < tokens.length; i++) {
20050 if (token.type !== 'code') {
20051 return --pending || done();
20053 return highlight(token.text, token.lang, function(err, code) {
20054 if (err) { return done(err); }
20055 if (code == null || code === token.text) {
20056 return --pending || done();
20059 token.escaped = true;
20060 --pending || done();
20068 if (opt) { opt = merge({}, marked.defaults, opt); }
20069 return Parser.parse(Lexer.lex(src, opt), opt);
20071 e.message += '\nPlease report this to https://github.com/chjj/marked.';
20072 if ((opt || marked.defaults).silent) {
20073 return '<p>An error occured:</p><pre>'
20074 + escape(e.message + '', true)
20086 marked.setOptions = function(opt) {
20087 merge(marked.defaults, opt);
20091 marked.defaults = {
20102 langPrefix: 'lang-',
20103 smartypants: false,
20105 renderer: new Renderer,
20113 marked.Parser = Parser;
20114 marked.parser = Parser.parse;
20116 marked.Renderer = Renderer;
20118 marked.Lexer = Lexer;
20119 marked.lexer = Lexer.lex;
20121 marked.InlineLexer = InlineLexer;
20122 marked.inlineLexer = InlineLexer.output;
20124 marked.parse = marked;
20126 Roo.Markdown.marked = marked;
20130 * Ext JS Library 1.1.1
20131 * Copyright(c) 2006-2007, Ext JS, LLC.
20133 * Originally Released Under LGPL - original licence link has changed is not relivant.
20136 * <script type="text/javascript">
20142 * These classes are derivatives of the similarly named classes in the YUI Library.
20143 * The original license:
20144 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20145 * Code licensed under the BSD License:
20146 * http://developer.yahoo.net/yui/license.txt
20151 var Event=Roo.EventManager;
20152 var Dom=Roo.lib.Dom;
20155 * @class Roo.dd.DragDrop
20156 * @extends Roo.util.Observable
20157 * Defines the interface and base operation of items that that can be
20158 * dragged or can be drop targets. It was designed to be extended, overriding
20159 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20160 * Up to three html elements can be associated with a DragDrop instance:
20162 * <li>linked element: the element that is passed into the constructor.
20163 * This is the element which defines the boundaries for interaction with
20164 * other DragDrop objects.</li>
20165 * <li>handle element(s): The drag operation only occurs if the element that
20166 * was clicked matches a handle element. By default this is the linked
20167 * element, but there are times that you will want only a portion of the
20168 * linked element to initiate the drag operation, and the setHandleElId()
20169 * method provides a way to define this.</li>
20170 * <li>drag element: this represents the element that would be moved along
20171 * with the cursor during a drag operation. By default, this is the linked
20172 * element itself as in {@link Roo.dd.DD}. setDragElId() lets you define
20173 * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20176 * This class should not be instantiated until the onload event to ensure that
20177 * the associated elements are available.
20178 * The following would define a DragDrop obj that would interact with any
20179 * other DragDrop obj in the "group1" group:
20181 * dd = new Roo.dd.DragDrop("div1", "group1");
20183 * Since none of the event handlers have been implemented, nothing would
20184 * actually happen if you were to run the code above. Normally you would
20185 * override this class or one of the default implementations, but you can
20186 * also override the methods you want on an instance of the class...
20188 * dd.onDragDrop = function(e, id) {
20189 * alert("dd was dropped on " + id);
20193 * @param {String} id of the element that is linked to this instance
20194 * @param {String} sGroup the group of related DragDrop objects
20195 * @param {object} config an object containing configurable attributes
20196 * Valid properties for DragDrop:
20197 * padding, isTarget, maintainOffset, primaryButtonOnly
20199 Roo.dd.DragDrop = function(id, sGroup, config) {
20201 this.init(id, sGroup, config);
20206 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20209 * The id of the element associated with this object. This is what we
20210 * refer to as the "linked element" because the size and position of
20211 * this element is used to determine when the drag and drop objects have
20219 * Configuration attributes passed into the constructor
20226 * The id of the element that will be dragged. By default this is same
20227 * as the linked element , but could be changed to another element. Ex:
20229 * @property dragElId
20236 * the id of the element that initiates the drag operation. By default
20237 * this is the linked element, but could be changed to be a child of this
20238 * element. This lets us do things like only starting the drag when the
20239 * header element within the linked html element is clicked.
20240 * @property handleElId
20247 * An associative array of HTML tags that will be ignored if clicked.
20248 * @property invalidHandleTypes
20249 * @type {string: string}
20251 invalidHandleTypes: null,
20254 * An associative array of ids for elements that will be ignored if clicked
20255 * @property invalidHandleIds
20256 * @type {string: string}
20258 invalidHandleIds: null,
20261 * An indexted array of css class names for elements that will be ignored
20263 * @property invalidHandleClasses
20266 invalidHandleClasses: null,
20269 * The linked element's absolute X position at the time the drag was
20271 * @property startPageX
20278 * The linked element's absolute X position at the time the drag was
20280 * @property startPageY
20287 * The group defines a logical collection of DragDrop objects that are
20288 * related. Instances only get events when interacting with other
20289 * DragDrop object in the same group. This lets us define multiple
20290 * groups using a single DragDrop subclass if we want.
20292 * @type {string: string}
20297 * Individual drag/drop instances can be locked. This will prevent
20298 * onmousedown start drag.
20306 * Lock this instance
20309 lock: function() { this.locked = true; },
20312 * Unlock this instace
20315 unlock: function() { this.locked = false; },
20318 * By default, all insances can be a drop target. This can be disabled by
20319 * setting isTarget to false.
20326 * The padding configured for this drag and drop object for calculating
20327 * the drop zone intersection with this object.
20334 * Cached reference to the linked element
20335 * @property _domRef
20341 * Internal typeof flag
20342 * @property __ygDragDrop
20345 __ygDragDrop: true,
20348 * Set to true when horizontal contraints are applied
20349 * @property constrainX
20356 * Set to true when vertical contraints are applied
20357 * @property constrainY
20364 * The left constraint
20372 * The right constraint
20380 * The up constraint
20389 * The down constraint
20397 * Maintain offsets when we resetconstraints. Set to true when you want
20398 * the position of the element relative to its parent to stay the same
20399 * when the page changes
20401 * @property maintainOffset
20404 maintainOffset: false,
20407 * Array of pixel locations the element will snap to if we specified a
20408 * horizontal graduation/interval. This array is generated automatically
20409 * when you define a tick interval.
20416 * Array of pixel locations the element will snap to if we specified a
20417 * vertical graduation/interval. This array is generated automatically
20418 * when you define a tick interval.
20425 * By default the drag and drop instance will only respond to the primary
20426 * button click (left button for a right-handed mouse). Set to true to
20427 * allow drag and drop to start with any mouse click that is propogated
20429 * @property primaryButtonOnly
20432 primaryButtonOnly: true,
20435 * The availabe property is false until the linked dom element is accessible.
20436 * @property available
20442 * By default, drags can only be initiated if the mousedown occurs in the
20443 * region the linked element is. This is done in part to work around a
20444 * bug in some browsers that mis-report the mousedown if the previous
20445 * mouseup happened outside of the window. This property is set to true
20446 * if outer handles are defined.
20448 * @property hasOuterHandles
20452 hasOuterHandles: false,
20455 * Code that executes immediately before the startDrag event
20456 * @method b4StartDrag
20459 b4StartDrag: function(x, y) { },
20462 * Abstract method called after a drag/drop object is clicked
20463 * and the drag or mousedown time thresholds have beeen met.
20464 * @method startDrag
20465 * @param {int} X click location
20466 * @param {int} Y click location
20468 startDrag: function(x, y) { /* override this */ },
20471 * Code that executes immediately before the onDrag event
20475 b4Drag: function(e) { },
20478 * Abstract method called during the onMouseMove event while dragging an
20481 * @param {Event} e the mousemove event
20483 onDrag: function(e) { /* override this */ },
20486 * Abstract method called when this element fist begins hovering over
20487 * another DragDrop obj
20488 * @method onDragEnter
20489 * @param {Event} e the mousemove event
20490 * @param {String|DragDrop[]} id In POINT mode, the element
20491 * id this is hovering over. In INTERSECT mode, an array of one or more
20492 * dragdrop items being hovered over.
20494 onDragEnter: function(e, id) { /* override this */ },
20497 * Code that executes immediately before the onDragOver event
20498 * @method b4DragOver
20501 b4DragOver: function(e) { },
20504 * Abstract method called when this element is hovering over another
20506 * @method onDragOver
20507 * @param {Event} e the mousemove event
20508 * @param {String|DragDrop[]} id In POINT mode, the element
20509 * id this is hovering over. In INTERSECT mode, an array of dd items
20510 * being hovered over.
20512 onDragOver: function(e, id) { /* override this */ },
20515 * Code that executes immediately before the onDragOut event
20516 * @method b4DragOut
20519 b4DragOut: function(e) { },
20522 * Abstract method called when we are no longer hovering over an element
20523 * @method onDragOut
20524 * @param {Event} e the mousemove event
20525 * @param {String|DragDrop[]} id In POINT mode, the element
20526 * id this was hovering over. In INTERSECT mode, an array of dd items
20527 * that the mouse is no longer over.
20529 onDragOut: function(e, id) { /* override this */ },
20532 * Code that executes immediately before the onDragDrop event
20533 * @method b4DragDrop
20536 b4DragDrop: function(e) { },
20539 * Abstract method called when this item is dropped on another DragDrop
20541 * @method onDragDrop
20542 * @param {Event} e the mouseup event
20543 * @param {String|DragDrop[]} id In POINT mode, the element
20544 * id this was dropped on. In INTERSECT mode, an array of dd items this
20547 onDragDrop: function(e, id) { /* override this */ },
20550 * Abstract method called when this item is dropped on an area with no
20552 * @method onInvalidDrop
20553 * @param {Event} e the mouseup event
20555 onInvalidDrop: function(e) { /* override this */ },
20558 * Code that executes immediately before the endDrag event
20559 * @method b4EndDrag
20562 b4EndDrag: function(e) { },
20565 * Fired when we are done dragging the object
20567 * @param {Event} e the mouseup event
20569 endDrag: function(e) { /* override this */ },
20572 * Code executed immediately before the onMouseDown event
20573 * @method b4MouseDown
20574 * @param {Event} e the mousedown event
20577 b4MouseDown: function(e) { },
20580 * Event handler that fires when a drag/drop obj gets a mousedown
20581 * @method onMouseDown
20582 * @param {Event} e the mousedown event
20584 onMouseDown: function(e) { /* override this */ },
20587 * Event handler that fires when a drag/drop obj gets a mouseup
20588 * @method onMouseUp
20589 * @param {Event} e the mouseup event
20591 onMouseUp: function(e) { /* override this */ },
20594 * Override the onAvailable method to do what is needed after the initial
20595 * position was determined.
20596 * @method onAvailable
20598 onAvailable: function () {
20602 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20605 defaultPadding : {left:0, right:0, top:0, bottom:0},
20608 * Initializes the drag drop object's constraints to restrict movement to a certain element.
20612 var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20613 { dragElId: "existingProxyDiv" });
20614 dd.startDrag = function(){
20615 this.constrainTo("parent-id");
20618 * Or you can initalize it using the {@link Roo.Element} object:
20620 Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20621 startDrag : function(){
20622 this.constrainTo("parent-id");
20626 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20627 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20628 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20629 * an object containing the sides to pad. For example: {right:10, bottom:10}
20630 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20632 constrainTo : function(constrainTo, pad, inContent){
20633 if(typeof pad == "number"){
20634 pad = {left: pad, right:pad, top:pad, bottom:pad};
20636 pad = pad || this.defaultPadding;
20637 var b = Roo.get(this.getEl()).getBox();
20638 var ce = Roo.get(constrainTo);
20639 var s = ce.getScroll();
20640 var c, cd = ce.dom;
20641 if(cd == document.body){
20642 c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20645 c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20649 var topSpace = b.y - c.y;
20650 var leftSpace = b.x - c.x;
20652 this.resetConstraints();
20653 this.setXConstraint(leftSpace - (pad.left||0), // left
20654 c.width - leftSpace - b.width - (pad.right||0) //right
20656 this.setYConstraint(topSpace - (pad.top||0), //top
20657 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20662 * Returns a reference to the linked element
20664 * @return {HTMLElement} the html element
20666 getEl: function() {
20667 if (!this._domRef) {
20668 this._domRef = Roo.getDom(this.id);
20671 return this._domRef;
20675 * Returns a reference to the actual element to drag. By default this is
20676 * the same as the html element, but it can be assigned to another
20677 * element. An example of this can be found in Roo.dd.DDProxy
20678 * @method getDragEl
20679 * @return {HTMLElement} the html element
20681 getDragEl: function() {
20682 return Roo.getDom(this.dragElId);
20686 * Sets up the DragDrop object. Must be called in the constructor of any
20687 * Roo.dd.DragDrop subclass
20689 * @param id the id of the linked element
20690 * @param {String} sGroup the group of related items
20691 * @param {object} config configuration attributes
20693 init: function(id, sGroup, config) {
20694 this.initTarget(id, sGroup, config);
20695 if (!Roo.isTouch) {
20696 Event.on(this.id, "mousedown", this.handleMouseDown, this);
20698 Event.on(this.id, "touchstart", this.handleMouseDown, this);
20699 // Event.on(this.id, "selectstart", Event.preventDefault);
20703 * Initializes Targeting functionality only... the object does not
20704 * get a mousedown handler.
20705 * @method initTarget
20706 * @param id the id of the linked element
20707 * @param {String} sGroup the group of related items
20708 * @param {object} config configuration attributes
20710 initTarget: function(id, sGroup, config) {
20712 // configuration attributes
20713 this.config = config || {};
20715 // create a local reference to the drag and drop manager
20716 this.DDM = Roo.dd.DDM;
20717 // initialize the groups array
20720 // assume that we have an element reference instead of an id if the
20721 // parameter is not a string
20722 if (typeof id !== "string") {
20729 // add to an interaction group
20730 this.addToGroup((sGroup) ? sGroup : "default");
20732 // We don't want to register this as the handle with the manager
20733 // so we just set the id rather than calling the setter.
20734 this.handleElId = id;
20736 // the linked element is the element that gets dragged by default
20737 this.setDragElId(id);
20739 // by default, clicked anchors will not start drag operations.
20740 this.invalidHandleTypes = { A: "A" };
20741 this.invalidHandleIds = {};
20742 this.invalidHandleClasses = [];
20744 this.applyConfig();
20746 this.handleOnAvailable();
20750 * Applies the configuration parameters that were passed into the constructor.
20751 * This is supposed to happen at each level through the inheritance chain. So
20752 * a DDProxy implentation will execute apply config on DDProxy, DD, and
20753 * DragDrop in order to get all of the parameters that are available in
20755 * @method applyConfig
20757 applyConfig: function() {
20759 // configurable properties:
20760 // padding, isTarget, maintainOffset, primaryButtonOnly
20761 this.padding = this.config.padding || [0, 0, 0, 0];
20762 this.isTarget = (this.config.isTarget !== false);
20763 this.maintainOffset = (this.config.maintainOffset);
20764 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20769 * Executed when the linked element is available
20770 * @method handleOnAvailable
20773 handleOnAvailable: function() {
20774 this.available = true;
20775 this.resetConstraints();
20776 this.onAvailable();
20780 * Configures the padding for the target zone in px. Effectively expands
20781 * (or reduces) the virtual object size for targeting calculations.
20782 * Supports css-style shorthand; if only one parameter is passed, all sides
20783 * will have that padding, and if only two are passed, the top and bottom
20784 * will have the first param, the left and right the second.
20785 * @method setPadding
20786 * @param {int} iTop Top pad
20787 * @param {int} iRight Right pad
20788 * @param {int} iBot Bot pad
20789 * @param {int} iLeft Left pad
20791 setPadding: function(iTop, iRight, iBot, iLeft) {
20792 // this.padding = [iLeft, iRight, iTop, iBot];
20793 if (!iRight && 0 !== iRight) {
20794 this.padding = [iTop, iTop, iTop, iTop];
20795 } else if (!iBot && 0 !== iBot) {
20796 this.padding = [iTop, iRight, iTop, iRight];
20798 this.padding = [iTop, iRight, iBot, iLeft];
20803 * Stores the initial placement of the linked element.
20804 * @method setInitialPosition
20805 * @param {int} diffX the X offset, default 0
20806 * @param {int} diffY the Y offset, default 0
20808 setInitPosition: function(diffX, diffY) {
20809 var el = this.getEl();
20811 if (!this.DDM.verifyEl(el)) {
20815 var dx = diffX || 0;
20816 var dy = diffY || 0;
20818 var p = Dom.getXY( el );
20820 this.initPageX = p[0] - dx;
20821 this.initPageY = p[1] - dy;
20823 this.lastPageX = p[0];
20824 this.lastPageY = p[1];
20827 this.setStartPosition(p);
20831 * Sets the start position of the element. This is set when the obj
20832 * is initialized, the reset when a drag is started.
20833 * @method setStartPosition
20834 * @param pos current position (from previous lookup)
20837 setStartPosition: function(pos) {
20838 var p = pos || Dom.getXY( this.getEl() );
20839 this.deltaSetXY = null;
20841 this.startPageX = p[0];
20842 this.startPageY = p[1];
20846 * Add this instance to a group of related drag/drop objects. All
20847 * instances belong to at least one group, and can belong to as many
20848 * groups as needed.
20849 * @method addToGroup
20850 * @param sGroup {string} the name of the group
20852 addToGroup: function(sGroup) {
20853 this.groups[sGroup] = true;
20854 this.DDM.regDragDrop(this, sGroup);
20858 * Remove's this instance from the supplied interaction group
20859 * @method removeFromGroup
20860 * @param {string} sGroup The group to drop
20862 removeFromGroup: function(sGroup) {
20863 if (this.groups[sGroup]) {
20864 delete this.groups[sGroup];
20867 this.DDM.removeDDFromGroup(this, sGroup);
20871 * Allows you to specify that an element other than the linked element
20872 * will be moved with the cursor during a drag
20873 * @method setDragElId
20874 * @param id {string} the id of the element that will be used to initiate the drag
20876 setDragElId: function(id) {
20877 this.dragElId = id;
20881 * Allows you to specify a child of the linked element that should be
20882 * used to initiate the drag operation. An example of this would be if
20883 * you have a content div with text and links. Clicking anywhere in the
20884 * content area would normally start the drag operation. Use this method
20885 * to specify that an element inside of the content div is the element
20886 * that starts the drag operation.
20887 * @method setHandleElId
20888 * @param id {string} the id of the element that will be used to
20889 * initiate the drag.
20891 setHandleElId: function(id) {
20892 if (typeof id !== "string") {
20895 this.handleElId = id;
20896 this.DDM.regHandle(this.id, id);
20900 * Allows you to set an element outside of the linked element as a drag
20902 * @method setOuterHandleElId
20903 * @param id the id of the element that will be used to initiate the drag
20905 setOuterHandleElId: function(id) {
20906 if (typeof id !== "string") {
20909 Event.on(id, "mousedown",
20910 this.handleMouseDown, this);
20911 this.setHandleElId(id);
20913 this.hasOuterHandles = true;
20917 * Remove all drag and drop hooks for this element
20920 unreg: function() {
20921 Event.un(this.id, "mousedown",
20922 this.handleMouseDown);
20923 Event.un(this.id, "touchstart",
20924 this.handleMouseDown);
20925 this._domRef = null;
20926 this.DDM._remove(this);
20929 destroy : function(){
20934 * Returns true if this instance is locked, or the drag drop mgr is locked
20935 * (meaning that all drag/drop is disabled on the page.)
20937 * @return {boolean} true if this obj or all drag/drop is locked, else
20940 isLocked: function() {
20941 return (this.DDM.isLocked() || this.locked);
20945 * Fired when this object is clicked
20946 * @method handleMouseDown
20948 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20951 handleMouseDown: function(e, oDD){
20953 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20954 //Roo.log('not touch/ button !=0');
20957 if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20958 return; // double touch..
20962 if (this.isLocked()) {
20963 //Roo.log('locked');
20967 this.DDM.refreshCache(this.groups);
20968 // Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20969 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20970 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
20971 //Roo.log('no outer handes or not over target');
20974 // Roo.log('check validator');
20975 if (this.clickValidator(e)) {
20976 // Roo.log('validate success');
20977 // set the initial element position
20978 this.setStartPosition();
20981 this.b4MouseDown(e);
20982 this.onMouseDown(e);
20984 this.DDM.handleMouseDown(e, this);
20986 this.DDM.stopEvent(e);
20994 clickValidator: function(e) {
20995 var target = e.getTarget();
20996 return ( this.isValidHandleChild(target) &&
20997 (this.id == this.handleElId ||
20998 this.DDM.handleWasClicked(target, this.id)) );
21002 * Allows you to specify a tag name that should not start a drag operation
21003 * when clicked. This is designed to facilitate embedding links within a
21004 * drag handle that do something other than start the drag.
21005 * @method addInvalidHandleType
21006 * @param {string} tagName the type of element to exclude
21008 addInvalidHandleType: function(tagName) {
21009 var type = tagName.toUpperCase();
21010 this.invalidHandleTypes[type] = type;
21014 * Lets you to specify an element id for a child of a drag handle
21015 * that should not initiate a drag
21016 * @method addInvalidHandleId
21017 * @param {string} id the element id of the element you wish to ignore
21019 addInvalidHandleId: function(id) {
21020 if (typeof id !== "string") {
21023 this.invalidHandleIds[id] = id;
21027 * Lets you specify a css class of elements that will not initiate a drag
21028 * @method addInvalidHandleClass
21029 * @param {string} cssClass the class of the elements you wish to ignore
21031 addInvalidHandleClass: function(cssClass) {
21032 this.invalidHandleClasses.push(cssClass);
21036 * Unsets an excluded tag name set by addInvalidHandleType
21037 * @method removeInvalidHandleType
21038 * @param {string} tagName the type of element to unexclude
21040 removeInvalidHandleType: function(tagName) {
21041 var type = tagName.toUpperCase();
21042 // this.invalidHandleTypes[type] = null;
21043 delete this.invalidHandleTypes[type];
21047 * Unsets an invalid handle id
21048 * @method removeInvalidHandleId
21049 * @param {string} id the id of the element to re-enable
21051 removeInvalidHandleId: function(id) {
21052 if (typeof id !== "string") {
21055 delete this.invalidHandleIds[id];
21059 * Unsets an invalid css class
21060 * @method removeInvalidHandleClass
21061 * @param {string} cssClass the class of the element(s) you wish to
21064 removeInvalidHandleClass: function(cssClass) {
21065 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21066 if (this.invalidHandleClasses[i] == cssClass) {
21067 delete this.invalidHandleClasses[i];
21073 * Checks the tag exclusion list to see if this click should be ignored
21074 * @method isValidHandleChild
21075 * @param {HTMLElement} node the HTMLElement to evaluate
21076 * @return {boolean} true if this is a valid tag type, false if not
21078 isValidHandleChild: function(node) {
21081 // var n = (node.nodeName == "#text") ? node.parentNode : node;
21084 nodeName = node.nodeName.toUpperCase();
21086 nodeName = node.nodeName;
21088 valid = valid && !this.invalidHandleTypes[nodeName];
21089 valid = valid && !this.invalidHandleIds[node.id];
21091 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21092 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21101 * Create the array of horizontal tick marks if an interval was specified
21102 * in setXConstraint().
21103 * @method setXTicks
21106 setXTicks: function(iStartX, iTickSize) {
21108 this.xTickSize = iTickSize;
21112 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21114 this.xTicks[this.xTicks.length] = i;
21119 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21121 this.xTicks[this.xTicks.length] = i;
21126 this.xTicks.sort(this.DDM.numericSort) ;
21130 * Create the array of vertical tick marks if an interval was specified in
21131 * setYConstraint().
21132 * @method setYTicks
21135 setYTicks: function(iStartY, iTickSize) {
21137 this.yTickSize = iTickSize;
21141 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21143 this.yTicks[this.yTicks.length] = i;
21148 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21150 this.yTicks[this.yTicks.length] = i;
21155 this.yTicks.sort(this.DDM.numericSort) ;
21159 * By default, the element can be dragged any place on the screen. Use
21160 * this method to limit the horizontal travel of the element. Pass in
21161 * 0,0 for the parameters if you want to lock the drag to the y axis.
21162 * @method setXConstraint
21163 * @param {int} iLeft the number of pixels the element can move to the left
21164 * @param {int} iRight the number of pixels the element can move to the
21166 * @param {int} iTickSize optional parameter for specifying that the
21168 * should move iTickSize pixels at a time.
21170 setXConstraint: function(iLeft, iRight, iTickSize) {
21171 this.leftConstraint = iLeft;
21172 this.rightConstraint = iRight;
21174 this.minX = this.initPageX - iLeft;
21175 this.maxX = this.initPageX + iRight;
21176 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21178 this.constrainX = true;
21182 * Clears any constraints applied to this instance. Also clears ticks
21183 * since they can't exist independent of a constraint at this time.
21184 * @method clearConstraints
21186 clearConstraints: function() {
21187 this.constrainX = false;
21188 this.constrainY = false;
21193 * Clears any tick interval defined for this instance
21194 * @method clearTicks
21196 clearTicks: function() {
21197 this.xTicks = null;
21198 this.yTicks = null;
21199 this.xTickSize = 0;
21200 this.yTickSize = 0;
21204 * By default, the element can be dragged any place on the screen. Set
21205 * this to limit the vertical travel of the element. Pass in 0,0 for the
21206 * parameters if you want to lock the drag to the x axis.
21207 * @method setYConstraint
21208 * @param {int} iUp the number of pixels the element can move up
21209 * @param {int} iDown the number of pixels the element can move down
21210 * @param {int} iTickSize optional parameter for specifying that the
21211 * element should move iTickSize pixels at a time.
21213 setYConstraint: function(iUp, iDown, iTickSize) {
21214 this.topConstraint = iUp;
21215 this.bottomConstraint = iDown;
21217 this.minY = this.initPageY - iUp;
21218 this.maxY = this.initPageY + iDown;
21219 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21221 this.constrainY = true;
21226 * resetConstraints must be called if you manually reposition a dd element.
21227 * @method resetConstraints
21228 * @param {boolean} maintainOffset
21230 resetConstraints: function() {
21233 // Maintain offsets if necessary
21234 if (this.initPageX || this.initPageX === 0) {
21235 // figure out how much this thing has moved
21236 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21237 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21239 this.setInitPosition(dx, dy);
21241 // This is the first time we have detected the element's position
21243 this.setInitPosition();
21246 if (this.constrainX) {
21247 this.setXConstraint( this.leftConstraint,
21248 this.rightConstraint,
21252 if (this.constrainY) {
21253 this.setYConstraint( this.topConstraint,
21254 this.bottomConstraint,
21260 * Normally the drag element is moved pixel by pixel, but we can specify
21261 * that it move a number of pixels at a time. This method resolves the
21262 * location when we have it set up like this.
21264 * @param {int} val where we want to place the object
21265 * @param {int[]} tickArray sorted array of valid points
21266 * @return {int} the closest tick
21269 getTick: function(val, tickArray) {
21272 // If tick interval is not defined, it is effectively 1 pixel,
21273 // so we return the value passed to us.
21275 } else if (tickArray[0] >= val) {
21276 // The value is lower than the first tick, so we return the first
21278 return tickArray[0];
21280 for (var i=0, len=tickArray.length; i<len; ++i) {
21282 if (tickArray[next] && tickArray[next] >= val) {
21283 var diff1 = val - tickArray[i];
21284 var diff2 = tickArray[next] - val;
21285 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21289 // The value is larger than the last tick, so we return the last
21291 return tickArray[tickArray.length - 1];
21298 * @return {string} string representation of the dd obj
21300 toString: function() {
21301 return ("DragDrop " + this.id);
21309 * Ext JS Library 1.1.1
21310 * Copyright(c) 2006-2007, Ext JS, LLC.
21312 * Originally Released Under LGPL - original licence link has changed is not relivant.
21315 * <script type="text/javascript">
21320 * The drag and drop utility provides a framework for building drag and drop
21321 * applications. In addition to enabling drag and drop for specific elements,
21322 * the drag and drop elements are tracked by the manager class, and the
21323 * interactions between the various elements are tracked during the drag and
21324 * the implementing code is notified about these important moments.
21327 // Only load the library once. Rewriting the manager class would orphan
21328 // existing drag and drop instances.
21329 if (!Roo.dd.DragDropMgr) {
21332 * @class Roo.dd.DragDropMgr
21333 * DragDropMgr is a singleton that tracks the element interaction for
21334 * all DragDrop items in the window. Generally, you will not call
21335 * this class directly, but it does have helper methods that could
21336 * be useful in your DragDrop implementations.
21339 Roo.dd.DragDropMgr = function() {
21341 var Event = Roo.EventManager;
21346 * Two dimensional Array of registered DragDrop objects. The first
21347 * dimension is the DragDrop item group, the second the DragDrop
21350 * @type {string: string}
21357 * Array of element ids defined as drag handles. Used to determine
21358 * if the element that generated the mousedown event is actually the
21359 * handle and not the html element itself.
21360 * @property handleIds
21361 * @type {string: string}
21368 * the DragDrop object that is currently being dragged
21369 * @property dragCurrent
21377 * the DragDrop object(s) that are being hovered over
21378 * @property dragOvers
21386 * the X distance between the cursor and the object being dragged
21395 * the Y distance between the cursor and the object being dragged
21404 * Flag to determine if we should prevent the default behavior of the
21405 * events we define. By default this is true, but this can be set to
21406 * false if you need the default behavior (not recommended)
21407 * @property preventDefault
21411 preventDefault: true,
21414 * Flag to determine if we should stop the propagation of the events
21415 * we generate. This is true by default but you may want to set it to
21416 * false if the html element contains other features that require the
21418 * @property stopPropagation
21422 stopPropagation: true,
21425 * Internal flag that is set to true when drag and drop has been
21427 * @property initialized
21434 * All drag and drop can be disabled.
21442 * Called the first time an element is registered.
21448 this.initialized = true;
21452 * In point mode, drag and drop interaction is defined by the
21453 * location of the cursor during the drag/drop
21461 * In intersect mode, drag and drop interactio nis defined by the
21462 * overlap of two or more drag and drop objects.
21463 * @property INTERSECT
21470 * The current drag and drop mode. Default: POINT
21478 * Runs method on all drag and drop objects
21479 * @method _execOnAll
21483 _execOnAll: function(sMethod, args) {
21484 for (var i in this.ids) {
21485 for (var j in this.ids[i]) {
21486 var oDD = this.ids[i][j];
21487 if (! this.isTypeOfDD(oDD)) {
21490 oDD[sMethod].apply(oDD, args);
21496 * Drag and drop initialization. Sets up the global event handlers
21501 _onLoad: function() {
21505 if (!Roo.isTouch) {
21506 Event.on(document, "mouseup", this.handleMouseUp, this, true);
21507 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21509 Event.on(document, "touchend", this.handleMouseUp, this, true);
21510 Event.on(document, "touchmove", this.handleMouseMove, this, true);
21512 Event.on(window, "unload", this._onUnload, this, true);
21513 Event.on(window, "resize", this._onResize, this, true);
21514 // Event.on(window, "mouseout", this._test);
21519 * Reset constraints on all drag and drop objs
21520 * @method _onResize
21524 _onResize: function(e) {
21525 this._execOnAll("resetConstraints", []);
21529 * Lock all drag and drop functionality
21533 lock: function() { this.locked = true; },
21536 * Unlock all drag and drop functionality
21540 unlock: function() { this.locked = false; },
21543 * Is drag and drop locked?
21545 * @return {boolean} True if drag and drop is locked, false otherwise.
21548 isLocked: function() { return this.locked; },
21551 * Location cache that is set for all drag drop objects when a drag is
21552 * initiated, cleared when the drag is finished.
21553 * @property locationCache
21560 * Set useCache to false if you want to force object the lookup of each
21561 * drag and drop linked element constantly during a drag.
21562 * @property useCache
21569 * The number of pixels that the mouse needs to move after the
21570 * mousedown before the drag is initiated. Default=3;
21571 * @property clickPixelThresh
21575 clickPixelThresh: 3,
21578 * The number of milliseconds after the mousedown event to initiate the
21579 * drag if we don't get a mouseup event. Default=1000
21580 * @property clickTimeThresh
21584 clickTimeThresh: 350,
21587 * Flag that indicates that either the drag pixel threshold or the
21588 * mousdown time threshold has been met
21589 * @property dragThreshMet
21594 dragThreshMet: false,
21597 * Timeout used for the click time threshold
21598 * @property clickTimeout
21603 clickTimeout: null,
21606 * The X position of the mousedown event stored for later use when a
21607 * drag threshold is met.
21616 * The Y position of the mousedown event stored for later use when a
21617 * drag threshold is met.
21626 * Each DragDrop instance must be registered with the DragDropMgr.
21627 * This is executed in DragDrop.init()
21628 * @method regDragDrop
21629 * @param {DragDrop} oDD the DragDrop object to register
21630 * @param {String} sGroup the name of the group this element belongs to
21633 regDragDrop: function(oDD, sGroup) {
21634 if (!this.initialized) { this.init(); }
21636 if (!this.ids[sGroup]) {
21637 this.ids[sGroup] = {};
21639 this.ids[sGroup][oDD.id] = oDD;
21643 * Removes the supplied dd instance from the supplied group. Executed
21644 * by DragDrop.removeFromGroup, so don't call this function directly.
21645 * @method removeDDFromGroup
21649 removeDDFromGroup: function(oDD, sGroup) {
21650 if (!this.ids[sGroup]) {
21651 this.ids[sGroup] = {};
21654 var obj = this.ids[sGroup];
21655 if (obj && obj[oDD.id]) {
21656 delete obj[oDD.id];
21661 * Unregisters a drag and drop item. This is executed in
21662 * DragDrop.unreg, use that method instead of calling this directly.
21667 _remove: function(oDD) {
21668 for (var g in oDD.groups) {
21669 if (g && this.ids[g][oDD.id]) {
21670 delete this.ids[g][oDD.id];
21673 delete this.handleIds[oDD.id];
21677 * Each DragDrop handle element must be registered. This is done
21678 * automatically when executing DragDrop.setHandleElId()
21679 * @method regHandle
21680 * @param {String} sDDId the DragDrop id this element is a handle for
21681 * @param {String} sHandleId the id of the element that is the drag
21685 regHandle: function(sDDId, sHandleId) {
21686 if (!this.handleIds[sDDId]) {
21687 this.handleIds[sDDId] = {};
21689 this.handleIds[sDDId][sHandleId] = sHandleId;
21693 * Utility function to determine if a given element has been
21694 * registered as a drag drop item.
21695 * @method isDragDrop
21696 * @param {String} id the element id to check
21697 * @return {boolean} true if this element is a DragDrop item,
21701 isDragDrop: function(id) {
21702 return ( this.getDDById(id) ) ? true : false;
21706 * Returns the drag and drop instances that are in all groups the
21707 * passed in instance belongs to.
21708 * @method getRelated
21709 * @param {DragDrop} p_oDD the obj to get related data for
21710 * @param {boolean} bTargetsOnly if true, only return targetable objs
21711 * @return {DragDrop[]} the related instances
21714 getRelated: function(p_oDD, bTargetsOnly) {
21716 for (var i in p_oDD.groups) {
21717 for (j in this.ids[i]) {
21718 var dd = this.ids[i][j];
21719 if (! this.isTypeOfDD(dd)) {
21722 if (!bTargetsOnly || dd.isTarget) {
21723 oDDs[oDDs.length] = dd;
21732 * Returns true if the specified dd target is a legal target for
21733 * the specifice drag obj
21734 * @method isLegalTarget
21735 * @param {DragDrop} the drag obj
21736 * @param {DragDrop} the target
21737 * @return {boolean} true if the target is a legal target for the
21741 isLegalTarget: function (oDD, oTargetDD) {
21742 var targets = this.getRelated(oDD, true);
21743 for (var i=0, len=targets.length;i<len;++i) {
21744 if (targets[i].id == oTargetDD.id) {
21753 * My goal is to be able to transparently determine if an object is
21754 * typeof DragDrop, and the exact subclass of DragDrop. typeof
21755 * returns "object", oDD.constructor.toString() always returns
21756 * "DragDrop" and not the name of the subclass. So for now it just
21757 * evaluates a well-known variable in DragDrop.
21758 * @method isTypeOfDD
21759 * @param {Object} the object to evaluate
21760 * @return {boolean} true if typeof oDD = DragDrop
21763 isTypeOfDD: function (oDD) {
21764 return (oDD && oDD.__ygDragDrop);
21768 * Utility function to determine if a given element has been
21769 * registered as a drag drop handle for the given Drag Drop object.
21771 * @param {String} id the element id to check
21772 * @return {boolean} true if this element is a DragDrop handle, false
21776 isHandle: function(sDDId, sHandleId) {
21777 return ( this.handleIds[sDDId] &&
21778 this.handleIds[sDDId][sHandleId] );
21782 * Returns the DragDrop instance for a given id
21783 * @method getDDById
21784 * @param {String} id the id of the DragDrop object
21785 * @return {DragDrop} the drag drop object, null if it is not found
21788 getDDById: function(id) {
21789 for (var i in this.ids) {
21790 if (this.ids[i][id]) {
21791 return this.ids[i][id];
21798 * Fired after a registered DragDrop object gets the mousedown event.
21799 * Sets up the events required to track the object being dragged
21800 * @method handleMouseDown
21801 * @param {Event} e the event
21802 * @param oDD the DragDrop object being dragged
21806 handleMouseDown: function(e, oDD) {
21808 Roo.QuickTips.disable();
21810 this.currentTarget = e.getTarget();
21812 this.dragCurrent = oDD;
21814 var el = oDD.getEl();
21816 // track start position
21817 this.startX = e.getPageX();
21818 this.startY = e.getPageY();
21820 this.deltaX = this.startX - el.offsetLeft;
21821 this.deltaY = this.startY - el.offsetTop;
21823 this.dragThreshMet = false;
21825 this.clickTimeout = setTimeout(
21827 var DDM = Roo.dd.DDM;
21828 DDM.startDrag(DDM.startX, DDM.startY);
21830 this.clickTimeThresh );
21834 * Fired when either the drag pixel threshol or the mousedown hold
21835 * time threshold has been met.
21836 * @method startDrag
21837 * @param x {int} the X position of the original mousedown
21838 * @param y {int} the Y position of the original mousedown
21841 startDrag: function(x, y) {
21842 clearTimeout(this.clickTimeout);
21843 if (this.dragCurrent) {
21844 this.dragCurrent.b4StartDrag(x, y);
21845 this.dragCurrent.startDrag(x, y);
21847 this.dragThreshMet = true;
21851 * Internal function to handle the mouseup event. Will be invoked
21852 * from the context of the document.
21853 * @method handleMouseUp
21854 * @param {Event} e the event
21858 handleMouseUp: function(e) {
21861 Roo.QuickTips.enable();
21863 if (! this.dragCurrent) {
21867 clearTimeout(this.clickTimeout);
21869 if (this.dragThreshMet) {
21870 this.fireEvents(e, true);
21880 * Utility to stop event propagation and event default, if these
21881 * features are turned on.
21882 * @method stopEvent
21883 * @param {Event} e the event as returned by this.getEvent()
21886 stopEvent: function(e){
21887 if(this.stopPropagation) {
21888 e.stopPropagation();
21891 if (this.preventDefault) {
21892 e.preventDefault();
21897 * Internal function to clean up event handlers after the drag
21898 * operation is complete
21900 * @param {Event} e the event
21904 stopDrag: function(e) {
21905 // Fire the drag end event for the item that was dragged
21906 if (this.dragCurrent) {
21907 if (this.dragThreshMet) {
21908 this.dragCurrent.b4EndDrag(e);
21909 this.dragCurrent.endDrag(e);
21912 this.dragCurrent.onMouseUp(e);
21915 this.dragCurrent = null;
21916 this.dragOvers = {};
21920 * Internal function to handle the mousemove event. Will be invoked
21921 * from the context of the html element.
21923 * @TODO figure out what we can do about mouse events lost when the
21924 * user drags objects beyond the window boundary. Currently we can
21925 * detect this in internet explorer by verifying that the mouse is
21926 * down during the mousemove event. Firefox doesn't give us the
21927 * button state on the mousemove event.
21928 * @method handleMouseMove
21929 * @param {Event} e the event
21933 handleMouseMove: function(e) {
21934 if (! this.dragCurrent) {
21938 // var button = e.which || e.button;
21940 // check for IE mouseup outside of page boundary
21941 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21943 return this.handleMouseUp(e);
21946 if (!this.dragThreshMet) {
21947 var diffX = Math.abs(this.startX - e.getPageX());
21948 var diffY = Math.abs(this.startY - e.getPageY());
21949 if (diffX > this.clickPixelThresh ||
21950 diffY > this.clickPixelThresh) {
21951 this.startDrag(this.startX, this.startY);
21955 if (this.dragThreshMet) {
21956 this.dragCurrent.b4Drag(e);
21957 this.dragCurrent.onDrag(e);
21958 if(!this.dragCurrent.moveOnly){
21959 this.fireEvents(e, false);
21969 * Iterates over all of the DragDrop elements to find ones we are
21970 * hovering over or dropping on
21971 * @method fireEvents
21972 * @param {Event} e the event
21973 * @param {boolean} isDrop is this a drop op or a mouseover op?
21977 fireEvents: function(e, isDrop) {
21978 var dc = this.dragCurrent;
21980 // If the user did the mouse up outside of the window, we could
21981 // get here even though we have ended the drag.
21982 if (!dc || dc.isLocked()) {
21986 var pt = e.getPoint();
21988 // cache the previous dragOver array
21994 var enterEvts = [];
21996 // Check to see if the object(s) we were hovering over is no longer
21997 // being hovered over so we can fire the onDragOut event
21998 for (var i in this.dragOvers) {
22000 var ddo = this.dragOvers[i];
22002 if (! this.isTypeOfDD(ddo)) {
22006 if (! this.isOverTarget(pt, ddo, this.mode)) {
22007 outEvts.push( ddo );
22010 oldOvers[i] = true;
22011 delete this.dragOvers[i];
22014 for (var sGroup in dc.groups) {
22016 if ("string" != typeof sGroup) {
22020 for (i in this.ids[sGroup]) {
22021 var oDD = this.ids[sGroup][i];
22022 if (! this.isTypeOfDD(oDD)) {
22026 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22027 if (this.isOverTarget(pt, oDD, this.mode)) {
22028 // look for drop interactions
22030 dropEvts.push( oDD );
22031 // look for drag enter and drag over interactions
22034 // initial drag over: dragEnter fires
22035 if (!oldOvers[oDD.id]) {
22036 enterEvts.push( oDD );
22037 // subsequent drag overs: dragOver fires
22039 overEvts.push( oDD );
22042 this.dragOvers[oDD.id] = oDD;
22050 if (outEvts.length) {
22051 dc.b4DragOut(e, outEvts);
22052 dc.onDragOut(e, outEvts);
22055 if (enterEvts.length) {
22056 dc.onDragEnter(e, enterEvts);
22059 if (overEvts.length) {
22060 dc.b4DragOver(e, overEvts);
22061 dc.onDragOver(e, overEvts);
22064 if (dropEvts.length) {
22065 dc.b4DragDrop(e, dropEvts);
22066 dc.onDragDrop(e, dropEvts);
22070 // fire dragout events
22072 for (i=0, len=outEvts.length; i<len; ++i) {
22073 dc.b4DragOut(e, outEvts[i].id);
22074 dc.onDragOut(e, outEvts[i].id);
22077 // fire enter events
22078 for (i=0,len=enterEvts.length; i<len; ++i) {
22079 // dc.b4DragEnter(e, oDD.id);
22080 dc.onDragEnter(e, enterEvts[i].id);
22083 // fire over events
22084 for (i=0,len=overEvts.length; i<len; ++i) {
22085 dc.b4DragOver(e, overEvts[i].id);
22086 dc.onDragOver(e, overEvts[i].id);
22089 // fire drop events
22090 for (i=0, len=dropEvts.length; i<len; ++i) {
22091 dc.b4DragDrop(e, dropEvts[i].id);
22092 dc.onDragDrop(e, dropEvts[i].id);
22097 // notify about a drop that did not find a target
22098 if (isDrop && !dropEvts.length) {
22099 dc.onInvalidDrop(e);
22105 * Helper function for getting the best match from the list of drag
22106 * and drop objects returned by the drag and drop events when we are
22107 * in INTERSECT mode. It returns either the first object that the
22108 * cursor is over, or the object that has the greatest overlap with
22109 * the dragged element.
22110 * @method getBestMatch
22111 * @param {DragDrop[]} dds The array of drag and drop objects
22113 * @return {DragDrop} The best single match
22116 getBestMatch: function(dds) {
22118 // Return null if the input is not what we expect
22119 //if (!dds || !dds.length || dds.length == 0) {
22121 // If there is only one item, it wins
22122 //} else if (dds.length == 1) {
22124 var len = dds.length;
22129 // Loop through the targeted items
22130 for (var i=0; i<len; ++i) {
22132 // If the cursor is over the object, it wins. If the
22133 // cursor is over multiple matches, the first one we come
22135 if (dd.cursorIsOver) {
22138 // Otherwise the object with the most overlap wins
22141 winner.overlap.getArea() < dd.overlap.getArea()) {
22152 * Refreshes the cache of the top-left and bottom-right points of the
22153 * drag and drop objects in the specified group(s). This is in the
22154 * format that is stored in the drag and drop instance, so typical
22157 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22161 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22163 * @TODO this really should be an indexed array. Alternatively this
22164 * method could accept both.
22165 * @method refreshCache
22166 * @param {Object} groups an associative array of groups to refresh
22169 refreshCache: function(groups) {
22170 for (var sGroup in groups) {
22171 if ("string" != typeof sGroup) {
22174 for (var i in this.ids[sGroup]) {
22175 var oDD = this.ids[sGroup][i];
22177 if (this.isTypeOfDD(oDD)) {
22178 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22179 var loc = this.getLocation(oDD);
22181 this.locationCache[oDD.id] = loc;
22183 delete this.locationCache[oDD.id];
22184 // this will unregister the drag and drop object if
22185 // the element is not in a usable state
22194 * This checks to make sure an element exists and is in the DOM. The
22195 * main purpose is to handle cases where innerHTML is used to remove
22196 * drag and drop objects from the DOM. IE provides an 'unspecified
22197 * error' when trying to access the offsetParent of such an element
22199 * @param {HTMLElement} el the element to check
22200 * @return {boolean} true if the element looks usable
22203 verifyEl: function(el) {
22208 parent = el.offsetParent;
22211 parent = el.offsetParent;
22222 * Returns a Region object containing the drag and drop element's position
22223 * and size, including the padding configured for it
22224 * @method getLocation
22225 * @param {DragDrop} oDD the drag and drop object to get the
22227 * @return {Roo.lib.Region} a Region object representing the total area
22228 * the element occupies, including any padding
22229 * the instance is configured for.
22232 getLocation: function(oDD) {
22233 if (! this.isTypeOfDD(oDD)) {
22237 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22240 pos= Roo.lib.Dom.getXY(el);
22248 x2 = x1 + el.offsetWidth;
22250 y2 = y1 + el.offsetHeight;
22252 t = y1 - oDD.padding[0];
22253 r = x2 + oDD.padding[1];
22254 b = y2 + oDD.padding[2];
22255 l = x1 - oDD.padding[3];
22257 return new Roo.lib.Region( t, r, b, l );
22261 * Checks the cursor location to see if it over the target
22262 * @method isOverTarget
22263 * @param {Roo.lib.Point} pt The point to evaluate
22264 * @param {DragDrop} oTarget the DragDrop object we are inspecting
22265 * @return {boolean} true if the mouse is over the target
22269 isOverTarget: function(pt, oTarget, intersect) {
22270 // use cache if available
22271 var loc = this.locationCache[oTarget.id];
22272 if (!loc || !this.useCache) {
22273 loc = this.getLocation(oTarget);
22274 this.locationCache[oTarget.id] = loc;
22282 oTarget.cursorIsOver = loc.contains( pt );
22284 // DragDrop is using this as a sanity check for the initial mousedown
22285 // in this case we are done. In POINT mode, if the drag obj has no
22286 // contraints, we are also done. Otherwise we need to evaluate the
22287 // location of the target as related to the actual location of the
22288 // dragged element.
22289 var dc = this.dragCurrent;
22290 if (!dc || !dc.getTargetCoord ||
22291 (!intersect && !dc.constrainX && !dc.constrainY)) {
22292 return oTarget.cursorIsOver;
22295 oTarget.overlap = null;
22297 // Get the current location of the drag element, this is the
22298 // location of the mouse event less the delta that represents
22299 // where the original mousedown happened on the element. We
22300 // need to consider constraints and ticks as well.
22301 var pos = dc.getTargetCoord(pt.x, pt.y);
22303 var el = dc.getDragEl();
22304 var curRegion = new Roo.lib.Region( pos.y,
22305 pos.x + el.offsetWidth,
22306 pos.y + el.offsetHeight,
22309 var overlap = curRegion.intersect(loc);
22312 oTarget.overlap = overlap;
22313 return (intersect) ? true : oTarget.cursorIsOver;
22320 * unload event handler
22321 * @method _onUnload
22325 _onUnload: function(e, me) {
22326 Roo.dd.DragDropMgr.unregAll();
22330 * Cleans up the drag and drop events and objects.
22335 unregAll: function() {
22337 if (this.dragCurrent) {
22339 this.dragCurrent = null;
22342 this._execOnAll("unreg", []);
22344 for (i in this.elementCache) {
22345 delete this.elementCache[i];
22348 this.elementCache = {};
22353 * A cache of DOM elements
22354 * @property elementCache
22361 * Get the wrapper for the DOM element specified
22362 * @method getElWrapper
22363 * @param {String} id the id of the element to get
22364 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22366 * @deprecated This wrapper isn't that useful
22369 getElWrapper: function(id) {
22370 var oWrapper = this.elementCache[id];
22371 if (!oWrapper || !oWrapper.el) {
22372 oWrapper = this.elementCache[id] =
22373 new this.ElementWrapper(Roo.getDom(id));
22379 * Returns the actual DOM element
22380 * @method getElement
22381 * @param {String} id the id of the elment to get
22382 * @return {Object} The element
22383 * @deprecated use Roo.getDom instead
22386 getElement: function(id) {
22387 return Roo.getDom(id);
22391 * Returns the style property for the DOM element (i.e.,
22392 * document.getElById(id).style)
22394 * @param {String} id the id of the elment to get
22395 * @return {Object} The style property of the element
22396 * @deprecated use Roo.getDom instead
22399 getCss: function(id) {
22400 var el = Roo.getDom(id);
22401 return (el) ? el.style : null;
22405 * Inner class for cached elements
22406 * @class DragDropMgr.ElementWrapper
22411 ElementWrapper: function(el) {
22416 this.el = el || null;
22421 this.id = this.el && el.id;
22423 * A reference to the style property
22426 this.css = this.el && el.style;
22430 * Returns the X position of an html element
22432 * @param el the element for which to get the position
22433 * @return {int} the X coordinate
22435 * @deprecated use Roo.lib.Dom.getX instead
22438 getPosX: function(el) {
22439 return Roo.lib.Dom.getX(el);
22443 * Returns the Y position of an html element
22445 * @param el the element for which to get the position
22446 * @return {int} the Y coordinate
22447 * @deprecated use Roo.lib.Dom.getY instead
22450 getPosY: function(el) {
22451 return Roo.lib.Dom.getY(el);
22455 * Swap two nodes. In IE, we use the native method, for others we
22456 * emulate the IE behavior
22458 * @param n1 the first node to swap
22459 * @param n2 the other node to swap
22462 swapNode: function(n1, n2) {
22466 var p = n2.parentNode;
22467 var s = n2.nextSibling;
22470 p.insertBefore(n1, n2);
22471 } else if (n2 == n1.nextSibling) {
22472 p.insertBefore(n2, n1);
22474 n1.parentNode.replaceChild(n2, n1);
22475 p.insertBefore(n1, s);
22481 * Returns the current scroll position
22482 * @method getScroll
22486 getScroll: function () {
22487 var t, l, dde=document.documentElement, db=document.body;
22488 if (dde && (dde.scrollTop || dde.scrollLeft)) {
22490 l = dde.scrollLeft;
22497 return { top: t, left: l };
22501 * Returns the specified element style property
22503 * @param {HTMLElement} el the element
22504 * @param {string} styleProp the style property
22505 * @return {string} The value of the style property
22506 * @deprecated use Roo.lib.Dom.getStyle
22509 getStyle: function(el, styleProp) {
22510 return Roo.fly(el).getStyle(styleProp);
22514 * Gets the scrollTop
22515 * @method getScrollTop
22516 * @return {int} the document's scrollTop
22519 getScrollTop: function () { return this.getScroll().top; },
22522 * Gets the scrollLeft
22523 * @method getScrollLeft
22524 * @return {int} the document's scrollTop
22527 getScrollLeft: function () { return this.getScroll().left; },
22530 * Sets the x/y position of an element to the location of the
22533 * @param {HTMLElement} moveEl The element to move
22534 * @param {HTMLElement} targetEl The position reference element
22537 moveToEl: function (moveEl, targetEl) {
22538 var aCoord = Roo.lib.Dom.getXY(targetEl);
22539 Roo.lib.Dom.setXY(moveEl, aCoord);
22543 * Numeric array sort function
22544 * @method numericSort
22547 numericSort: function(a, b) { return (a - b); },
22551 * @property _timeoutCount
22558 * Trying to make the load order less important. Without this we get
22559 * an error if this file is loaded before the Event Utility.
22560 * @method _addListeners
22564 _addListeners: function() {
22565 var DDM = Roo.dd.DDM;
22566 if ( Roo.lib.Event && document ) {
22569 if (DDM._timeoutCount > 2000) {
22571 setTimeout(DDM._addListeners, 10);
22572 if (document && document.body) {
22573 DDM._timeoutCount += 1;
22580 * Recursively searches the immediate parent and all child nodes for
22581 * the handle element in order to determine wheter or not it was
22583 * @method handleWasClicked
22584 * @param node the html element to inspect
22587 handleWasClicked: function(node, id) {
22588 if (this.isHandle(id, node.id)) {
22591 // check to see if this is a text node child of the one we want
22592 var p = node.parentNode;
22595 if (this.isHandle(id, p.id)) {
22610 // shorter alias, save a few bytes
22611 Roo.dd.DDM = Roo.dd.DragDropMgr;
22612 Roo.dd.DDM._addListeners();
22616 * Ext JS Library 1.1.1
22617 * Copyright(c) 2006-2007, Ext JS, LLC.
22619 * Originally Released Under LGPL - original licence link has changed is not relivant.
22622 * <script type="text/javascript">
22627 * A DragDrop implementation where the linked element follows the
22628 * mouse cursor during a drag.
22629 * @extends Roo.dd.DragDrop
22631 * @param {String} id the id of the linked element
22632 * @param {String} sGroup the group of related DragDrop items
22633 * @param {object} config an object containing configurable attributes
22634 * Valid properties for DD:
22637 Roo.dd.DD = function(id, sGroup, config) {
22639 this.init(id, sGroup, config);
22643 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22646 * When set to true, the utility automatically tries to scroll the browser
22647 * window wehn a drag and drop element is dragged near the viewport boundary.
22648 * Defaults to true.
22655 * Sets the pointer offset to the distance between the linked element's top
22656 * left corner and the location the element was clicked
22657 * @method autoOffset
22658 * @param {int} iPageX the X coordinate of the click
22659 * @param {int} iPageY the Y coordinate of the click
22661 autoOffset: function(iPageX, iPageY) {
22662 var x = iPageX - this.startPageX;
22663 var y = iPageY - this.startPageY;
22664 this.setDelta(x, y);
22668 * Sets the pointer offset. You can call this directly to force the
22669 * offset to be in a particular location (e.g., pass in 0,0 to set it
22670 * to the center of the object)
22672 * @param {int} iDeltaX the distance from the left
22673 * @param {int} iDeltaY the distance from the top
22675 setDelta: function(iDeltaX, iDeltaY) {
22676 this.deltaX = iDeltaX;
22677 this.deltaY = iDeltaY;
22681 * Sets the drag element to the location of the mousedown or click event,
22682 * maintaining the cursor location relative to the location on the element
22683 * that was clicked. Override this if you want to place the element in a
22684 * location other than where the cursor is.
22685 * @method setDragElPos
22686 * @param {int} iPageX the X coordinate of the mousedown or drag event
22687 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22689 setDragElPos: function(iPageX, iPageY) {
22690 // the first time we do this, we are going to check to make sure
22691 // the element has css positioning
22693 var el = this.getDragEl();
22694 this.alignElWithMouse(el, iPageX, iPageY);
22698 * Sets the element to the location of the mousedown or click event,
22699 * maintaining the cursor location relative to the location on the element
22700 * that was clicked. Override this if you want to place the element in a
22701 * location other than where the cursor is.
22702 * @method alignElWithMouse
22703 * @param {HTMLElement} el the element to move
22704 * @param {int} iPageX the X coordinate of the mousedown or drag event
22705 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22707 alignElWithMouse: function(el, iPageX, iPageY) {
22708 var oCoord = this.getTargetCoord(iPageX, iPageY);
22709 var fly = el.dom ? el : Roo.fly(el);
22710 if (!this.deltaSetXY) {
22711 var aCoord = [oCoord.x, oCoord.y];
22713 var newLeft = fly.getLeft(true);
22714 var newTop = fly.getTop(true);
22715 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22717 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22720 this.cachePosition(oCoord.x, oCoord.y);
22721 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22726 * Saves the most recent position so that we can reset the constraints and
22727 * tick marks on-demand. We need to know this so that we can calculate the
22728 * number of pixels the element is offset from its original position.
22729 * @method cachePosition
22730 * @param iPageX the current x position (optional, this just makes it so we
22731 * don't have to look it up again)
22732 * @param iPageY the current y position (optional, this just makes it so we
22733 * don't have to look it up again)
22735 cachePosition: function(iPageX, iPageY) {
22737 this.lastPageX = iPageX;
22738 this.lastPageY = iPageY;
22740 var aCoord = Roo.lib.Dom.getXY(this.getEl());
22741 this.lastPageX = aCoord[0];
22742 this.lastPageY = aCoord[1];
22747 * Auto-scroll the window if the dragged object has been moved beyond the
22748 * visible window boundary.
22749 * @method autoScroll
22750 * @param {int} x the drag element's x position
22751 * @param {int} y the drag element's y position
22752 * @param {int} h the height of the drag element
22753 * @param {int} w the width of the drag element
22756 autoScroll: function(x, y, h, w) {
22759 // The client height
22760 var clientH = Roo.lib.Dom.getViewWidth();
22762 // The client width
22763 var clientW = Roo.lib.Dom.getViewHeight();
22765 // The amt scrolled down
22766 var st = this.DDM.getScrollTop();
22768 // The amt scrolled right
22769 var sl = this.DDM.getScrollLeft();
22771 // Location of the bottom of the element
22774 // Location of the right of the element
22777 // The distance from the cursor to the bottom of the visible area,
22778 // adjusted so that we don't scroll if the cursor is beyond the
22779 // element drag constraints
22780 var toBot = (clientH + st - y - this.deltaY);
22782 // The distance from the cursor to the right of the visible area
22783 var toRight = (clientW + sl - x - this.deltaX);
22786 // How close to the edge the cursor must be before we scroll
22787 // var thresh = (document.all) ? 100 : 40;
22790 // How many pixels to scroll per autoscroll op. This helps to reduce
22791 // clunky scrolling. IE is more sensitive about this ... it needs this
22792 // value to be higher.
22793 var scrAmt = (document.all) ? 80 : 30;
22795 // Scroll down if we are near the bottom of the visible page and the
22796 // obj extends below the crease
22797 if ( bot > clientH && toBot < thresh ) {
22798 window.scrollTo(sl, st + scrAmt);
22801 // Scroll up if the window is scrolled down and the top of the object
22802 // goes above the top border
22803 if ( y < st && st > 0 && y - st < thresh ) {
22804 window.scrollTo(sl, st - scrAmt);
22807 // Scroll right if the obj is beyond the right border and the cursor is
22808 // near the border.
22809 if ( right > clientW && toRight < thresh ) {
22810 window.scrollTo(sl + scrAmt, st);
22813 // Scroll left if the window has been scrolled to the right and the obj
22814 // extends past the left border
22815 if ( x < sl && sl > 0 && x - sl < thresh ) {
22816 window.scrollTo(sl - scrAmt, st);
22822 * Finds the location the element should be placed if we want to move
22823 * it to where the mouse location less the click offset would place us.
22824 * @method getTargetCoord
22825 * @param {int} iPageX the X coordinate of the click
22826 * @param {int} iPageY the Y coordinate of the click
22827 * @return an object that contains the coordinates (Object.x and Object.y)
22830 getTargetCoord: function(iPageX, iPageY) {
22833 var x = iPageX - this.deltaX;
22834 var y = iPageY - this.deltaY;
22836 if (this.constrainX) {
22837 if (x < this.minX) { x = this.minX; }
22838 if (x > this.maxX) { x = this.maxX; }
22841 if (this.constrainY) {
22842 if (y < this.minY) { y = this.minY; }
22843 if (y > this.maxY) { y = this.maxY; }
22846 x = this.getTick(x, this.xTicks);
22847 y = this.getTick(y, this.yTicks);
22854 * Sets up config options specific to this class. Overrides
22855 * Roo.dd.DragDrop, but all versions of this method through the
22856 * inheritance chain are called
22858 applyConfig: function() {
22859 Roo.dd.DD.superclass.applyConfig.call(this);
22860 this.scroll = (this.config.scroll !== false);
22864 * Event that fires prior to the onMouseDown event. Overrides
22867 b4MouseDown: function(e) {
22868 // this.resetConstraints();
22869 this.autoOffset(e.getPageX(),
22874 * Event that fires prior to the onDrag event. Overrides
22877 b4Drag: function(e) {
22878 this.setDragElPos(e.getPageX(),
22882 toString: function() {
22883 return ("DD " + this.id);
22886 //////////////////////////////////////////////////////////////////////////
22887 // Debugging ygDragDrop events that can be overridden
22888 //////////////////////////////////////////////////////////////////////////
22890 startDrag: function(x, y) {
22893 onDrag: function(e) {
22896 onDragEnter: function(e, id) {
22899 onDragOver: function(e, id) {
22902 onDragOut: function(e, id) {
22905 onDragDrop: function(e, id) {
22908 endDrag: function(e) {
22915 * Ext JS Library 1.1.1
22916 * Copyright(c) 2006-2007, Ext JS, LLC.
22918 * Originally Released Under LGPL - original licence link has changed is not relivant.
22921 * <script type="text/javascript">
22925 * @class Roo.dd.DDProxy
22926 * A DragDrop implementation that inserts an empty, bordered div into
22927 * the document that follows the cursor during drag operations. At the time of
22928 * the click, the frame div is resized to the dimensions of the linked html
22929 * element, and moved to the exact location of the linked element.
22931 * References to the "frame" element refer to the single proxy element that
22932 * was created to be dragged in place of all DDProxy elements on the
22935 * @extends Roo.dd.DD
22937 * @param {String} id the id of the linked html element
22938 * @param {String} sGroup the group of related DragDrop objects
22939 * @param {object} config an object containing configurable attributes
22940 * Valid properties for DDProxy in addition to those in DragDrop:
22941 * resizeFrame, centerFrame, dragElId
22943 Roo.dd.DDProxy = function(id, sGroup, config) {
22945 this.init(id, sGroup, config);
22951 * The default drag frame div id
22952 * @property Roo.dd.DDProxy.dragElId
22956 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22958 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22961 * By default we resize the drag frame to be the same size as the element
22962 * we want to drag (this is to get the frame effect). We can turn it off
22963 * if we want a different behavior.
22964 * @property resizeFrame
22970 * By default the frame is positioned exactly where the drag element is, so
22971 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
22972 * you do not have constraints on the obj is to have the drag frame centered
22973 * around the cursor. Set centerFrame to true for this effect.
22974 * @property centerFrame
22977 centerFrame: false,
22980 * Creates the proxy element if it does not yet exist
22981 * @method createFrame
22983 createFrame: function() {
22985 var body = document.body;
22987 if (!body || !body.firstChild) {
22988 setTimeout( function() { self.createFrame(); }, 50 );
22992 var div = this.getDragEl();
22995 div = document.createElement("div");
22996 div.id = this.dragElId;
22999 s.position = "absolute";
23000 s.visibility = "hidden";
23002 s.border = "2px solid #aaa";
23005 // appendChild can blow up IE if invoked prior to the window load event
23006 // while rendering a table. It is possible there are other scenarios
23007 // that would cause this to happen as well.
23008 body.insertBefore(div, body.firstChild);
23013 * Initialization for the drag frame element. Must be called in the
23014 * constructor of all subclasses
23015 * @method initFrame
23017 initFrame: function() {
23018 this.createFrame();
23021 applyConfig: function() {
23022 Roo.dd.DDProxy.superclass.applyConfig.call(this);
23024 this.resizeFrame = (this.config.resizeFrame !== false);
23025 this.centerFrame = (this.config.centerFrame);
23026 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23030 * Resizes the drag frame to the dimensions of the clicked object, positions
23031 * it over the object, and finally displays it
23032 * @method showFrame
23033 * @param {int} iPageX X click position
23034 * @param {int} iPageY Y click position
23037 showFrame: function(iPageX, iPageY) {
23038 var el = this.getEl();
23039 var dragEl = this.getDragEl();
23040 var s = dragEl.style;
23042 this._resizeProxy();
23044 if (this.centerFrame) {
23045 this.setDelta( Math.round(parseInt(s.width, 10)/2),
23046 Math.round(parseInt(s.height, 10)/2) );
23049 this.setDragElPos(iPageX, iPageY);
23051 Roo.fly(dragEl).show();
23055 * The proxy is automatically resized to the dimensions of the linked
23056 * element when a drag is initiated, unless resizeFrame is set to false
23057 * @method _resizeProxy
23060 _resizeProxy: function() {
23061 if (this.resizeFrame) {
23062 var el = this.getEl();
23063 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23067 // overrides Roo.dd.DragDrop
23068 b4MouseDown: function(e) {
23069 var x = e.getPageX();
23070 var y = e.getPageY();
23071 this.autoOffset(x, y);
23072 this.setDragElPos(x, y);
23075 // overrides Roo.dd.DragDrop
23076 b4StartDrag: function(x, y) {
23077 // show the drag frame
23078 this.showFrame(x, y);
23081 // overrides Roo.dd.DragDrop
23082 b4EndDrag: function(e) {
23083 Roo.fly(this.getDragEl()).hide();
23086 // overrides Roo.dd.DragDrop
23087 // By default we try to move the element to the last location of the frame.
23088 // This is so that the default behavior mirrors that of Roo.dd.DD.
23089 endDrag: function(e) {
23091 var lel = this.getEl();
23092 var del = this.getDragEl();
23094 // Show the drag frame briefly so we can get its position
23095 del.style.visibility = "";
23098 // Hide the linked element before the move to get around a Safari
23100 lel.style.visibility = "hidden";
23101 Roo.dd.DDM.moveToEl(lel, del);
23102 del.style.visibility = "hidden";
23103 lel.style.visibility = "";
23108 beforeMove : function(){
23112 afterDrag : function(){
23116 toString: function() {
23117 return ("DDProxy " + this.id);
23123 * Ext JS Library 1.1.1
23124 * Copyright(c) 2006-2007, Ext JS, LLC.
23126 * Originally Released Under LGPL - original licence link has changed is not relivant.
23129 * <script type="text/javascript">
23133 * @class Roo.dd.DDTarget
23134 * A DragDrop implementation that does not move, but can be a drop
23135 * target. You would get the same result by simply omitting implementation
23136 * for the event callbacks, but this way we reduce the processing cost of the
23137 * event listener and the callbacks.
23138 * @extends Roo.dd.DragDrop
23140 * @param {String} id the id of the element that is a drop target
23141 * @param {String} sGroup the group of related DragDrop objects
23142 * @param {object} config an object containing configurable attributes
23143 * Valid properties for DDTarget in addition to those in
23147 Roo.dd.DDTarget = function(id, sGroup, config) {
23149 this.initTarget(id, sGroup, config);
23151 if (config && (config.listeners || config.events)) {
23152 Roo.dd.DragDrop.superclass.constructor.call(this, {
23153 listeners : config.listeners || {},
23154 events : config.events || {}
23159 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23160 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23161 toString: function() {
23162 return ("DDTarget " + this.id);
23167 * Ext JS Library 1.1.1
23168 * Copyright(c) 2006-2007, Ext JS, LLC.
23170 * Originally Released Under LGPL - original licence link has changed is not relivant.
23173 * <script type="text/javascript">
23178 * @class Roo.dd.ScrollManager
23179 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23180 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23183 Roo.dd.ScrollManager = function(){
23184 var ddm = Roo.dd.DragDropMgr;
23191 var onStop = function(e){
23196 var triggerRefresh = function(){
23197 if(ddm.dragCurrent){
23198 ddm.refreshCache(ddm.dragCurrent.groups);
23202 var doScroll = function(){
23203 if(ddm.dragCurrent){
23204 var dds = Roo.dd.ScrollManager;
23206 if(proc.el.scroll(proc.dir, dds.increment)){
23210 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23215 var clearProc = function(){
23217 clearInterval(proc.id);
23224 var startProc = function(el, dir){
23225 Roo.log('scroll startproc');
23229 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23232 var onFire = function(e, isDrop){
23234 if(isDrop || !ddm.dragCurrent){ return; }
23235 var dds = Roo.dd.ScrollManager;
23236 if(!dragEl || dragEl != ddm.dragCurrent){
23237 dragEl = ddm.dragCurrent;
23238 // refresh regions on drag start
23239 dds.refreshCache();
23242 var xy = Roo.lib.Event.getXY(e);
23243 var pt = new Roo.lib.Point(xy[0], xy[1]);
23244 for(var id in els){
23245 var el = els[id], r = el._region;
23246 if(r && r.contains(pt) && el.isScrollable()){
23247 if(r.bottom - pt.y <= dds.thresh){
23249 startProc(el, "down");
23252 }else if(r.right - pt.x <= dds.thresh){
23254 startProc(el, "left");
23257 }else if(pt.y - r.top <= dds.thresh){
23259 startProc(el, "up");
23262 }else if(pt.x - r.left <= dds.thresh){
23264 startProc(el, "right");
23273 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23274 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23278 * Registers new overflow element(s) to auto scroll
23279 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23281 register : function(el){
23282 if(el instanceof Array){
23283 for(var i = 0, len = el.length; i < len; i++) {
23284 this.register(el[i]);
23290 Roo.dd.ScrollManager.els = els;
23294 * Unregisters overflow element(s) so they are no longer scrolled
23295 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23297 unregister : function(el){
23298 if(el instanceof Array){
23299 for(var i = 0, len = el.length; i < len; i++) {
23300 this.unregister(el[i]);
23309 * The number of pixels from the edge of a container the pointer needs to be to
23310 * trigger scrolling (defaults to 25)
23316 * The number of pixels to scroll in each scroll increment (defaults to 50)
23322 * The frequency of scrolls in milliseconds (defaults to 500)
23328 * True to animate the scroll (defaults to true)
23334 * The animation duration in seconds -
23335 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23341 * Manually trigger a cache refresh.
23343 refreshCache : function(){
23344 for(var id in els){
23345 if(typeof els[id] == 'object'){ // for people extending the object prototype
23346 els[id]._region = els[id].getRegion();
23353 * Ext JS Library 1.1.1
23354 * Copyright(c) 2006-2007, Ext JS, LLC.
23356 * Originally Released Under LGPL - original licence link has changed is not relivant.
23359 * <script type="text/javascript">
23364 * @class Roo.dd.Registry
23365 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
23366 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23369 Roo.dd.Registry = function(){
23372 var autoIdSeed = 0;
23374 var getId = function(el, autogen){
23375 if(typeof el == "string"){
23379 if(!id && autogen !== false){
23380 id = "roodd-" + (++autoIdSeed);
23388 * Register a drag drop element
23389 * @param {String|HTMLElement} element The id or DOM node to register
23390 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23391 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
23392 * knows how to interpret, plus there are some specific properties known to the Registry that should be
23393 * populated in the data object (if applicable):
23395 Value Description<br />
23396 --------- ------------------------------------------<br />
23397 handles Array of DOM nodes that trigger dragging<br />
23398 for the element being registered<br />
23399 isHandle True if the element passed in triggers<br />
23400 dragging itself, else false
23403 register : function(el, data){
23405 if(typeof el == "string"){
23406 el = document.getElementById(el);
23409 elements[getId(el)] = data;
23410 if(data.isHandle !== false){
23411 handles[data.ddel.id] = data;
23414 var hs = data.handles;
23415 for(var i = 0, len = hs.length; i < len; i++){
23416 handles[getId(hs[i])] = data;
23422 * Unregister a drag drop element
23423 * @param {String|HTMLElement} element The id or DOM node to unregister
23425 unregister : function(el){
23426 var id = getId(el, false);
23427 var data = elements[id];
23429 delete elements[id];
23431 var hs = data.handles;
23432 for(var i = 0, len = hs.length; i < len; i++){
23433 delete handles[getId(hs[i], false)];
23440 * Returns the handle registered for a DOM Node by id
23441 * @param {String|HTMLElement} id The DOM node or id to look up
23442 * @return {Object} handle The custom handle data
23444 getHandle : function(id){
23445 if(typeof id != "string"){ // must be element?
23448 return handles[id];
23452 * Returns the handle that is registered for the DOM node that is the target of the event
23453 * @param {Event} e The event
23454 * @return {Object} handle The custom handle data
23456 getHandleFromEvent : function(e){
23457 var t = Roo.lib.Event.getTarget(e);
23458 return t ? handles[t.id] : null;
23462 * Returns a custom data object that is registered for a DOM node by id
23463 * @param {String|HTMLElement} id The DOM node or id to look up
23464 * @return {Object} data The custom data
23466 getTarget : function(id){
23467 if(typeof id != "string"){ // must be element?
23470 return elements[id];
23474 * Returns a custom data object that is registered for the DOM node that is the target of the event
23475 * @param {Event} e The event
23476 * @return {Object} data The custom data
23478 getTargetFromEvent : function(e){
23479 var t = Roo.lib.Event.getTarget(e);
23480 return t ? elements[t.id] || handles[t.id] : null;
23485 * Ext JS Library 1.1.1
23486 * Copyright(c) 2006-2007, Ext JS, LLC.
23488 * Originally Released Under LGPL - original licence link has changed is not relivant.
23491 * <script type="text/javascript">
23496 * @class Roo.dd.StatusProxy
23497 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
23498 * default drag proxy used by all Roo.dd components.
23500 * @param {Object} config
23502 Roo.dd.StatusProxy = function(config){
23503 Roo.apply(this, config);
23504 this.id = this.id || Roo.id();
23505 this.el = new Roo.Layer({
23507 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23508 {tag: "div", cls: "x-dd-drop-icon"},
23509 {tag: "div", cls: "x-dd-drag-ghost"}
23512 shadow: !config || config.shadow !== false
23514 this.ghost = Roo.get(this.el.dom.childNodes[1]);
23515 this.dropStatus = this.dropNotAllowed;
23518 Roo.dd.StatusProxy.prototype = {
23520 * @cfg {String} dropAllowed
23521 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23523 dropAllowed : "x-dd-drop-ok",
23525 * @cfg {String} dropNotAllowed
23526 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23528 dropNotAllowed : "x-dd-drop-nodrop",
23531 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23532 * over the current target element.
23533 * @param {String} cssClass The css class for the new drop status indicator image
23535 setStatus : function(cssClass){
23536 cssClass = cssClass || this.dropNotAllowed;
23537 if(this.dropStatus != cssClass){
23538 this.el.replaceClass(this.dropStatus, cssClass);
23539 this.dropStatus = cssClass;
23544 * Resets the status indicator to the default dropNotAllowed value
23545 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23547 reset : function(clearGhost){
23548 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23549 this.dropStatus = this.dropNotAllowed;
23551 this.ghost.update("");
23556 * Updates the contents of the ghost element
23557 * @param {String} html The html that will replace the current innerHTML of the ghost element
23559 update : function(html){
23560 if(typeof html == "string"){
23561 this.ghost.update(html);
23563 this.ghost.update("");
23564 html.style.margin = "0";
23565 this.ghost.dom.appendChild(html);
23567 // ensure float = none set?? cant remember why though.
23568 var el = this.ghost.dom.firstChild;
23570 Roo.fly(el).setStyle('float', 'none');
23575 * Returns the underlying proxy {@link Roo.Layer}
23576 * @return {Roo.Layer} el
23578 getEl : function(){
23583 * Returns the ghost element
23584 * @return {Roo.Element} el
23586 getGhost : function(){
23592 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23594 hide : function(clear){
23602 * Stops the repair animation if it's currently running
23605 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23611 * Displays this proxy
23618 * Force the Layer to sync its shadow and shim positions to the element
23625 * Causes the proxy to return to its position of origin via an animation. Should be called after an
23626 * invalid drop operation by the item being dragged.
23627 * @param {Array} xy The XY position of the element ([x, y])
23628 * @param {Function} callback The function to call after the repair is complete
23629 * @param {Object} scope The scope in which to execute the callback
23631 repair : function(xy, callback, scope){
23632 this.callback = callback;
23633 this.scope = scope;
23634 if(xy && this.animRepair !== false){
23635 this.el.addClass("x-dd-drag-repair");
23636 this.el.hideUnders(true);
23637 this.anim = this.el.shift({
23638 duration: this.repairDuration || .5,
23642 callback: this.afterRepair,
23646 this.afterRepair();
23651 afterRepair : function(){
23653 if(typeof this.callback == "function"){
23654 this.callback.call(this.scope || this);
23656 this.callback = null;
23661 * Ext JS Library 1.1.1
23662 * Copyright(c) 2006-2007, Ext JS, LLC.
23664 * Originally Released Under LGPL - original licence link has changed is not relivant.
23667 * <script type="text/javascript">
23671 * @class Roo.dd.DragSource
23672 * @extends Roo.dd.DDProxy
23673 * A simple class that provides the basic implementation needed to make any element draggable.
23675 * @param {String/HTMLElement/Element} el The container element
23676 * @param {Object} config
23678 Roo.dd.DragSource = function(el, config){
23679 this.el = Roo.get(el);
23680 this.dragData = {};
23682 Roo.apply(this, config);
23685 this.proxy = new Roo.dd.StatusProxy();
23688 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23689 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23691 this.dragging = false;
23694 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23696 * @cfg {String} dropAllowed
23697 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23699 dropAllowed : "x-dd-drop-ok",
23701 * @cfg {String} dropNotAllowed
23702 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23704 dropNotAllowed : "x-dd-drop-nodrop",
23707 * Returns the data object associated with this drag source
23708 * @return {Object} data An object containing arbitrary data
23710 getDragData : function(e){
23711 return this.dragData;
23715 onDragEnter : function(e, id){
23716 var target = Roo.dd.DragDropMgr.getDDById(id);
23717 this.cachedTarget = target;
23718 if(this.beforeDragEnter(target, e, id) !== false){
23719 if(target.isNotifyTarget){
23720 var status = target.notifyEnter(this, e, this.dragData);
23721 this.proxy.setStatus(status);
23723 this.proxy.setStatus(this.dropAllowed);
23726 if(this.afterDragEnter){
23728 * An empty function by default, but provided so that you can perform a custom action
23729 * when the dragged item enters the drop target by providing an implementation.
23730 * @param {Roo.dd.DragDrop} target The drop target
23731 * @param {Event} e The event object
23732 * @param {String} id The id of the dragged element
23733 * @method afterDragEnter
23735 this.afterDragEnter(target, e, id);
23741 * An empty function by default, but provided so that you can perform a custom action
23742 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23743 * @param {Roo.dd.DragDrop} target The drop target
23744 * @param {Event} e The event object
23745 * @param {String} id The id of the dragged element
23746 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23748 beforeDragEnter : function(target, e, id){
23753 alignElWithMouse: function() {
23754 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23759 onDragOver : function(e, id){
23760 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23761 if(this.beforeDragOver(target, e, id) !== false){
23762 if(target.isNotifyTarget){
23763 var status = target.notifyOver(this, e, this.dragData);
23764 this.proxy.setStatus(status);
23767 if(this.afterDragOver){
23769 * An empty function by default, but provided so that you can perform a custom action
23770 * while the dragged item is over the drop target by providing an implementation.
23771 * @param {Roo.dd.DragDrop} target The drop target
23772 * @param {Event} e The event object
23773 * @param {String} id The id of the dragged element
23774 * @method afterDragOver
23776 this.afterDragOver(target, e, id);
23782 * An empty function by default, but provided so that you can perform a custom action
23783 * while the dragged item is over the drop target and optionally cancel the onDragOver.
23784 * @param {Roo.dd.DragDrop} target The drop target
23785 * @param {Event} e The event object
23786 * @param {String} id The id of the dragged element
23787 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23789 beforeDragOver : function(target, e, id){
23794 onDragOut : function(e, id){
23795 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23796 if(this.beforeDragOut(target, e, id) !== false){
23797 if(target.isNotifyTarget){
23798 target.notifyOut(this, e, this.dragData);
23800 this.proxy.reset();
23801 if(this.afterDragOut){
23803 * An empty function by default, but provided so that you can perform a custom action
23804 * after the dragged item is dragged out of the target without dropping.
23805 * @param {Roo.dd.DragDrop} target The drop target
23806 * @param {Event} e The event object
23807 * @param {String} id The id of the dragged element
23808 * @method afterDragOut
23810 this.afterDragOut(target, e, id);
23813 this.cachedTarget = null;
23817 * An empty function by default, but provided so that you can perform a custom action before the dragged
23818 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23819 * @param {Roo.dd.DragDrop} target The drop target
23820 * @param {Event} e The event object
23821 * @param {String} id The id of the dragged element
23822 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23824 beforeDragOut : function(target, e, id){
23829 onDragDrop : function(e, id){
23830 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23831 if(this.beforeDragDrop(target, e, id) !== false){
23832 if(target.isNotifyTarget){
23833 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23834 this.onValidDrop(target, e, id);
23836 this.onInvalidDrop(target, e, id);
23839 this.onValidDrop(target, e, id);
23842 if(this.afterDragDrop){
23844 * An empty function by default, but provided so that you can perform a custom action
23845 * after a valid drag drop has occurred by providing an implementation.
23846 * @param {Roo.dd.DragDrop} target The drop target
23847 * @param {Event} e The event object
23848 * @param {String} id The id of the dropped element
23849 * @method afterDragDrop
23851 this.afterDragDrop(target, e, id);
23854 delete this.cachedTarget;
23858 * An empty function by default, but provided so that you can perform a custom action before the dragged
23859 * item is dropped onto the target and optionally cancel the onDragDrop.
23860 * @param {Roo.dd.DragDrop} target The drop target
23861 * @param {Event} e The event object
23862 * @param {String} id The id of the dragged element
23863 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23865 beforeDragDrop : function(target, e, id){
23870 onValidDrop : function(target, e, id){
23872 if(this.afterValidDrop){
23874 * An empty function by default, but provided so that you can perform a custom action
23875 * after a valid drop has occurred by providing an implementation.
23876 * @param {Object} target The target DD
23877 * @param {Event} e The event object
23878 * @param {String} id The id of the dropped element
23879 * @method afterInvalidDrop
23881 this.afterValidDrop(target, e, id);
23886 getRepairXY : function(e, data){
23887 return this.el.getXY();
23891 onInvalidDrop : function(target, e, id){
23892 this.beforeInvalidDrop(target, e, id);
23893 if(this.cachedTarget){
23894 if(this.cachedTarget.isNotifyTarget){
23895 this.cachedTarget.notifyOut(this, e, this.dragData);
23897 this.cacheTarget = null;
23899 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23901 if(this.afterInvalidDrop){
23903 * An empty function by default, but provided so that you can perform a custom action
23904 * after an invalid drop has occurred by providing an implementation.
23905 * @param {Event} e The event object
23906 * @param {String} id The id of the dropped element
23907 * @method afterInvalidDrop
23909 this.afterInvalidDrop(e, id);
23914 afterRepair : function(){
23916 this.el.highlight(this.hlColor || "c3daf9");
23918 this.dragging = false;
23922 * An empty function by default, but provided so that you can perform a custom action after an invalid
23923 * drop has occurred.
23924 * @param {Roo.dd.DragDrop} target The drop target
23925 * @param {Event} e The event object
23926 * @param {String} id The id of the dragged element
23927 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23929 beforeInvalidDrop : function(target, e, id){
23934 handleMouseDown : function(e){
23935 if(this.dragging) {
23938 var data = this.getDragData(e);
23939 if(data && this.onBeforeDrag(data, e) !== false){
23940 this.dragData = data;
23942 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23947 * An empty function by default, but provided so that you can perform a custom action before the initial
23948 * drag event begins and optionally cancel it.
23949 * @param {Object} data An object containing arbitrary data to be shared with drop targets
23950 * @param {Event} e The event object
23951 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23953 onBeforeDrag : function(data, e){
23958 * An empty function by default, but provided so that you can perform a custom action once the initial
23959 * drag event has begun. The drag cannot be canceled from this function.
23960 * @param {Number} x The x position of the click on the dragged object
23961 * @param {Number} y The y position of the click on the dragged object
23963 onStartDrag : Roo.emptyFn,
23965 // private - YUI override
23966 startDrag : function(x, y){
23967 this.proxy.reset();
23968 this.dragging = true;
23969 this.proxy.update("");
23970 this.onInitDrag(x, y);
23975 onInitDrag : function(x, y){
23976 var clone = this.el.dom.cloneNode(true);
23977 clone.id = Roo.id(); // prevent duplicate ids
23978 this.proxy.update(clone);
23979 this.onStartDrag(x, y);
23984 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23985 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23987 getProxy : function(){
23992 * Hides the drag source's {@link Roo.dd.StatusProxy}
23994 hideProxy : function(){
23996 this.proxy.reset(true);
23997 this.dragging = false;
24001 triggerCacheRefresh : function(){
24002 Roo.dd.DDM.refreshCache(this.groups);
24005 // private - override to prevent hiding
24006 b4EndDrag: function(e) {
24009 // private - override to prevent moving
24010 endDrag : function(e){
24011 this.onEndDrag(this.dragData, e);
24015 onEndDrag : function(data, e){
24018 // private - pin to cursor
24019 autoOffset : function(x, y) {
24020 this.setDelta(-12, -20);
24024 * Ext JS Library 1.1.1
24025 * Copyright(c) 2006-2007, Ext JS, LLC.
24027 * Originally Released Under LGPL - original licence link has changed is not relivant.
24030 * <script type="text/javascript">
24035 * @class Roo.dd.DropTarget
24036 * @extends Roo.dd.DDTarget
24037 * A simple class that provides the basic implementation needed to make any element a drop target that can have
24038 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
24040 * @param {String/HTMLElement/Element} el The container element
24041 * @param {Object} config
24043 Roo.dd.DropTarget = function(el, config){
24044 this.el = Roo.get(el);
24046 var listeners = false; ;
24047 if (config && config.listeners) {
24048 listeners= config.listeners;
24049 delete config.listeners;
24051 Roo.apply(this, config);
24053 if(this.containerScroll){
24054 Roo.dd.ScrollManager.register(this.el);
24058 * @scope Roo.dd.DropTarget
24063 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24064 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
24065 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
24067 * IMPORTANT : it should set this.valid to true|false
24069 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24070 * @param {Event} e The event
24071 * @param {Object} data An object containing arbitrary data supplied by the drag source
24077 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24078 * This method will be called on every mouse movement while the drag source is over the drop target.
24079 * This default implementation simply returns the dropAllowed config value.
24081 * IMPORTANT : it should set this.valid to true|false
24083 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24084 * @param {Event} e The event
24085 * @param {Object} data An object containing arbitrary data supplied by the drag source
24091 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24092 * out of the target without dropping. This default implementation simply removes the CSS class specified by
24093 * overClass (if any) from the drop element.
24096 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24097 * @param {Event} e The event
24098 * @param {Object} data An object containing arbitrary data supplied by the drag source
24104 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24105 * been dropped on it. This method has no default implementation and returns false, so you must provide an
24106 * implementation that does something to process the drop event and returns true so that the drag source's
24107 * repair action does not run.
24109 * IMPORTANT : it should set this.success
24111 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24112 * @param {Event} e The event
24113 * @param {Object} data An object containing arbitrary data supplied by the drag source
24119 Roo.dd.DropTarget.superclass.constructor.call( this,
24121 this.ddGroup || this.group,
24124 listeners : listeners || {}
24132 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24134 * @cfg {String} overClass
24135 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24138 * @cfg {String} ddGroup
24139 * The drag drop group to handle drop events for
24143 * @cfg {String} dropAllowed
24144 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24146 dropAllowed : "x-dd-drop-ok",
24148 * @cfg {String} dropNotAllowed
24149 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24151 dropNotAllowed : "x-dd-drop-nodrop",
24153 * @cfg {boolean} success
24154 * set this after drop listener..
24158 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24159 * if the drop point is valid for over/enter..
24166 isNotifyTarget : true,
24171 notifyEnter : function(dd, e, data)
24174 this.fireEvent('enter', dd, e, data);
24175 if(this.overClass){
24176 this.el.addClass(this.overClass);
24178 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24179 this.valid ? this.dropAllowed : this.dropNotAllowed
24186 notifyOver : function(dd, e, data)
24189 this.fireEvent('over', dd, e, data);
24190 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24191 this.valid ? this.dropAllowed : this.dropNotAllowed
24198 notifyOut : function(dd, e, data)
24200 this.fireEvent('out', dd, e, data);
24201 if(this.overClass){
24202 this.el.removeClass(this.overClass);
24209 notifyDrop : function(dd, e, data)
24211 this.success = false;
24212 this.fireEvent('drop', dd, e, data);
24213 return this.success;
24217 * Ext JS Library 1.1.1
24218 * Copyright(c) 2006-2007, Ext JS, LLC.
24220 * Originally Released Under LGPL - original licence link has changed is not relivant.
24223 * <script type="text/javascript">
24228 * @class Roo.dd.DragZone
24229 * @extends Roo.dd.DragSource
24230 * This class provides a container DD instance that proxies for multiple child node sources.<br />
24231 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24233 * @param {String/HTMLElement/Element} el The container element
24234 * @param {Object} config
24236 Roo.dd.DragZone = function(el, config){
24237 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24238 if(this.containerScroll){
24239 Roo.dd.ScrollManager.register(this.el);
24243 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24245 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24246 * for auto scrolling during drag operations.
24249 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24250 * method after a failed drop (defaults to "c3daf9" - light blue)
24254 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24255 * for a valid target to drag based on the mouse down. Override this method
24256 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24257 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24258 * @param {EventObject} e The mouse down event
24259 * @return {Object} The dragData
24261 getDragData : function(e){
24262 return Roo.dd.Registry.getHandleFromEvent(e);
24266 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24267 * this.dragData.ddel
24268 * @param {Number} x The x position of the click on the dragged object
24269 * @param {Number} y The y position of the click on the dragged object
24270 * @return {Boolean} true to continue the drag, false to cancel
24272 onInitDrag : function(x, y){
24273 this.proxy.update(this.dragData.ddel.cloneNode(true));
24274 this.onStartDrag(x, y);
24279 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
24281 afterRepair : function(){
24283 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24285 this.dragging = false;
24289 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24290 * the XY of this.dragData.ddel
24291 * @param {EventObject} e The mouse up event
24292 * @return {Array} The xy location (e.g. [100, 200])
24294 getRepairXY : function(e){
24295 return Roo.Element.fly(this.dragData.ddel).getXY();
24299 * Ext JS Library 1.1.1
24300 * Copyright(c) 2006-2007, Ext JS, LLC.
24302 * Originally Released Under LGPL - original licence link has changed is not relivant.
24305 * <script type="text/javascript">
24308 * @class Roo.dd.DropZone
24309 * @extends Roo.dd.DropTarget
24310 * This class provides a container DD instance that proxies for multiple child node targets.<br />
24311 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24313 * @param {String/HTMLElement/Element} el The container element
24314 * @param {Object} config
24316 Roo.dd.DropZone = function(el, config){
24317 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24320 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24322 * Returns a custom data object associated with the DOM node that is the target of the event. By default
24323 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24324 * provide your own custom lookup.
24325 * @param {Event} e The event
24326 * @return {Object} data The custom data
24328 getTargetFromEvent : function(e){
24329 return Roo.dd.Registry.getTargetFromEvent(e);
24333 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24334 * that it has registered. This method has no default implementation and should be overridden to provide
24335 * node-specific processing if necessary.
24336 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24337 * {@link #getTargetFromEvent} for this node)
24338 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24339 * @param {Event} e The event
24340 * @param {Object} data An object containing arbitrary data supplied by the drag source
24342 onNodeEnter : function(n, dd, e, data){
24347 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24348 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
24349 * overridden to provide the proper feedback.
24350 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24351 * {@link #getTargetFromEvent} for this node)
24352 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24353 * @param {Event} e The event
24354 * @param {Object} data An object containing arbitrary data supplied by the drag source
24355 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24356 * underlying {@link Roo.dd.StatusProxy} can be updated
24358 onNodeOver : function(n, dd, e, data){
24359 return this.dropAllowed;
24363 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24364 * the drop node without dropping. This method has no default implementation and should be overridden to provide
24365 * node-specific processing if necessary.
24366 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24367 * {@link #getTargetFromEvent} for this node)
24368 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24369 * @param {Event} e The event
24370 * @param {Object} data An object containing arbitrary data supplied by the drag source
24372 onNodeOut : function(n, dd, e, data){
24377 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24378 * the drop node. The default implementation returns false, so it should be overridden to provide the
24379 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24380 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24381 * {@link #getTargetFromEvent} for this node)
24382 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24383 * @param {Event} e The event
24384 * @param {Object} data An object containing arbitrary data supplied by the drag source
24385 * @return {Boolean} True if the drop was valid, else false
24387 onNodeDrop : function(n, dd, e, data){
24392 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24393 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
24394 * it should be overridden to provide the proper feedback if necessary.
24395 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24396 * @param {Event} e The event
24397 * @param {Object} data An object containing arbitrary data supplied by the drag source
24398 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24399 * underlying {@link Roo.dd.StatusProxy} can be updated
24401 onContainerOver : function(dd, e, data){
24402 return this.dropNotAllowed;
24406 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24407 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
24408 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24409 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
24410 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24411 * @param {Event} e The event
24412 * @param {Object} data An object containing arbitrary data supplied by the drag source
24413 * @return {Boolean} True if the drop was valid, else false
24415 onContainerDrop : function(dd, e, data){
24420 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24421 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
24422 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24423 * you should override this method and provide a custom implementation.
24424 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24425 * @param {Event} e The event
24426 * @param {Object} data An object containing arbitrary data supplied by the drag source
24427 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24428 * underlying {@link Roo.dd.StatusProxy} can be updated
24430 notifyEnter : function(dd, e, data){
24431 return this.dropNotAllowed;
24435 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24436 * This method will be called on every mouse movement while the drag source is over the drop zone.
24437 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24438 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24439 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24440 * registered node, it will call {@link #onContainerOver}.
24441 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24442 * @param {Event} e The event
24443 * @param {Object} data An object containing arbitrary data supplied by the drag source
24444 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24445 * underlying {@link Roo.dd.StatusProxy} can be updated
24447 notifyOver : function(dd, e, data){
24448 var n = this.getTargetFromEvent(e);
24449 if(!n){ // not over valid drop target
24450 if(this.lastOverNode){
24451 this.onNodeOut(this.lastOverNode, dd, e, data);
24452 this.lastOverNode = null;
24454 return this.onContainerOver(dd, e, data);
24456 if(this.lastOverNode != n){
24457 if(this.lastOverNode){
24458 this.onNodeOut(this.lastOverNode, dd, e, data);
24460 this.onNodeEnter(n, dd, e, data);
24461 this.lastOverNode = n;
24463 return this.onNodeOver(n, dd, e, data);
24467 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24468 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
24469 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24470 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24471 * @param {Event} e The event
24472 * @param {Object} data An object containing arbitrary data supplied by the drag zone
24474 notifyOut : function(dd, e, data){
24475 if(this.lastOverNode){
24476 this.onNodeOut(this.lastOverNode, dd, e, data);
24477 this.lastOverNode = null;
24482 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24483 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
24484 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24485 * otherwise it will call {@link #onContainerDrop}.
24486 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24487 * @param {Event} e The event
24488 * @param {Object} data An object containing arbitrary data supplied by the drag source
24489 * @return {Boolean} True if the drop was valid, else false
24491 notifyDrop : function(dd, e, data){
24492 if(this.lastOverNode){
24493 this.onNodeOut(this.lastOverNode, dd, e, data);
24494 this.lastOverNode = null;
24496 var n = this.getTargetFromEvent(e);
24498 this.onNodeDrop(n, dd, e, data) :
24499 this.onContainerDrop(dd, e, data);
24503 triggerCacheRefresh : function(){
24504 Roo.dd.DDM.refreshCache(this.groups);
24508 * Ext JS Library 1.1.1
24509 * Copyright(c) 2006-2007, Ext JS, LLC.
24511 * Originally Released Under LGPL - original licence link has changed is not relivant.
24514 * <script type="text/javascript">
24519 * @class Roo.data.SortTypes
24521 * Defines the default sorting (casting?) comparison functions used when sorting data.
24523 Roo.data.SortTypes = {
24525 * Default sort that does nothing
24526 * @param {Mixed} s The value being converted
24527 * @return {Mixed} The comparison value
24529 none : function(s){
24534 * The regular expression used to strip tags
24538 stripTagsRE : /<\/?[^>]+>/gi,
24541 * Strips all HTML tags to sort on text only
24542 * @param {Mixed} s The value being converted
24543 * @return {String} The comparison value
24545 asText : function(s){
24546 return String(s).replace(this.stripTagsRE, "");
24550 * Strips all HTML tags to sort on text only - Case insensitive
24551 * @param {Mixed} s The value being converted
24552 * @return {String} The comparison value
24554 asUCText : function(s){
24555 return String(s).toUpperCase().replace(this.stripTagsRE, "");
24559 * Case insensitive string
24560 * @param {Mixed} s The value being converted
24561 * @return {String} The comparison value
24563 asUCString : function(s) {
24564 return String(s).toUpperCase();
24569 * @param {Mixed} s The value being converted
24570 * @return {Number} The comparison value
24572 asDate : function(s) {
24576 if(s instanceof Date){
24577 return s.getTime();
24579 return Date.parse(String(s));
24584 * @param {Mixed} s The value being converted
24585 * @return {Float} The comparison value
24587 asFloat : function(s) {
24588 var val = parseFloat(String(s).replace(/,/g, ""));
24597 * @param {Mixed} s The value being converted
24598 * @return {Number} The comparison value
24600 asInt : function(s) {
24601 var val = parseInt(String(s).replace(/,/g, ""));
24609 * Ext JS Library 1.1.1
24610 * Copyright(c) 2006-2007, Ext JS, LLC.
24612 * Originally Released Under LGPL - original licence link has changed is not relivant.
24615 * <script type="text/javascript">
24619 * @class Roo.data.Record
24620 * Instances of this class encapsulate both record <em>definition</em> information, and record
24621 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24622 * to access Records cached in an {@link Roo.data.Store} object.<br>
24624 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24625 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24628 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24630 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24631 * {@link #create}. The parameters are the same.
24632 * @param {Array} data An associative Array of data values keyed by the field name.
24633 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24634 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24635 * not specified an integer id is generated.
24637 Roo.data.Record = function(data, id){
24638 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24643 * Generate a constructor for a specific record layout.
24644 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24645 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24646 * Each field definition object may contain the following properties: <ul>
24647 * <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,
24648 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24649 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24650 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24651 * is being used, then this is a string containing the javascript expression to reference the data relative to
24652 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24653 * to the data item relative to the record element. If the mapping expression is the same as the field name,
24654 * this may be omitted.</p></li>
24655 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24656 * <ul><li>auto (Default, implies no conversion)</li>
24661 * <li>date</li></ul></p></li>
24662 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24663 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24664 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24665 * by the Reader into an object that will be stored in the Record. It is passed the
24666 * following parameters:<ul>
24667 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24669 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24671 * <br>usage:<br><pre><code>
24672 var TopicRecord = Roo.data.Record.create(
24673 {name: 'title', mapping: 'topic_title'},
24674 {name: 'author', mapping: 'username'},
24675 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24676 {name: 'lastPost', mapping: 'post_time', type: 'date'},
24677 {name: 'lastPoster', mapping: 'user2'},
24678 {name: 'excerpt', mapping: 'post_text'}
24681 var myNewRecord = new TopicRecord({
24682 title: 'Do my job please',
24685 lastPost: new Date(),
24686 lastPoster: 'Animal',
24687 excerpt: 'No way dude!'
24689 myStore.add(myNewRecord);
24694 Roo.data.Record.create = function(o){
24695 var f = function(){
24696 f.superclass.constructor.apply(this, arguments);
24698 Roo.extend(f, Roo.data.Record);
24699 var p = f.prototype;
24700 p.fields = new Roo.util.MixedCollection(false, function(field){
24703 for(var i = 0, len = o.length; i < len; i++){
24704 p.fields.add(new Roo.data.Field(o[i]));
24706 f.getField = function(name){
24707 return p.fields.get(name);
24712 Roo.data.Record.AUTO_ID = 1000;
24713 Roo.data.Record.EDIT = 'edit';
24714 Roo.data.Record.REJECT = 'reject';
24715 Roo.data.Record.COMMIT = 'commit';
24717 Roo.data.Record.prototype = {
24719 * Readonly flag - true if this record has been modified.
24728 join : function(store){
24729 this.store = store;
24733 * Set the named field to the specified value.
24734 * @param {String} name The name of the field to set.
24735 * @param {Object} value The value to set the field to.
24737 set : function(name, value){
24738 if(this.data[name] == value){
24742 if(!this.modified){
24743 this.modified = {};
24745 if(typeof this.modified[name] == 'undefined'){
24746 this.modified[name] = this.data[name];
24748 this.data[name] = value;
24749 if(!this.editing && this.store){
24750 this.store.afterEdit(this);
24755 * Get the value of the named field.
24756 * @param {String} name The name of the field to get the value of.
24757 * @return {Object} The value of the field.
24759 get : function(name){
24760 return this.data[name];
24764 beginEdit : function(){
24765 this.editing = true;
24766 this.modified = {};
24770 cancelEdit : function(){
24771 this.editing = false;
24772 delete this.modified;
24776 endEdit : function(){
24777 this.editing = false;
24778 if(this.dirty && this.store){
24779 this.store.afterEdit(this);
24784 * Usually called by the {@link Roo.data.Store} which owns the Record.
24785 * Rejects all changes made to the Record since either creation, or the last commit operation.
24786 * Modified fields are reverted to their original values.
24788 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24789 * of reject operations.
24791 reject : function(){
24792 var m = this.modified;
24794 if(typeof m[n] != "function"){
24795 this.data[n] = m[n];
24798 this.dirty = false;
24799 delete this.modified;
24800 this.editing = false;
24802 this.store.afterReject(this);
24807 * Usually called by the {@link Roo.data.Store} which owns the Record.
24808 * Commits all changes made to the Record since either creation, or the last commit operation.
24810 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24811 * of commit operations.
24813 commit : function(){
24814 this.dirty = false;
24815 delete this.modified;
24816 this.editing = false;
24818 this.store.afterCommit(this);
24823 hasError : function(){
24824 return this.error != null;
24828 clearError : function(){
24833 * Creates a copy of this record.
24834 * @param {String} id (optional) A new record id if you don't want to use this record's id
24837 copy : function(newId) {
24838 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24842 * Ext JS Library 1.1.1
24843 * Copyright(c) 2006-2007, Ext JS, LLC.
24845 * Originally Released Under LGPL - original licence link has changed is not relivant.
24848 * <script type="text/javascript">
24854 * @class Roo.data.Store
24855 * @extends Roo.util.Observable
24856 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24857 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24859 * 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
24860 * has no knowledge of the format of the data returned by the Proxy.<br>
24862 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24863 * instances from the data object. These records are cached and made available through accessor functions.
24865 * Creates a new Store.
24866 * @param {Object} config A config object containing the objects needed for the Store to access data,
24867 * and read the data into Records.
24869 Roo.data.Store = function(config){
24870 this.data = new Roo.util.MixedCollection(false);
24871 this.data.getKey = function(o){
24874 this.baseParams = {};
24876 this.paramNames = {
24881 "multisort" : "_multisort"
24884 if(config && config.data){
24885 this.inlineData = config.data;
24886 delete config.data;
24889 Roo.apply(this, config);
24891 if(this.reader){ // reader passed
24892 this.reader = Roo.factory(this.reader, Roo.data);
24893 this.reader.xmodule = this.xmodule || false;
24894 if(!this.recordType){
24895 this.recordType = this.reader.recordType;
24897 if(this.reader.onMetaChange){
24898 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24902 if(this.recordType){
24903 this.fields = this.recordType.prototype.fields;
24905 this.modified = [];
24909 * @event datachanged
24910 * Fires when the data cache has changed, and a widget which is using this Store
24911 * as a Record cache should refresh its view.
24912 * @param {Store} this
24914 datachanged : true,
24916 * @event metachange
24917 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24918 * @param {Store} this
24919 * @param {Object} meta The JSON metadata
24924 * Fires when Records have been added to the Store
24925 * @param {Store} this
24926 * @param {Roo.data.Record[]} records The array of Records added
24927 * @param {Number} index The index at which the record(s) were added
24932 * Fires when a Record has been removed from the Store
24933 * @param {Store} this
24934 * @param {Roo.data.Record} record The Record that was removed
24935 * @param {Number} index The index at which the record was removed
24940 * Fires when a Record has been updated
24941 * @param {Store} this
24942 * @param {Roo.data.Record} record The Record that was updated
24943 * @param {String} operation The update operation being performed. Value may be one of:
24945 Roo.data.Record.EDIT
24946 Roo.data.Record.REJECT
24947 Roo.data.Record.COMMIT
24953 * Fires when the data cache has been cleared.
24954 * @param {Store} this
24958 * @event beforeload
24959 * Fires before a request is made for a new data object. If the beforeload handler returns false
24960 * the load action will be canceled.
24961 * @param {Store} this
24962 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24966 * @event beforeloadadd
24967 * Fires after a new set of Records has been loaded.
24968 * @param {Store} this
24969 * @param {Roo.data.Record[]} records The Records that were loaded
24970 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24972 beforeloadadd : true,
24975 * Fires after a new set of Records has been loaded, before they are added to the store.
24976 * @param {Store} this
24977 * @param {Roo.data.Record[]} records The Records that were loaded
24978 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24979 * @params {Object} return from reader
24983 * @event loadexception
24984 * Fires if an exception occurs in the Proxy during loading.
24985 * Called with the signature of the Proxy's "loadexception" event.
24986 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24989 * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
24990 * @param {Object} opts - load Options
24991 * @param {Object} jsonData from your request (normally this contains the Exception)
24993 loadexception : true
24997 this.proxy = Roo.factory(this.proxy, Roo.data);
24998 this.proxy.xmodule = this.xmodule || false;
24999 this.relayEvents(this.proxy, ["loadexception"]);
25001 this.sortToggle = {};
25002 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
25004 Roo.data.Store.superclass.constructor.call(this);
25006 if(this.inlineData){
25007 this.loadData(this.inlineData);
25008 delete this.inlineData;
25012 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25014 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
25015 * without a remote query - used by combo/forms at present.
25019 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25022 * @cfg {Array} data Inline data to be loaded when the store is initialized.
25025 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
25026 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25029 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25030 * on any HTTP request
25033 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25036 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25040 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25041 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25043 remoteSort : false,
25046 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25047 * loaded or when a record is removed. (defaults to false).
25049 pruneModifiedRecords : false,
25052 lastOptions : null,
25055 * Add Records to the Store and fires the add event.
25056 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25058 add : function(records){
25059 records = [].concat(records);
25060 for(var i = 0, len = records.length; i < len; i++){
25061 records[i].join(this);
25063 var index = this.data.length;
25064 this.data.addAll(records);
25065 this.fireEvent("add", this, records, index);
25069 * Remove a Record from the Store and fires the remove event.
25070 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25072 remove : function(record){
25073 var index = this.data.indexOf(record);
25074 this.data.removeAt(index);
25076 if(this.pruneModifiedRecords){
25077 this.modified.remove(record);
25079 this.fireEvent("remove", this, record, index);
25083 * Remove all Records from the Store and fires the clear event.
25085 removeAll : function(){
25087 if(this.pruneModifiedRecords){
25088 this.modified = [];
25090 this.fireEvent("clear", this);
25094 * Inserts Records to the Store at the given index and fires the add event.
25095 * @param {Number} index The start index at which to insert the passed Records.
25096 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25098 insert : function(index, records){
25099 records = [].concat(records);
25100 for(var i = 0, len = records.length; i < len; i++){
25101 this.data.insert(index, records[i]);
25102 records[i].join(this);
25104 this.fireEvent("add", this, records, index);
25108 * Get the index within the cache of the passed Record.
25109 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25110 * @return {Number} The index of the passed Record. Returns -1 if not found.
25112 indexOf : function(record){
25113 return this.data.indexOf(record);
25117 * Get the index within the cache of the Record with the passed id.
25118 * @param {String} id The id of the Record to find.
25119 * @return {Number} The index of the Record. Returns -1 if not found.
25121 indexOfId : function(id){
25122 return this.data.indexOfKey(id);
25126 * Get the Record with the specified id.
25127 * @param {String} id The id of the Record to find.
25128 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25130 getById : function(id){
25131 return this.data.key(id);
25135 * Get the Record at the specified index.
25136 * @param {Number} index The index of the Record to find.
25137 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25139 getAt : function(index){
25140 return this.data.itemAt(index);
25144 * Returns a range of Records between specified indices.
25145 * @param {Number} startIndex (optional) The starting index (defaults to 0)
25146 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25147 * @return {Roo.data.Record[]} An array of Records
25149 getRange : function(start, end){
25150 return this.data.getRange(start, end);
25154 storeOptions : function(o){
25155 o = Roo.apply({}, o);
25158 this.lastOptions = o;
25162 * Loads the Record cache from the configured Proxy using the configured Reader.
25164 * If using remote paging, then the first load call must specify the <em>start</em>
25165 * and <em>limit</em> properties in the options.params property to establish the initial
25166 * position within the dataset, and the number of Records to cache on each read from the Proxy.
25168 * <strong>It is important to note that for remote data sources, loading is asynchronous,
25169 * and this call will return before the new data has been loaded. Perform any post-processing
25170 * in a callback function, or in a "load" event handler.</strong>
25172 * @param {Object} options An object containing properties which control loading options:<ul>
25173 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25174 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25177 data : data, // array of key=>value data like JsonReader
25178 total : data.length,
25184 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25185 * passed the following arguments:<ul>
25186 * <li>r : Roo.data.Record[]</li>
25187 * <li>options: Options object from the load call</li>
25188 * <li>success: Boolean success indicator</li></ul></li>
25189 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25190 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25193 load : function(options){
25194 options = options || {};
25195 if(this.fireEvent("beforeload", this, options) !== false){
25196 this.storeOptions(options);
25197 var p = Roo.apply(options.params || {}, this.baseParams);
25198 // if meta was not loaded from remote source.. try requesting it.
25199 if (!this.reader.metaFromRemote) {
25200 p._requestMeta = 1;
25202 if(this.sortInfo && this.remoteSort){
25203 var pn = this.paramNames;
25204 p[pn["sort"]] = this.sortInfo.field;
25205 p[pn["dir"]] = this.sortInfo.direction;
25207 if (this.multiSort) {
25208 var pn = this.paramNames;
25209 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25212 this.proxy.load(p, this.reader, this.loadRecords, this, options);
25217 * Reloads the Record cache from the configured Proxy using the configured Reader and
25218 * the options from the last load operation performed.
25219 * @param {Object} options (optional) An object containing properties which may override the options
25220 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25221 * the most recently used options are reused).
25223 reload : function(options){
25224 this.load(Roo.applyIf(options||{}, this.lastOptions));
25228 // Called as a callback by the Reader during a load operation.
25229 loadRecords : function(o, options, success){
25232 if(success !== false){
25233 this.fireEvent("load", this, [], options, o);
25235 if(options.callback){
25236 options.callback.call(options.scope || this, [], options, false);
25240 // if data returned failure - throw an exception.
25241 if (o.success === false) {
25242 // show a message if no listener is registered.
25243 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25244 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25246 // loadmask wil be hooked into this..
25247 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25250 var r = o.records, t = o.totalRecords || r.length;
25252 this.fireEvent("beforeloadadd", this, r, options, o);
25254 if(!options || options.add !== true){
25255 if(this.pruneModifiedRecords){
25256 this.modified = [];
25258 for(var i = 0, len = r.length; i < len; i++){
25262 this.data = this.snapshot;
25263 delete this.snapshot;
25266 this.data.addAll(r);
25267 this.totalLength = t;
25269 this.fireEvent("datachanged", this);
25271 this.totalLength = Math.max(t, this.data.length+r.length);
25275 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25277 var e = new Roo.data.Record({});
25279 e.set(this.parent.displayField, this.parent.emptyTitle);
25280 e.set(this.parent.valueField, '');
25285 this.fireEvent("load", this, r, options, o);
25286 if(options.callback){
25287 options.callback.call(options.scope || this, r, options, true);
25293 * Loads data from a passed data block. A Reader which understands the format of the data
25294 * must have been configured in the constructor.
25295 * @param {Object} data The data block from which to read the Records. The format of the data expected
25296 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25297 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25299 loadData : function(o, append){
25300 var r = this.reader.readRecords(o);
25301 this.loadRecords(r, {add: append}, true);
25305 * using 'cn' the nested child reader read the child array into it's child stores.
25306 * @param {Object} rec The record with a 'children array
25308 loadDataFromChildren : function(rec)
25310 this.loadData(this.reader.toLoadData(rec));
25315 * Gets the number of cached records.
25317 * <em>If using paging, this may not be the total size of the dataset. If the data object
25318 * used by the Reader contains the dataset size, then the getTotalCount() function returns
25319 * the data set size</em>
25321 getCount : function(){
25322 return this.data.length || 0;
25326 * Gets the total number of records in the dataset as returned by the server.
25328 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25329 * the dataset size</em>
25331 getTotalCount : function(){
25332 return this.totalLength || 0;
25336 * Returns the sort state of the Store as an object with two properties:
25338 field {String} The name of the field by which the Records are sorted
25339 direction {String} The sort order, "ASC" or "DESC"
25342 getSortState : function(){
25343 return this.sortInfo;
25347 applySort : function(){
25348 if(this.sortInfo && !this.remoteSort){
25349 var s = this.sortInfo, f = s.field;
25350 var st = this.fields.get(f).sortType;
25351 var fn = function(r1, r2){
25352 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25353 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25355 this.data.sort(s.direction, fn);
25356 if(this.snapshot && this.snapshot != this.data){
25357 this.snapshot.sort(s.direction, fn);
25363 * Sets the default sort column and order to be used by the next load operation.
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 setDefaultSort : function(field, dir){
25368 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25372 * Sort the Records.
25373 * If remote sorting is used, the sort is performed on the server, and the cache is
25374 * reloaded. If local sorting is used, the cache is sorted internally.
25375 * @param {String} fieldName The name of the field to sort by.
25376 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25378 sort : function(fieldName, dir){
25379 var f = this.fields.get(fieldName);
25381 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25383 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25384 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25389 this.sortToggle[f.name] = dir;
25390 this.sortInfo = {field: f.name, direction: dir};
25391 if(!this.remoteSort){
25393 this.fireEvent("datachanged", this);
25395 this.load(this.lastOptions);
25400 * Calls the specified function for each of the Records in the cache.
25401 * @param {Function} fn The function to call. The Record is passed as the first parameter.
25402 * Returning <em>false</em> aborts and exits the iteration.
25403 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25405 each : function(fn, scope){
25406 this.data.each(fn, scope);
25410 * Gets all records modified since the last commit. Modified records are persisted across load operations
25411 * (e.g., during paging).
25412 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25414 getModifiedRecords : function(){
25415 return this.modified;
25419 createFilterFn : function(property, value, anyMatch){
25420 if(!value.exec){ // not a regex
25421 value = String(value);
25422 if(value.length == 0){
25425 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25427 return function(r){
25428 return value.test(r.data[property]);
25433 * Sums the value of <i>property</i> for each record between start and end and returns the result.
25434 * @param {String} property A field on your records
25435 * @param {Number} start The record index to start at (defaults to 0)
25436 * @param {Number} end The last record index to include (defaults to length - 1)
25437 * @return {Number} The sum
25439 sum : function(property, start, end){
25440 var rs = this.data.items, v = 0;
25441 start = start || 0;
25442 end = (end || end === 0) ? end : rs.length-1;
25444 for(var i = start; i <= end; i++){
25445 v += (rs[i].data[property] || 0);
25451 * Filter the records by a specified property.
25452 * @param {String} field A field on your records
25453 * @param {String/RegExp} value Either a string that the field
25454 * should start with or a RegExp to test against the field
25455 * @param {Boolean} anyMatch True to match any part not just the beginning
25457 filter : function(property, value, anyMatch){
25458 var fn = this.createFilterFn(property, value, anyMatch);
25459 return fn ? this.filterBy(fn) : this.clearFilter();
25463 * Filter by a function. The specified function will be called with each
25464 * record in this data source. If the function returns true the record is included,
25465 * otherwise it is filtered.
25466 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25467 * @param {Object} scope (optional) The scope of the function (defaults to this)
25469 filterBy : function(fn, scope){
25470 this.snapshot = this.snapshot || this.data;
25471 this.data = this.queryBy(fn, scope||this);
25472 this.fireEvent("datachanged", this);
25476 * Query the records by a specified property.
25477 * @param {String} field A field on your records
25478 * @param {String/RegExp} value Either a string that the field
25479 * should start with or a RegExp to test against the field
25480 * @param {Boolean} anyMatch True to match any part not just the beginning
25481 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25483 query : function(property, value, anyMatch){
25484 var fn = this.createFilterFn(property, value, anyMatch);
25485 return fn ? this.queryBy(fn) : this.data.clone();
25489 * Query by a function. The specified function will be called with each
25490 * record in this data source. If the function returns true the record is included
25492 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25493 * @param {Object} scope (optional) The scope of the function (defaults to this)
25494 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25496 queryBy : function(fn, scope){
25497 var data = this.snapshot || this.data;
25498 return data.filterBy(fn, scope||this);
25502 * Collects unique values for a particular dataIndex from this store.
25503 * @param {String} dataIndex The property to collect
25504 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25505 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25506 * @return {Array} An array of the unique values
25508 collect : function(dataIndex, allowNull, bypassFilter){
25509 var d = (bypassFilter === true && this.snapshot) ?
25510 this.snapshot.items : this.data.items;
25511 var v, sv, r = [], l = {};
25512 for(var i = 0, len = d.length; i < len; i++){
25513 v = d[i].data[dataIndex];
25515 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25524 * Revert to a view of the Record cache with no filtering applied.
25525 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25527 clearFilter : function(suppressEvent){
25528 if(this.snapshot && this.snapshot != this.data){
25529 this.data = this.snapshot;
25530 delete this.snapshot;
25531 if(suppressEvent !== true){
25532 this.fireEvent("datachanged", this);
25538 afterEdit : function(record){
25539 if(this.modified.indexOf(record) == -1){
25540 this.modified.push(record);
25542 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25546 afterReject : function(record){
25547 this.modified.remove(record);
25548 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25552 afterCommit : function(record){
25553 this.modified.remove(record);
25554 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25558 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25559 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25561 commitChanges : function(){
25562 var m = this.modified.slice(0);
25563 this.modified = [];
25564 for(var i = 0, len = m.length; i < len; i++){
25570 * Cancel outstanding changes on all changed records.
25572 rejectChanges : function(){
25573 var m = this.modified.slice(0);
25574 this.modified = [];
25575 for(var i = 0, len = m.length; i < len; i++){
25580 onMetaChange : function(meta, rtype, o){
25581 this.recordType = rtype;
25582 this.fields = rtype.prototype.fields;
25583 delete this.snapshot;
25584 this.sortInfo = meta.sortInfo || this.sortInfo;
25585 this.modified = [];
25586 this.fireEvent('metachange', this, this.reader.meta);
25589 moveIndex : function(data, type)
25591 var index = this.indexOf(data);
25593 var newIndex = index + type;
25597 this.insert(newIndex, data);
25602 * Ext JS Library 1.1.1
25603 * Copyright(c) 2006-2007, Ext JS, LLC.
25605 * Originally Released Under LGPL - original licence link has changed is not relivant.
25608 * <script type="text/javascript">
25612 * @class Roo.data.SimpleStore
25613 * @extends Roo.data.Store
25614 * Small helper class to make creating Stores from Array data easier.
25615 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25616 * @cfg {Array} fields An array of field definition objects, or field name strings.
25617 * @cfg {Object} an existing reader (eg. copied from another store)
25618 * @cfg {Array} data The multi-dimensional array of data
25619 * @cfg {Roo.data.DataProxy} proxy [not-required]
25620 * @cfg {Roo.data.Reader} reader [not-required]
25622 * @param {Object} config
25624 Roo.data.SimpleStore = function(config)
25626 Roo.data.SimpleStore.superclass.constructor.call(this, {
25628 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25631 Roo.data.Record.create(config.fields)
25633 proxy : new Roo.data.MemoryProxy(config.data)
25637 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25639 * Ext JS Library 1.1.1
25640 * Copyright(c) 2006-2007, Ext JS, LLC.
25642 * Originally Released Under LGPL - original licence link has changed is not relivant.
25645 * <script type="text/javascript">
25650 * @extends Roo.data.Store
25651 * @class Roo.data.JsonStore
25652 * Small helper class to make creating Stores for JSON data easier. <br/>
25654 var store = new Roo.data.JsonStore({
25655 url: 'get-images.php',
25657 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25660 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25661 * JsonReader and HttpProxy (unless inline data is provided).</b>
25662 * @cfg {Array} fields An array of field definition objects, or field name strings.
25664 * @param {Object} config
25666 Roo.data.JsonStore = function(c){
25667 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25668 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25669 reader: new Roo.data.JsonReader(c, c.fields)
25672 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25674 * Ext JS Library 1.1.1
25675 * Copyright(c) 2006-2007, Ext JS, LLC.
25677 * Originally Released Under LGPL - original licence link has changed is not relivant.
25680 * <script type="text/javascript">
25684 Roo.data.Field = function(config){
25685 if(typeof config == "string"){
25686 config = {name: config};
25688 Roo.apply(this, config);
25691 this.type = "auto";
25694 var st = Roo.data.SortTypes;
25695 // named sortTypes are supported, here we look them up
25696 if(typeof this.sortType == "string"){
25697 this.sortType = st[this.sortType];
25700 // set default sortType for strings and dates
25701 if(!this.sortType){
25704 this.sortType = st.asUCString;
25707 this.sortType = st.asDate;
25710 this.sortType = st.none;
25715 var stripRe = /[\$,%]/g;
25717 // prebuilt conversion function for this field, instead of
25718 // switching every time we're reading a value
25720 var cv, dateFormat = this.dateFormat;
25725 cv = function(v){ return v; };
25728 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25732 return v !== undefined && v !== null && v !== '' ?
25733 parseInt(String(v).replace(stripRe, ""), 10) : '';
25738 return v !== undefined && v !== null && v !== '' ?
25739 parseFloat(String(v).replace(stripRe, ""), 10) : '';
25744 cv = function(v){ return v === true || v === "true" || v == 1; };
25751 if(v instanceof Date){
25755 if(dateFormat == "timestamp"){
25756 return new Date(v*1000);
25758 return Date.parseDate(v, dateFormat);
25760 var parsed = Date.parse(v);
25761 return parsed ? new Date(parsed) : null;
25770 Roo.data.Field.prototype = {
25778 * Ext JS Library 1.1.1
25779 * Copyright(c) 2006-2007, Ext JS, LLC.
25781 * Originally Released Under LGPL - original licence link has changed is not relivant.
25784 * <script type="text/javascript">
25787 // Base class for reading structured data from a data source. This class is intended to be
25788 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25791 * @class Roo.data.DataReader
25793 * Base class for reading structured data from a data source. This class is intended to be
25794 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25797 Roo.data.DataReader = function(meta, recordType){
25801 this.recordType = recordType instanceof Array ?
25802 Roo.data.Record.create(recordType) : recordType;
25805 Roo.data.DataReader.prototype = {
25808 readerType : 'Data',
25810 * Create an empty record
25811 * @param {Object} data (optional) - overlay some values
25812 * @return {Roo.data.Record} record created.
25814 newRow : function(d) {
25816 this.recordType.prototype.fields.each(function(c) {
25818 case 'int' : da[c.name] = 0; break;
25819 case 'date' : da[c.name] = new Date(); break;
25820 case 'float' : da[c.name] = 0.0; break;
25821 case 'boolean' : da[c.name] = false; break;
25822 default : da[c.name] = ""; break;
25826 return new this.recordType(Roo.apply(da, d));
25832 * Ext JS Library 1.1.1
25833 * Copyright(c) 2006-2007, Ext JS, LLC.
25835 * Originally Released Under LGPL - original licence link has changed is not relivant.
25838 * <script type="text/javascript">
25842 * @class Roo.data.DataProxy
25843 * @extends Roo.util.Observable
25845 * This class is an abstract base class for implementations which provide retrieval of
25846 * unformatted data objects.<br>
25848 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25849 * (of the appropriate type which knows how to parse the data object) to provide a block of
25850 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25852 * Custom implementations must implement the load method as described in
25853 * {@link Roo.data.HttpProxy#load}.
25855 Roo.data.DataProxy = function(){
25858 * @event beforeload
25859 * Fires before a network request is made to retrieve a data object.
25860 * @param {Object} This DataProxy object.
25861 * @param {Object} params The params parameter to the load function.
25866 * Fires before the load method's callback is called.
25867 * @param {Object} This DataProxy object.
25868 * @param {Object} o The data object.
25869 * @param {Object} arg The callback argument object passed to the load function.
25873 * @event loadexception
25874 * Fires if an Exception occurs during data retrieval.
25875 * @param {Object} This DataProxy object.
25876 * @param {Object} o The data object.
25877 * @param {Object} arg The callback argument object passed to the load function.
25878 * @param {Object} e The Exception.
25880 loadexception : true
25882 Roo.data.DataProxy.superclass.constructor.call(this);
25885 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25888 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25892 * Ext JS Library 1.1.1
25893 * Copyright(c) 2006-2007, Ext JS, LLC.
25895 * Originally Released Under LGPL - original licence link has changed is not relivant.
25898 * <script type="text/javascript">
25901 * @class Roo.data.MemoryProxy
25902 * @extends Roo.data.DataProxy
25903 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25904 * to the Reader when its load method is called.
25906 * @param {Object} config A config object containing the objects needed for the Store to access data,
25908 Roo.data.MemoryProxy = function(config){
25910 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25911 data = config.data;
25913 Roo.data.MemoryProxy.superclass.constructor.call(this);
25917 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25920 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25923 * Load data from the requested source (in this case an in-memory
25924 * data object passed to the constructor), read the data object into
25925 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25926 * process that block using the passed callback.
25927 * @param {Object} params This parameter is not used by the MemoryProxy class.
25928 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25929 * object into a block of Roo.data.Records.
25930 * @param {Function} callback The function into which to pass the block of Roo.data.records.
25931 * The function must be passed <ul>
25932 * <li>The Record block object</li>
25933 * <li>The "arg" argument from the load function</li>
25934 * <li>A boolean success indicator</li>
25936 * @param {Object} scope The scope in which to call the callback
25937 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25939 load : function(params, reader, callback, scope, arg){
25940 params = params || {};
25943 result = reader.readRecords(params.data ? params.data :this.data);
25945 this.fireEvent("loadexception", this, arg, null, e);
25946 callback.call(scope, null, arg, false);
25949 callback.call(scope, result, arg, true);
25953 update : function(params, records){
25958 * Ext JS Library 1.1.1
25959 * Copyright(c) 2006-2007, Ext JS, LLC.
25961 * Originally Released Under LGPL - original licence link has changed is not relivant.
25964 * <script type="text/javascript">
25967 * @class Roo.data.HttpProxy
25968 * @extends Roo.data.DataProxy
25969 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25970 * configured to reference a certain URL.<br><br>
25972 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25973 * from which the running page was served.<br><br>
25975 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25977 * Be aware that to enable the browser to parse an XML document, the server must set
25978 * the Content-Type header in the HTTP response to "text/xml".
25980 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25981 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
25982 * will be used to make the request.
25984 Roo.data.HttpProxy = function(conn){
25985 Roo.data.HttpProxy.superclass.constructor.call(this);
25986 // is conn a conn config or a real conn?
25988 this.useAjax = !conn || !conn.events;
25992 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25993 // thse are take from connection...
25996 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
25999 * @cfg {Object} extraParams An object containing properties which are used as
26000 * extra parameters to each request made by this object. (defaults to undefined)
26003 * @cfg {Object} defaultHeaders An object containing request headers which are added
26004 * to each request made by this object. (defaults to undefined)
26007 * @cfg {String} method (GET|POST) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
26010 * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
26013 * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
26019 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26023 * Return the {@link Roo.data.Connection} object being used by this Proxy.
26024 * @return {Connection} The Connection object. This object may be used to subscribe to events on
26025 * a finer-grained basis than the DataProxy events.
26027 getConnection : function(){
26028 return this.useAjax ? Roo.Ajax : this.conn;
26032 * Load data from the configured {@link Roo.data.Connection}, read the data object into
26033 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26034 * process that block using the passed callback.
26035 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26036 * for the request to the remote server.
26037 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26038 * object into a block of Roo.data.Records.
26039 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26040 * The function must be passed <ul>
26041 * <li>The Record block object</li>
26042 * <li>The "arg" argument from the load function</li>
26043 * <li>A boolean success indicator</li>
26045 * @param {Object} scope The scope in which to call the callback
26046 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26048 load : function(params, reader, callback, scope, arg){
26049 if(this.fireEvent("beforeload", this, params) !== false){
26051 params : params || {},
26053 callback : callback,
26058 callback : this.loadResponse,
26062 Roo.applyIf(o, this.conn);
26063 if(this.activeRequest){
26064 Roo.Ajax.abort(this.activeRequest);
26066 this.activeRequest = Roo.Ajax.request(o);
26068 this.conn.request(o);
26071 callback.call(scope||this, null, arg, false);
26076 loadResponse : function(o, success, response){
26077 delete this.activeRequest;
26079 this.fireEvent("loadexception", this, o, response);
26080 o.request.callback.call(o.request.scope, null, o.request.arg, false);
26085 result = o.reader.read(response);
26088 o.raw = { errorMsg : response.responseText };
26089 this.fireEvent("loadexception", this, o, response, e);
26090 o.request.callback.call(o.request.scope, o, o.request.arg, false);
26094 this.fireEvent("load", this, o, o.request.arg);
26095 o.request.callback.call(o.request.scope, result, o.request.arg, true);
26099 update : function(dataSet){
26104 updateResponse : function(dataSet){
26109 * Ext JS Library 1.1.1
26110 * Copyright(c) 2006-2007, Ext JS, LLC.
26112 * Originally Released Under LGPL - original licence link has changed is not relivant.
26115 * <script type="text/javascript">
26119 * @class Roo.data.ScriptTagProxy
26120 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26121 * other than the originating domain of the running page.<br><br>
26123 * <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
26124 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26126 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26127 * source code that is used as the source inside a <script> tag.<br><br>
26129 * In order for the browser to process the returned data, the server must wrap the data object
26130 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26131 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26132 * depending on whether the callback name was passed:
26135 boolean scriptTag = false;
26136 String cb = request.getParameter("callback");
26139 response.setContentType("text/javascript");
26141 response.setContentType("application/x-json");
26143 Writer out = response.getWriter();
26145 out.write(cb + "(");
26147 out.print(dataBlock.toJsonString());
26154 * @param {Object} config A configuration object.
26156 Roo.data.ScriptTagProxy = function(config){
26157 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26158 Roo.apply(this, config);
26159 this.head = document.getElementsByTagName("head")[0];
26162 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26164 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26166 * @cfg {String} url The URL from which to request the data object.
26169 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26173 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26174 * the server the name of the callback function set up by the load call to process the returned data object.
26175 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26176 * javascript output which calls this named function passing the data object as its only parameter.
26178 callbackParam : "callback",
26180 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26181 * name to the request.
26186 * Load data from the configured URL, read the data object into
26187 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26188 * process that block using the passed callback.
26189 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26190 * for the request to the remote server.
26191 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26192 * object into a block of Roo.data.Records.
26193 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26194 * The function must be passed <ul>
26195 * <li>The Record block object</li>
26196 * <li>The "arg" argument from the load function</li>
26197 * <li>A boolean success indicator</li>
26199 * @param {Object} scope The scope in which to call the callback
26200 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26202 load : function(params, reader, callback, scope, arg){
26203 if(this.fireEvent("beforeload", this, params) !== false){
26205 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26207 var url = this.url;
26208 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26210 url += "&_dc=" + (new Date().getTime());
26212 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26215 cb : "stcCallback"+transId,
26216 scriptId : "stcScript"+transId,
26220 callback : callback,
26226 window[trans.cb] = function(o){
26227 conn.handleResponse(o, trans);
26230 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26232 if(this.autoAbort !== false){
26236 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26238 var script = document.createElement("script");
26239 script.setAttribute("src", url);
26240 script.setAttribute("type", "text/javascript");
26241 script.setAttribute("id", trans.scriptId);
26242 this.head.appendChild(script);
26244 this.trans = trans;
26246 callback.call(scope||this, null, arg, false);
26251 isLoading : function(){
26252 return this.trans ? true : false;
26256 * Abort the current server request.
26258 abort : function(){
26259 if(this.isLoading()){
26260 this.destroyTrans(this.trans);
26265 destroyTrans : function(trans, isLoaded){
26266 this.head.removeChild(document.getElementById(trans.scriptId));
26267 clearTimeout(trans.timeoutId);
26269 window[trans.cb] = undefined;
26271 delete window[trans.cb];
26274 // if hasn't been loaded, wait for load to remove it to prevent script error
26275 window[trans.cb] = function(){
26276 window[trans.cb] = undefined;
26278 delete window[trans.cb];
26285 handleResponse : function(o, trans){
26286 this.trans = false;
26287 this.destroyTrans(trans, true);
26290 result = trans.reader.readRecords(o);
26292 this.fireEvent("loadexception", this, o, trans.arg, e);
26293 trans.callback.call(trans.scope||window, null, trans.arg, false);
26296 this.fireEvent("load", this, o, trans.arg);
26297 trans.callback.call(trans.scope||window, result, trans.arg, true);
26301 handleFailure : function(trans){
26302 this.trans = false;
26303 this.destroyTrans(trans, false);
26304 this.fireEvent("loadexception", this, null, trans.arg);
26305 trans.callback.call(trans.scope||window, null, trans.arg, false);
26309 * Ext JS Library 1.1.1
26310 * Copyright(c) 2006-2007, Ext JS, LLC.
26312 * Originally Released Under LGPL - original licence link has changed is not relivant.
26315 * <script type="text/javascript">
26319 * @class Roo.data.JsonReader
26320 * @extends Roo.data.DataReader
26321 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26322 * based on mappings in a provided Roo.data.Record constructor.
26324 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26325 * in the reply previously.
26330 var RecordDef = Roo.data.Record.create([
26331 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26332 {name: 'occupation'} // This field will use "occupation" as the mapping.
26334 var myReader = new Roo.data.JsonReader({
26335 totalProperty: "results", // The property which contains the total dataset size (optional)
26336 root: "rows", // The property which contains an Array of row objects
26337 id: "id" // The property within each row object that provides an ID for the record (optional)
26341 * This would consume a JSON file like this:
26343 { 'results': 2, 'rows': [
26344 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26345 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26348 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26349 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26350 * paged from the remote server.
26351 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26352 * @cfg {String} root name of the property which contains the Array of row objects.
26353 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26354 * @cfg {Array} fields Array of field definition objects
26356 * Create a new JsonReader
26357 * @param {Object} meta Metadata configuration options
26358 * @param {Object} recordType Either an Array of field definition objects,
26359 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26361 Roo.data.JsonReader = function(meta, recordType){
26364 // set some defaults:
26365 Roo.applyIf(meta, {
26366 totalProperty: 'total',
26367 successProperty : 'success',
26372 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26374 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26376 readerType : 'Json',
26379 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
26380 * Used by Store query builder to append _requestMeta to params.
26383 metaFromRemote : false,
26385 * This method is only used by a DataProxy which has retrieved data from a remote server.
26386 * @param {Object} response The XHR object which contains the JSON data in its responseText.
26387 * @return {Object} data A data block which is used by an Roo.data.Store object as
26388 * a cache of Roo.data.Records.
26390 read : function(response){
26391 var json = response.responseText;
26393 var o = /* eval:var:o */ eval("("+json+")");
26395 throw {message: "JsonReader.read: Json object not found"};
26401 this.metaFromRemote = true;
26402 this.meta = o.metaData;
26403 this.recordType = Roo.data.Record.create(o.metaData.fields);
26404 this.onMetaChange(this.meta, this.recordType, o);
26406 return this.readRecords(o);
26409 // private function a store will implement
26410 onMetaChange : function(meta, recordType, o){
26417 simpleAccess: function(obj, subsc) {
26424 getJsonAccessor: function(){
26426 return function(expr) {
26428 return(re.test(expr))
26429 ? new Function("obj", "return obj." + expr)
26434 return Roo.emptyFn;
26439 * Create a data block containing Roo.data.Records from an XML document.
26440 * @param {Object} o An object which contains an Array of row objects in the property specified
26441 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26442 * which contains the total size of the dataset.
26443 * @return {Object} data A data block which is used by an Roo.data.Store object as
26444 * a cache of Roo.data.Records.
26446 readRecords : function(o){
26448 * After any data loads, the raw JSON data is available for further custom processing.
26452 var s = this.meta, Record = this.recordType,
26453 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26455 // Generate extraction functions for the totalProperty, the root, the id, and for each field
26457 if(s.totalProperty) {
26458 this.getTotal = this.getJsonAccessor(s.totalProperty);
26460 if(s.successProperty) {
26461 this.getSuccess = this.getJsonAccessor(s.successProperty);
26463 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26465 var g = this.getJsonAccessor(s.id);
26466 this.getId = function(rec) {
26468 return (r === undefined || r === "") ? null : r;
26471 this.getId = function(){return null;};
26474 for(var jj = 0; jj < fl; jj++){
26476 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26477 this.ef[jj] = this.getJsonAccessor(map);
26481 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26482 if(s.totalProperty){
26483 var vt = parseInt(this.getTotal(o), 10);
26488 if(s.successProperty){
26489 var vs = this.getSuccess(o);
26490 if(vs === false || vs === 'false'){
26495 for(var i = 0; i < c; i++){
26498 var id = this.getId(n);
26499 for(var j = 0; j < fl; j++){
26501 var v = this.ef[j](n);
26503 Roo.log('missing convert for ' + f.name);
26507 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26511 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26517 var record = new Record(values, id);
26519 records[i] = record;
26525 totalRecords : totalRecords
26528 // used when loading children.. @see loadDataFromChildren
26529 toLoadData: function(rec)
26531 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26532 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26533 return { data : data, total : data.length };
26538 * Ext JS Library 1.1.1
26539 * Copyright(c) 2006-2007, Ext JS, LLC.
26541 * Originally Released Under LGPL - original licence link has changed is not relivant.
26544 * <script type="text/javascript">
26548 * @class Roo.data.XmlReader
26549 * @extends Roo.data.DataReader
26550 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26551 * based on mappings in a provided Roo.data.Record constructor.<br><br>
26553 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26554 * header in the HTTP response must be set to "text/xml".</em>
26558 var RecordDef = Roo.data.Record.create([
26559 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26560 {name: 'occupation'} // This field will use "occupation" as the mapping.
26562 var myReader = new Roo.data.XmlReader({
26563 totalRecords: "results", // The element which contains the total dataset size (optional)
26564 record: "row", // The repeated element which contains row information
26565 id: "id" // The element within the row that provides an ID for the record (optional)
26569 * This would consume an XML file like this:
26573 <results>2</results>
26576 <name>Bill</name>
26577 <occupation>Gardener</occupation>
26581 <name>Ben</name>
26582 <occupation>Horticulturalist</occupation>
26586 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26587 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26588 * paged from the remote server.
26589 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26590 * @cfg {String} success The DomQuery path to the success attribute used by forms.
26591 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26592 * a record identifier value.
26594 * Create a new XmlReader
26595 * @param {Object} meta Metadata configuration options
26596 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
26597 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26598 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
26600 Roo.data.XmlReader = function(meta, recordType){
26602 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26604 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26606 readerType : 'Xml',
26609 * This method is only used by a DataProxy which has retrieved data from a remote server.
26610 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
26611 * to contain a method called 'responseXML' that returns an XML document object.
26612 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26613 * a cache of Roo.data.Records.
26615 read : function(response){
26616 var doc = response.responseXML;
26618 throw {message: "XmlReader.read: XML Document not available"};
26620 return this.readRecords(doc);
26624 * Create a data block containing Roo.data.Records from an XML document.
26625 * @param {Object} doc A parsed XML document.
26626 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26627 * a cache of Roo.data.Records.
26629 readRecords : function(doc){
26631 * After any data loads/reads, the raw XML Document is available for further custom processing.
26632 * @type XMLDocument
26634 this.xmlData = doc;
26635 var root = doc.documentElement || doc;
26636 var q = Roo.DomQuery;
26637 var recordType = this.recordType, fields = recordType.prototype.fields;
26638 var sid = this.meta.id;
26639 var totalRecords = 0, success = true;
26640 if(this.meta.totalRecords){
26641 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26644 if(this.meta.success){
26645 var sv = q.selectValue(this.meta.success, root, true);
26646 success = sv !== false && sv !== 'false';
26649 var ns = q.select(this.meta.record, root);
26650 for(var i = 0, len = ns.length; i < len; i++) {
26653 var id = sid ? q.selectValue(sid, n) : undefined;
26654 for(var j = 0, jlen = fields.length; j < jlen; j++){
26655 var f = fields.items[j];
26656 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26658 values[f.name] = v;
26660 var record = new recordType(values, id);
26662 records[records.length] = record;
26668 totalRecords : totalRecords || records.length
26673 * Ext JS Library 1.1.1
26674 * Copyright(c) 2006-2007, Ext JS, LLC.
26676 * Originally Released Under LGPL - original licence link has changed is not relivant.
26679 * <script type="text/javascript">
26683 * @class Roo.data.ArrayReader
26684 * @extends Roo.data.DataReader
26685 * Data reader class to create an Array of Roo.data.Record objects from an Array.
26686 * Each element of that Array represents a row of data fields. The
26687 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26688 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26692 var RecordDef = Roo.data.Record.create([
26693 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
26694 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
26696 var myReader = new Roo.data.ArrayReader({
26697 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
26701 * This would consume an Array like this:
26703 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26707 * Create a new JsonReader
26708 * @param {Object} meta Metadata configuration options.
26709 * @param {Object|Array} recordType Either an Array of field definition objects
26711 * @cfg {Array} fields Array of field definition objects
26712 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26713 * as specified to {@link Roo.data.Record#create},
26714 * or an {@link Roo.data.Record} object
26717 * created using {@link Roo.data.Record#create}.
26719 Roo.data.ArrayReader = function(meta, recordType)
26721 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26724 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26727 * Create a data block containing Roo.data.Records from an XML document.
26728 * @param {Object} o An Array of row objects which represents the dataset.
26729 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26730 * a cache of Roo.data.Records.
26732 readRecords : function(o)
26734 var sid = this.meta ? this.meta.id : null;
26735 var recordType = this.recordType, fields = recordType.prototype.fields;
26738 for(var i = 0; i < root.length; i++){
26741 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26742 for(var j = 0, jlen = fields.length; j < jlen; j++){
26743 var f = fields.items[j];
26744 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26745 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26747 values[f.name] = v;
26749 var record = new recordType(values, id);
26751 records[records.length] = record;
26755 totalRecords : records.length
26758 // used when loading children.. @see loadDataFromChildren
26759 toLoadData: function(rec)
26761 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26762 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26769 * Ext JS Library 1.1.1
26770 * Copyright(c) 2006-2007, Ext JS, LLC.
26772 * Originally Released Under LGPL - original licence link has changed is not relivant.
26775 * <script type="text/javascript">
26780 * @class Roo.data.Tree
26781 * @extends Roo.util.Observable
26782 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26783 * in the tree have most standard DOM functionality.
26785 * @param {Node} root (optional) The root node
26787 Roo.data.Tree = function(root){
26788 this.nodeHash = {};
26790 * The root node for this tree
26795 this.setRootNode(root);
26800 * Fires when a new child node is appended to a node in this tree.
26801 * @param {Tree} tree The owner tree
26802 * @param {Node} parent The parent node
26803 * @param {Node} node The newly appended node
26804 * @param {Number} index The index of the newly appended node
26809 * Fires when a child node is removed from a node in this tree.
26810 * @param {Tree} tree The owner tree
26811 * @param {Node} parent The parent node
26812 * @param {Node} node The child node removed
26817 * Fires when a node is moved to a new location in the tree
26818 * @param {Tree} tree The owner tree
26819 * @param {Node} node The node moved
26820 * @param {Node} oldParent The old parent of this node
26821 * @param {Node} newParent The new parent of this node
26822 * @param {Number} index The index it was moved to
26827 * Fires when a new child node is inserted in a node in this tree.
26828 * @param {Tree} tree The owner tree
26829 * @param {Node} parent The parent node
26830 * @param {Node} node The child node inserted
26831 * @param {Node} refNode The child node the node was inserted before
26835 * @event beforeappend
26836 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26837 * @param {Tree} tree The owner tree
26838 * @param {Node} parent The parent node
26839 * @param {Node} node The child node to be appended
26841 "beforeappend" : true,
26843 * @event beforeremove
26844 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26845 * @param {Tree} tree The owner tree
26846 * @param {Node} parent The parent node
26847 * @param {Node} node The child node to be removed
26849 "beforeremove" : true,
26851 * @event beforemove
26852 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26853 * @param {Tree} tree The owner tree
26854 * @param {Node} node The node being moved
26855 * @param {Node} oldParent The parent of the node
26856 * @param {Node} newParent The new parent the node is moving to
26857 * @param {Number} index The index it is being moved to
26859 "beforemove" : true,
26861 * @event beforeinsert
26862 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26863 * @param {Tree} tree The owner tree
26864 * @param {Node} parent The parent node
26865 * @param {Node} node The child node to be inserted
26866 * @param {Node} refNode The child node the node is being inserted before
26868 "beforeinsert" : true
26871 Roo.data.Tree.superclass.constructor.call(this);
26874 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26875 pathSeparator: "/",
26877 proxyNodeEvent : function(){
26878 return this.fireEvent.apply(this, arguments);
26882 * Returns the root node for this tree.
26885 getRootNode : function(){
26890 * Sets the root node for this tree.
26891 * @param {Node} node
26894 setRootNode : function(node){
26896 node.ownerTree = this;
26897 node.isRoot = true;
26898 this.registerNode(node);
26903 * Gets a node in this tree by its id.
26904 * @param {String} id
26907 getNodeById : function(id){
26908 return this.nodeHash[id];
26911 registerNode : function(node){
26912 this.nodeHash[node.id] = node;
26915 unregisterNode : function(node){
26916 delete this.nodeHash[node.id];
26919 toString : function(){
26920 return "[Tree"+(this.id?" "+this.id:"")+"]";
26925 * @class Roo.data.Node
26926 * @extends Roo.util.Observable
26927 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26928 * @cfg {String} id The id for this node. If one is not specified, one is generated.
26930 * @param {Object} attributes The attributes/config for the node
26932 Roo.data.Node = function(attributes){
26934 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26937 this.attributes = attributes || {};
26938 this.leaf = this.attributes.leaf;
26940 * The node id. @type String
26942 this.id = this.attributes.id;
26944 this.id = Roo.id(null, "ynode-");
26945 this.attributes.id = this.id;
26950 * All child nodes of this node. @type Array
26952 this.childNodes = [];
26953 if(!this.childNodes.indexOf){ // indexOf is a must
26954 this.childNodes.indexOf = function(o){
26955 for(var i = 0, len = this.length; i < len; i++){
26964 * The parent node for this node. @type Node
26966 this.parentNode = null;
26968 * The first direct child node of this node, or null if this node has no child nodes. @type Node
26970 this.firstChild = null;
26972 * The last direct child node of this node, or null if this node has no child nodes. @type Node
26974 this.lastChild = null;
26976 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26978 this.previousSibling = null;
26980 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26982 this.nextSibling = null;
26987 * Fires when a new child node is appended
26988 * @param {Tree} tree The owner tree
26989 * @param {Node} this This node
26990 * @param {Node} node The newly appended node
26991 * @param {Number} index The index of the newly appended node
26996 * Fires when a child node is removed
26997 * @param {Tree} tree The owner tree
26998 * @param {Node} this This node
26999 * @param {Node} node The removed node
27004 * Fires when this node is moved to a new location in the tree
27005 * @param {Tree} tree The owner tree
27006 * @param {Node} this This node
27007 * @param {Node} oldParent The old parent of this node
27008 * @param {Node} newParent The new parent of this node
27009 * @param {Number} index The index it was moved to
27014 * Fires when a new child node is inserted.
27015 * @param {Tree} tree The owner tree
27016 * @param {Node} this This node
27017 * @param {Node} node The child node inserted
27018 * @param {Node} refNode The child node the node was inserted before
27022 * @event beforeappend
27023 * Fires before a new child is appended, return false to cancel the append.
27024 * @param {Tree} tree The owner tree
27025 * @param {Node} this This node
27026 * @param {Node} node The child node to be appended
27028 "beforeappend" : true,
27030 * @event beforeremove
27031 * Fires before a child is removed, return false to cancel the remove.
27032 * @param {Tree} tree The owner tree
27033 * @param {Node} this This node
27034 * @param {Node} node The child node to be removed
27036 "beforeremove" : true,
27038 * @event beforemove
27039 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27040 * @param {Tree} tree The owner tree
27041 * @param {Node} this This node
27042 * @param {Node} oldParent The parent of this node
27043 * @param {Node} newParent The new parent this node is moving to
27044 * @param {Number} index The index it is being moved to
27046 "beforemove" : true,
27048 * @event beforeinsert
27049 * Fires before a new child is inserted, return false to cancel the insert.
27050 * @param {Tree} tree The owner tree
27051 * @param {Node} this This node
27052 * @param {Node} node The child node to be inserted
27053 * @param {Node} refNode The child node the node is being inserted before
27055 "beforeinsert" : true
27057 this.listeners = this.attributes.listeners;
27058 Roo.data.Node.superclass.constructor.call(this);
27061 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27062 fireEvent : function(evtName){
27063 // first do standard event for this node
27064 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27067 // then bubble it up to the tree if the event wasn't cancelled
27068 var ot = this.getOwnerTree();
27070 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27078 * Returns true if this node is a leaf
27079 * @return {Boolean}
27081 isLeaf : function(){
27082 return this.leaf === true;
27086 setFirstChild : function(node){
27087 this.firstChild = node;
27091 setLastChild : function(node){
27092 this.lastChild = node;
27097 * Returns true if this node is the last child of its parent
27098 * @return {Boolean}
27100 isLast : function(){
27101 return (!this.parentNode ? true : this.parentNode.lastChild == this);
27105 * Returns true if this node is the first child of its parent
27106 * @return {Boolean}
27108 isFirst : function(){
27109 return (!this.parentNode ? true : this.parentNode.firstChild == this);
27112 hasChildNodes : function(){
27113 return !this.isLeaf() && this.childNodes.length > 0;
27117 * Insert node(s) as the last child node of this node.
27118 * @param {Node/Array} node The node or Array of nodes to append
27119 * @return {Node} The appended node if single append, or null if an array was passed
27121 appendChild : function(node){
27123 if(node instanceof Array){
27125 }else if(arguments.length > 1){
27129 // if passed an array or multiple args do them one by one
27131 for(var i = 0, len = multi.length; i < len; i++) {
27132 this.appendChild(multi[i]);
27135 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27138 var index = this.childNodes.length;
27139 var oldParent = node.parentNode;
27140 // it's a move, make sure we move it cleanly
27142 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27145 oldParent.removeChild(node);
27148 index = this.childNodes.length;
27150 this.setFirstChild(node);
27152 this.childNodes.push(node);
27153 node.parentNode = this;
27154 var ps = this.childNodes[index-1];
27156 node.previousSibling = ps;
27157 ps.nextSibling = node;
27159 node.previousSibling = null;
27161 node.nextSibling = null;
27162 this.setLastChild(node);
27163 node.setOwnerTree(this.getOwnerTree());
27164 this.fireEvent("append", this.ownerTree, this, node, index);
27165 if(this.ownerTree) {
27166 this.ownerTree.fireEvent("appendnode", this, node, index);
27169 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27176 * Removes a child node from this node.
27177 * @param {Node} node The node to remove
27178 * @return {Node} The removed node
27180 removeChild : function(node){
27181 var index = this.childNodes.indexOf(node);
27185 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27189 // remove it from childNodes collection
27190 this.childNodes.splice(index, 1);
27193 if(node.previousSibling){
27194 node.previousSibling.nextSibling = node.nextSibling;
27196 if(node.nextSibling){
27197 node.nextSibling.previousSibling = node.previousSibling;
27200 // update child refs
27201 if(this.firstChild == node){
27202 this.setFirstChild(node.nextSibling);
27204 if(this.lastChild == node){
27205 this.setLastChild(node.previousSibling);
27208 node.setOwnerTree(null);
27209 // clear any references from the node
27210 node.parentNode = null;
27211 node.previousSibling = null;
27212 node.nextSibling = null;
27213 this.fireEvent("remove", this.ownerTree, this, node);
27218 * Inserts the first node before the second node in this nodes childNodes collection.
27219 * @param {Node} node The node to insert
27220 * @param {Node} refNode The node to insert before (if null the node is appended)
27221 * @return {Node} The inserted node
27223 insertBefore : function(node, refNode){
27224 if(!refNode){ // like standard Dom, refNode can be null for append
27225 return this.appendChild(node);
27228 if(node == refNode){
27232 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27235 var index = this.childNodes.indexOf(refNode);
27236 var oldParent = node.parentNode;
27237 var refIndex = index;
27239 // when moving internally, indexes will change after remove
27240 if(oldParent == this && this.childNodes.indexOf(node) < index){
27244 // it's a move, make sure we move it cleanly
27246 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27249 oldParent.removeChild(node);
27252 this.setFirstChild(node);
27254 this.childNodes.splice(refIndex, 0, node);
27255 node.parentNode = this;
27256 var ps = this.childNodes[refIndex-1];
27258 node.previousSibling = ps;
27259 ps.nextSibling = node;
27261 node.previousSibling = null;
27263 node.nextSibling = refNode;
27264 refNode.previousSibling = node;
27265 node.setOwnerTree(this.getOwnerTree());
27266 this.fireEvent("insert", this.ownerTree, this, node, refNode);
27268 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27274 * Returns the child node at the specified index.
27275 * @param {Number} index
27278 item : function(index){
27279 return this.childNodes[index];
27283 * Replaces one child node in this node with another.
27284 * @param {Node} newChild The replacement node
27285 * @param {Node} oldChild The node to replace
27286 * @return {Node} The replaced node
27288 replaceChild : function(newChild, oldChild){
27289 this.insertBefore(newChild, oldChild);
27290 this.removeChild(oldChild);
27295 * Returns the index of a child node
27296 * @param {Node} node
27297 * @return {Number} The index of the node or -1 if it was not found
27299 indexOf : function(child){
27300 return this.childNodes.indexOf(child);
27304 * Returns the tree this node is in.
27307 getOwnerTree : function(){
27308 // if it doesn't have one, look for one
27309 if(!this.ownerTree){
27313 this.ownerTree = p.ownerTree;
27319 return this.ownerTree;
27323 * Returns depth of this node (the root node has a depth of 0)
27326 getDepth : function(){
27329 while(p.parentNode){
27337 setOwnerTree : function(tree){
27338 // if it's move, we need to update everyone
27339 if(tree != this.ownerTree){
27340 if(this.ownerTree){
27341 this.ownerTree.unregisterNode(this);
27343 this.ownerTree = tree;
27344 var cs = this.childNodes;
27345 for(var i = 0, len = cs.length; i < len; i++) {
27346 cs[i].setOwnerTree(tree);
27349 tree.registerNode(this);
27355 * Returns the path for this node. The path can be used to expand or select this node programmatically.
27356 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27357 * @return {String} The path
27359 getPath : function(attr){
27360 attr = attr || "id";
27361 var p = this.parentNode;
27362 var b = [this.attributes[attr]];
27364 b.unshift(p.attributes[attr]);
27367 var sep = this.getOwnerTree().pathSeparator;
27368 return sep + b.join(sep);
27372 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27373 * function call will be the scope provided or the current node. The arguments to the function
27374 * will be the args provided or the current node. If the function returns false at any point,
27375 * the bubble is stopped.
27376 * @param {Function} fn The function to call
27377 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27378 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27380 bubble : function(fn, scope, args){
27383 if(fn.call(scope || p, args || p) === false){
27391 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27392 * function call will be the scope provided or the current node. The arguments to the function
27393 * will be the args provided or the current node. If the function returns false at any point,
27394 * the cascade is stopped on that branch.
27395 * @param {Function} fn The function to call
27396 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27397 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27399 cascade : function(fn, scope, args){
27400 if(fn.call(scope || this, args || this) !== false){
27401 var cs = this.childNodes;
27402 for(var i = 0, len = cs.length; i < len; i++) {
27403 cs[i].cascade(fn, scope, args);
27409 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27410 * function call will be the scope provided or the current node. The arguments to the function
27411 * will be the args provided or the current node. If the function returns false at any point,
27412 * the iteration stops.
27413 * @param {Function} fn The function to call
27414 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27415 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27417 eachChild : function(fn, scope, args){
27418 var cs = this.childNodes;
27419 for(var i = 0, len = cs.length; i < len; i++) {
27420 if(fn.call(scope || this, args || cs[i]) === false){
27427 * Finds the first child that has the attribute with the specified value.
27428 * @param {String} attribute The attribute name
27429 * @param {Mixed} value The value to search for
27430 * @return {Node} The found child or null if none was found
27432 findChild : function(attribute, value){
27433 var cs = this.childNodes;
27434 for(var i = 0, len = cs.length; i < len; i++) {
27435 if(cs[i].attributes[attribute] == value){
27443 * Finds the first child by a custom function. The child matches if the function passed
27445 * @param {Function} fn
27446 * @param {Object} scope (optional)
27447 * @return {Node} The found child or null if none was found
27449 findChildBy : function(fn, scope){
27450 var cs = this.childNodes;
27451 for(var i = 0, len = cs.length; i < len; i++) {
27452 if(fn.call(scope||cs[i], cs[i]) === true){
27460 * Sorts this nodes children using the supplied sort function
27461 * @param {Function} fn
27462 * @param {Object} scope (optional)
27464 sort : function(fn, scope){
27465 var cs = this.childNodes;
27466 var len = cs.length;
27468 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27470 for(var i = 0; i < len; i++){
27472 n.previousSibling = cs[i-1];
27473 n.nextSibling = cs[i+1];
27475 this.setFirstChild(n);
27478 this.setLastChild(n);
27485 * Returns true if this node is an ancestor (at any point) of the passed node.
27486 * @param {Node} node
27487 * @return {Boolean}
27489 contains : function(node){
27490 return node.isAncestor(this);
27494 * Returns true if the passed node is an ancestor (at any point) of this node.
27495 * @param {Node} node
27496 * @return {Boolean}
27498 isAncestor : function(node){
27499 var p = this.parentNode;
27509 toString : function(){
27510 return "[Node"+(this.id?" "+this.id:"")+"]";
27514 * Ext JS Library 1.1.1
27515 * Copyright(c) 2006-2007, Ext JS, LLC.
27517 * Originally Released Under LGPL - original licence link has changed is not relivant.
27520 * <script type="text/javascript">
27525 * @class Roo.Shadow
27526 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
27527 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
27528 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27530 * Create a new Shadow
27531 * @param {Object} config The config object
27533 Roo.Shadow = function(config){
27534 Roo.apply(this, config);
27535 if(typeof this.mode != "string"){
27536 this.mode = this.defaultMode;
27538 var o = this.offset, a = {h: 0};
27539 var rad = Math.floor(this.offset/2);
27540 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27546 a.l -= this.offset + rad;
27547 a.t -= this.offset + rad;
27558 a.l -= (this.offset - rad);
27559 a.t -= this.offset + rad;
27561 a.w -= (this.offset - rad)*2;
27572 a.l -= (this.offset - rad);
27573 a.t -= (this.offset - rad);
27575 a.w -= (this.offset + rad + 1);
27576 a.h -= (this.offset + rad);
27585 Roo.Shadow.prototype = {
27587 * @cfg {String} mode
27588 * The shadow display mode. Supports the following options:<br />
27589 * sides: Shadow displays on both sides and bottom only<br />
27590 * frame: Shadow displays equally on all four sides<br />
27591 * drop: Traditional bottom-right drop shadow (default)
27595 * @cfg {String} offset
27596 * The number of pixels to offset the shadow from the element (defaults to 4)
27601 defaultMode: "drop",
27604 * Displays the shadow under the target element
27605 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27607 show : function(target){
27608 target = Roo.get(target);
27610 this.el = Roo.Shadow.Pool.pull();
27611 if(this.el.dom.nextSibling != target.dom){
27612 this.el.insertBefore(target);
27615 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27617 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27620 target.getLeft(true),
27621 target.getTop(true),
27625 this.el.dom.style.display = "block";
27629 * Returns true if the shadow is visible, else false
27631 isVisible : function(){
27632 return this.el ? true : false;
27636 * Direct alignment when values are already available. Show must be called at least once before
27637 * calling this method to ensure it is initialized.
27638 * @param {Number} left The target element left position
27639 * @param {Number} top The target element top position
27640 * @param {Number} width The target element width
27641 * @param {Number} height The target element height
27643 realign : function(l, t, w, h){
27647 var a = this.adjusts, d = this.el.dom, s = d.style;
27649 s.left = (l+a.l)+"px";
27650 s.top = (t+a.t)+"px";
27651 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27653 if(s.width != sws || s.height != shs){
27657 var cn = d.childNodes;
27658 var sww = Math.max(0, (sw-12))+"px";
27659 cn[0].childNodes[1].style.width = sww;
27660 cn[1].childNodes[1].style.width = sww;
27661 cn[2].childNodes[1].style.width = sww;
27662 cn[1].style.height = Math.max(0, (sh-12))+"px";
27668 * Hides this shadow
27672 this.el.dom.style.display = "none";
27673 Roo.Shadow.Pool.push(this.el);
27679 * Adjust the z-index of this shadow
27680 * @param {Number} zindex The new z-index
27682 setZIndex : function(z){
27685 this.el.setStyle("z-index", z);
27690 // Private utility class that manages the internal Shadow cache
27691 Roo.Shadow.Pool = function(){
27693 var markup = Roo.isIE ?
27694 '<div class="x-ie-shadow"></div>' :
27695 '<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>';
27698 var sh = p.shift();
27700 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27701 sh.autoBoxAdjust = false;
27706 push : function(sh){
27712 * Ext JS Library 1.1.1
27713 * Copyright(c) 2006-2007, Ext JS, LLC.
27715 * Originally Released Under LGPL - original licence link has changed is not relivant.
27718 * <script type="text/javascript">
27723 * @class Roo.SplitBar
27724 * @extends Roo.util.Observable
27725 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27729 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27730 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27731 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27732 split.minSize = 100;
27733 split.maxSize = 600;
27734 split.animate = true;
27735 split.on('moved', splitterMoved);
27738 * Create a new SplitBar
27739 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
27740 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
27741 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27742 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
27743 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27744 position of the SplitBar).
27746 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27749 this.el = Roo.get(dragElement, true);
27750 this.el.dom.unselectable = "on";
27752 this.resizingEl = Roo.get(resizingElement, true);
27756 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27757 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27760 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27763 * The minimum size of the resizing element. (Defaults to 0)
27769 * The maximum size of the resizing element. (Defaults to 2000)
27772 this.maxSize = 2000;
27775 * Whether to animate the transition to the new size
27778 this.animate = false;
27781 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27784 this.useShim = false;
27789 if(!existingProxy){
27791 this.proxy = Roo.SplitBar.createProxy(this.orientation);
27793 this.proxy = Roo.get(existingProxy).dom;
27796 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27799 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27802 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27805 this.dragSpecs = {};
27808 * @private The adapter to use to positon and resize elements
27810 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27811 this.adapter.init(this);
27813 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27815 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27816 this.el.addClass("x-splitbar-h");
27819 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27820 this.el.addClass("x-splitbar-v");
27826 * Fires when the splitter is moved (alias for {@link #event-moved})
27827 * @param {Roo.SplitBar} this
27828 * @param {Number} newSize the new width or height
27833 * Fires when the splitter is moved
27834 * @param {Roo.SplitBar} this
27835 * @param {Number} newSize the new width or height
27839 * @event beforeresize
27840 * Fires before the splitter is dragged
27841 * @param {Roo.SplitBar} this
27843 "beforeresize" : true,
27845 "beforeapply" : true
27848 Roo.util.Observable.call(this);
27851 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27852 onStartProxyDrag : function(x, y){
27853 this.fireEvent("beforeresize", this);
27855 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
27857 o.enableDisplayMode("block");
27858 // all splitbars share the same overlay
27859 Roo.SplitBar.prototype.overlay = o;
27861 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27862 this.overlay.show();
27863 Roo.get(this.proxy).setDisplayed("block");
27864 var size = this.adapter.getElementSize(this);
27865 this.activeMinSize = this.getMinimumSize();;
27866 this.activeMaxSize = this.getMaximumSize();;
27867 var c1 = size - this.activeMinSize;
27868 var c2 = Math.max(this.activeMaxSize - size, 0);
27869 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27870 this.dd.resetConstraints();
27871 this.dd.setXConstraint(
27872 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
27873 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27875 this.dd.setYConstraint(0, 0);
27877 this.dd.resetConstraints();
27878 this.dd.setXConstraint(0, 0);
27879 this.dd.setYConstraint(
27880 this.placement == Roo.SplitBar.TOP ? c1 : c2,
27881 this.placement == Roo.SplitBar.TOP ? c2 : c1
27884 this.dragSpecs.startSize = size;
27885 this.dragSpecs.startPoint = [x, y];
27886 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27890 * @private Called after the drag operation by the DDProxy
27892 onEndProxyDrag : function(e){
27893 Roo.get(this.proxy).setDisplayed(false);
27894 var endPoint = Roo.lib.Event.getXY(e);
27896 this.overlay.hide();
27899 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27900 newSize = this.dragSpecs.startSize +
27901 (this.placement == Roo.SplitBar.LEFT ?
27902 endPoint[0] - this.dragSpecs.startPoint[0] :
27903 this.dragSpecs.startPoint[0] - endPoint[0]
27906 newSize = this.dragSpecs.startSize +
27907 (this.placement == Roo.SplitBar.TOP ?
27908 endPoint[1] - this.dragSpecs.startPoint[1] :
27909 this.dragSpecs.startPoint[1] - endPoint[1]
27912 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27913 if(newSize != this.dragSpecs.startSize){
27914 if(this.fireEvent('beforeapply', this, newSize) !== false){
27915 this.adapter.setElementSize(this, newSize);
27916 this.fireEvent("moved", this, newSize);
27917 this.fireEvent("resize", this, newSize);
27923 * Get the adapter this SplitBar uses
27924 * @return The adapter object
27926 getAdapter : function(){
27927 return this.adapter;
27931 * Set the adapter this SplitBar uses
27932 * @param {Object} adapter A SplitBar adapter object
27934 setAdapter : function(adapter){
27935 this.adapter = adapter;
27936 this.adapter.init(this);
27940 * Gets the minimum size for the resizing element
27941 * @return {Number} The minimum size
27943 getMinimumSize : function(){
27944 return this.minSize;
27948 * Sets the minimum size for the resizing element
27949 * @param {Number} minSize The minimum size
27951 setMinimumSize : function(minSize){
27952 this.minSize = minSize;
27956 * Gets the maximum size for the resizing element
27957 * @return {Number} The maximum size
27959 getMaximumSize : function(){
27960 return this.maxSize;
27964 * Sets the maximum size for the resizing element
27965 * @param {Number} maxSize The maximum size
27967 setMaximumSize : function(maxSize){
27968 this.maxSize = maxSize;
27972 * Sets the initialize size for the resizing element
27973 * @param {Number} size The initial size
27975 setCurrentSize : function(size){
27976 var oldAnimate = this.animate;
27977 this.animate = false;
27978 this.adapter.setElementSize(this, size);
27979 this.animate = oldAnimate;
27983 * Destroy this splitbar.
27984 * @param {Boolean} removeEl True to remove the element
27986 destroy : function(removeEl){
27988 this.shim.remove();
27991 this.proxy.parentNode.removeChild(this.proxy);
27999 * @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.
28001 Roo.SplitBar.createProxy = function(dir){
28002 var proxy = new Roo.Element(document.createElement("div"));
28003 proxy.unselectable();
28004 var cls = 'x-splitbar-proxy';
28005 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
28006 document.body.appendChild(proxy.dom);
28011 * @class Roo.SplitBar.BasicLayoutAdapter
28012 * Default Adapter. It assumes the splitter and resizing element are not positioned
28013 * elements and only gets/sets the width of the element. Generally used for table based layouts.
28015 Roo.SplitBar.BasicLayoutAdapter = function(){
28018 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28019 // do nothing for now
28020 init : function(s){
28024 * Called before drag operations to get the current size of the resizing element.
28025 * @param {Roo.SplitBar} s The SplitBar using this adapter
28027 getElementSize : function(s){
28028 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28029 return s.resizingEl.getWidth();
28031 return s.resizingEl.getHeight();
28036 * Called after drag operations to set the size of the resizing element.
28037 * @param {Roo.SplitBar} s The SplitBar using this adapter
28038 * @param {Number} newSize The new size to set
28039 * @param {Function} onComplete A function to be invoked when resizing is complete
28041 setElementSize : function(s, newSize, onComplete){
28042 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28044 s.resizingEl.setWidth(newSize);
28046 onComplete(s, newSize);
28049 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28054 s.resizingEl.setHeight(newSize);
28056 onComplete(s, newSize);
28059 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28066 *@class Roo.SplitBar.AbsoluteLayoutAdapter
28067 * @extends Roo.SplitBar.BasicLayoutAdapter
28068 * Adapter that moves the splitter element to align with the resized sizing element.
28069 * Used with an absolute positioned SplitBar.
28070 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28071 * document.body, make sure you assign an id to the body element.
28073 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28074 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28075 this.container = Roo.get(container);
28078 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28079 init : function(s){
28080 this.basic.init(s);
28083 getElementSize : function(s){
28084 return this.basic.getElementSize(s);
28087 setElementSize : function(s, newSize, onComplete){
28088 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28091 moveSplitter : function(s){
28092 var yes = Roo.SplitBar;
28093 switch(s.placement){
28095 s.el.setX(s.resizingEl.getRight());
28098 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28101 s.el.setY(s.resizingEl.getBottom());
28104 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28111 * Orientation constant - Create a vertical SplitBar
28115 Roo.SplitBar.VERTICAL = 1;
28118 * Orientation constant - Create a horizontal SplitBar
28122 Roo.SplitBar.HORIZONTAL = 2;
28125 * Placement constant - The resizing element is to the left of the splitter element
28129 Roo.SplitBar.LEFT = 1;
28132 * Placement constant - The resizing element is to the right of the splitter element
28136 Roo.SplitBar.RIGHT = 2;
28139 * Placement constant - The resizing element is positioned above the splitter element
28143 Roo.SplitBar.TOP = 3;
28146 * Placement constant - The resizing element is positioned under splitter element
28150 Roo.SplitBar.BOTTOM = 4;
28153 * Ext JS Library 1.1.1
28154 * Copyright(c) 2006-2007, Ext JS, LLC.
28156 * Originally Released Under LGPL - original licence link has changed is not relivant.
28159 * <script type="text/javascript">
28164 * @extends Roo.util.Observable
28165 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
28166 * This class also supports single and multi selection modes. <br>
28167 * Create a data model bound view:
28169 var store = new Roo.data.Store(...);
28171 var view = new Roo.View({
28173 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
28175 singleSelect: true,
28176 selectedClass: "ydataview-selected",
28180 // listen for node click?
28181 view.on("click", function(vw, index, node, e){
28182 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28186 dataModel.load("foobar.xml");
28188 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28190 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28191 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28193 * Note: old style constructor is still suported (container, template, config)
28196 * Create a new View
28197 * @param {Object} config The config object
28200 Roo.View = function(config, depreciated_tpl, depreciated_config){
28202 this.parent = false;
28204 if (typeof(depreciated_tpl) == 'undefined') {
28205 // new way.. - universal constructor.
28206 Roo.apply(this, config);
28207 this.el = Roo.get(this.el);
28210 this.el = Roo.get(config);
28211 this.tpl = depreciated_tpl;
28212 Roo.apply(this, depreciated_config);
28214 this.wrapEl = this.el.wrap().wrap();
28215 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28218 if(typeof(this.tpl) == "string"){
28219 this.tpl = new Roo.Template(this.tpl);
28221 // support xtype ctors..
28222 this.tpl = new Roo.factory(this.tpl, Roo);
28226 this.tpl.compile();
28231 * @event beforeclick
28232 * Fires before a click is processed. Returns false to cancel the default action.
28233 * @param {Roo.View} this
28234 * @param {Number} index The index of the target node
28235 * @param {HTMLElement} node The target node
28236 * @param {Roo.EventObject} e The raw event object
28238 "beforeclick" : true,
28241 * Fires when a template node is clicked.
28242 * @param {Roo.View} this
28243 * @param {Number} index The index of the target node
28244 * @param {HTMLElement} node The target node
28245 * @param {Roo.EventObject} e The raw event object
28250 * Fires when a template node is double clicked.
28251 * @param {Roo.View} this
28252 * @param {Number} index The index of the target node
28253 * @param {HTMLElement} node The target node
28254 * @param {Roo.EventObject} e The raw event object
28258 * @event contextmenu
28259 * Fires when a template node is right clicked.
28260 * @param {Roo.View} this
28261 * @param {Number} index The index of the target node
28262 * @param {HTMLElement} node The target node
28263 * @param {Roo.EventObject} e The raw event object
28265 "contextmenu" : true,
28267 * @event selectionchange
28268 * Fires when the selected nodes change.
28269 * @param {Roo.View} this
28270 * @param {Array} selections Array of the selected nodes
28272 "selectionchange" : true,
28275 * @event beforeselect
28276 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28277 * @param {Roo.View} this
28278 * @param {HTMLElement} node The node to be selected
28279 * @param {Array} selections Array of currently selected nodes
28281 "beforeselect" : true,
28283 * @event preparedata
28284 * Fires on every row to render, to allow you to change the data.
28285 * @param {Roo.View} this
28286 * @param {Object} data to be rendered (change this)
28288 "preparedata" : true
28296 "click": this.onClick,
28297 "dblclick": this.onDblClick,
28298 "contextmenu": this.onContextMenu,
28302 this.selections = [];
28304 this.cmp = new Roo.CompositeElementLite([]);
28306 this.store = Roo.factory(this.store, Roo.data);
28307 this.setStore(this.store, true);
28310 if ( this.footer && this.footer.xtype) {
28312 var fctr = this.wrapEl.appendChild(document.createElement("div"));
28314 this.footer.dataSource = this.store;
28315 this.footer.container = fctr;
28316 this.footer = Roo.factory(this.footer, Roo);
28317 fctr.insertFirst(this.el);
28319 // this is a bit insane - as the paging toolbar seems to detach the el..
28320 // dom.parentNode.parentNode.parentNode
28321 // they get detached?
28325 Roo.View.superclass.constructor.call(this);
28330 Roo.extend(Roo.View, Roo.util.Observable, {
28333 * @cfg {Roo.data.Store} store Data store to load data from.
28338 * @cfg {String|Roo.Element} el The container element.
28343 * @cfg {String|Roo.Template} tpl The template used by this View
28347 * @cfg {String} dataName the named area of the template to use as the data area
28348 * Works with domtemplates roo-name="name"
28352 * @cfg {String} selectedClass The css class to add to selected nodes
28354 selectedClass : "x-view-selected",
28356 * @cfg {String} emptyText The empty text to show when nothing is loaded.
28361 * @cfg {String} text to display on mask (default Loading)
28365 * @cfg {Boolean} multiSelect Allow multiple selection
28367 multiSelect : false,
28369 * @cfg {Boolean} singleSelect Allow single selection
28371 singleSelect: false,
28374 * @cfg {Boolean} toggleSelect - selecting
28376 toggleSelect : false,
28379 * @cfg {Boolean} tickable - selecting
28384 * Returns the element this view is bound to.
28385 * @return {Roo.Element}
28387 getEl : function(){
28388 return this.wrapEl;
28394 * Refreshes the view. - called by datachanged on the store. - do not call directly.
28396 refresh : function(){
28397 //Roo.log('refresh');
28400 // if we are using something like 'domtemplate', then
28401 // the what gets used is:
28402 // t.applySubtemplate(NAME, data, wrapping data..)
28403 // the outer template then get' applied with
28404 // the store 'extra data'
28405 // and the body get's added to the
28406 // roo-name="data" node?
28407 // <span class='roo-tpl-{name}'></span> ?????
28411 this.clearSelections();
28412 this.el.update("");
28414 var records = this.store.getRange();
28415 if(records.length < 1) {
28417 // is this valid?? = should it render a template??
28419 this.el.update(this.emptyText);
28423 if (this.dataName) {
28424 this.el.update(t.apply(this.store.meta)); //????
28425 el = this.el.child('.roo-tpl-' + this.dataName);
28428 for(var i = 0, len = records.length; i < len; i++){
28429 var data = this.prepareData(records[i].data, i, records[i]);
28430 this.fireEvent("preparedata", this, data, i, records[i]);
28432 var d = Roo.apply({}, data);
28435 Roo.apply(d, {'roo-id' : Roo.id()});
28439 Roo.each(this.parent.item, function(item){
28440 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28443 Roo.apply(d, {'roo-data-checked' : 'checked'});
28447 html[html.length] = Roo.util.Format.trim(
28449 t.applySubtemplate(this.dataName, d, this.store.meta) :
28456 el.update(html.join(""));
28457 this.nodes = el.dom.childNodes;
28458 this.updateIndexes(0);
28463 * Function to override to reformat the data that is sent to
28464 * the template for each node.
28465 * DEPRICATED - use the preparedata event handler.
28466 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28467 * a JSON object for an UpdateManager bound view).
28469 prepareData : function(data, index, record)
28471 this.fireEvent("preparedata", this, data, index, record);
28475 onUpdate : function(ds, record){
28476 // Roo.log('on update');
28477 this.clearSelections();
28478 var index = this.store.indexOf(record);
28479 var n = this.nodes[index];
28480 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28481 n.parentNode.removeChild(n);
28482 this.updateIndexes(index, index);
28488 onAdd : function(ds, records, index)
28490 //Roo.log(['on Add', ds, records, index] );
28491 this.clearSelections();
28492 if(this.nodes.length == 0){
28496 var n = this.nodes[index];
28497 for(var i = 0, len = records.length; i < len; i++){
28498 var d = this.prepareData(records[i].data, i, records[i]);
28500 this.tpl.insertBefore(n, d);
28503 this.tpl.append(this.el, d);
28506 this.updateIndexes(index);
28509 onRemove : function(ds, record, index){
28510 // Roo.log('onRemove');
28511 this.clearSelections();
28512 var el = this.dataName ?
28513 this.el.child('.roo-tpl-' + this.dataName) :
28516 el.dom.removeChild(this.nodes[index]);
28517 this.updateIndexes(index);
28521 * Refresh an individual node.
28522 * @param {Number} index
28524 refreshNode : function(index){
28525 this.onUpdate(this.store, this.store.getAt(index));
28528 updateIndexes : function(startIndex, endIndex){
28529 var ns = this.nodes;
28530 startIndex = startIndex || 0;
28531 endIndex = endIndex || ns.length - 1;
28532 for(var i = startIndex; i <= endIndex; i++){
28533 ns[i].nodeIndex = i;
28538 * Changes the data store this view uses and refresh the view.
28539 * @param {Store} store
28541 setStore : function(store, initial){
28542 if(!initial && this.store){
28543 this.store.un("datachanged", this.refresh);
28544 this.store.un("add", this.onAdd);
28545 this.store.un("remove", this.onRemove);
28546 this.store.un("update", this.onUpdate);
28547 this.store.un("clear", this.refresh);
28548 this.store.un("beforeload", this.onBeforeLoad);
28549 this.store.un("load", this.onLoad);
28550 this.store.un("loadexception", this.onLoad);
28554 store.on("datachanged", this.refresh, this);
28555 store.on("add", this.onAdd, this);
28556 store.on("remove", this.onRemove, this);
28557 store.on("update", this.onUpdate, this);
28558 store.on("clear", this.refresh, this);
28559 store.on("beforeload", this.onBeforeLoad, this);
28560 store.on("load", this.onLoad, this);
28561 store.on("loadexception", this.onLoad, this);
28569 * onbeforeLoad - masks the loading area.
28572 onBeforeLoad : function(store,opts)
28574 //Roo.log('onBeforeLoad');
28576 this.el.update("");
28578 this.el.mask(this.mask ? this.mask : "Loading" );
28580 onLoad : function ()
28587 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28588 * @param {HTMLElement} node
28589 * @return {HTMLElement} The template node
28591 findItemFromChild : function(node){
28592 var el = this.dataName ?
28593 this.el.child('.roo-tpl-' + this.dataName,true) :
28596 if(!node || node.parentNode == el){
28599 var p = node.parentNode;
28600 while(p && p != el){
28601 if(p.parentNode == el){
28610 onClick : function(e){
28611 var item = this.findItemFromChild(e.getTarget());
28613 var index = this.indexOf(item);
28614 if(this.onItemClick(item, index, e) !== false){
28615 this.fireEvent("click", this, index, item, e);
28618 this.clearSelections();
28623 onContextMenu : function(e){
28624 var item = this.findItemFromChild(e.getTarget());
28626 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28631 onDblClick : function(e){
28632 var item = this.findItemFromChild(e.getTarget());
28634 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28638 onItemClick : function(item, index, e)
28640 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28643 if (this.toggleSelect) {
28644 var m = this.isSelected(item) ? 'unselect' : 'select';
28647 _t[m](item, true, false);
28650 if(this.multiSelect || this.singleSelect){
28651 if(this.multiSelect && e.shiftKey && this.lastSelection){
28652 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28654 this.select(item, this.multiSelect && e.ctrlKey);
28655 this.lastSelection = item;
28658 if(!this.tickable){
28659 e.preventDefault();
28667 * Get the number of selected nodes.
28670 getSelectionCount : function(){
28671 return this.selections.length;
28675 * Get the currently selected nodes.
28676 * @return {Array} An array of HTMLElements
28678 getSelectedNodes : function(){
28679 return this.selections;
28683 * Get the indexes of the selected nodes.
28686 getSelectedIndexes : function(){
28687 var indexes = [], s = this.selections;
28688 for(var i = 0, len = s.length; i < len; i++){
28689 indexes.push(s[i].nodeIndex);
28695 * Clear all selections
28696 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28698 clearSelections : function(suppressEvent){
28699 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28700 this.cmp.elements = this.selections;
28701 this.cmp.removeClass(this.selectedClass);
28702 this.selections = [];
28703 if(!suppressEvent){
28704 this.fireEvent("selectionchange", this, this.selections);
28710 * Returns true if the passed node is selected
28711 * @param {HTMLElement/Number} node The node or node index
28712 * @return {Boolean}
28714 isSelected : function(node){
28715 var s = this.selections;
28719 node = this.getNode(node);
28720 return s.indexOf(node) !== -1;
28725 * @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
28726 * @param {Boolean} keepExisting (optional) true to keep existing selections
28727 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28729 select : function(nodeInfo, keepExisting, suppressEvent){
28730 if(nodeInfo instanceof Array){
28732 this.clearSelections(true);
28734 for(var i = 0, len = nodeInfo.length; i < len; i++){
28735 this.select(nodeInfo[i], true, true);
28739 var node = this.getNode(nodeInfo);
28740 if(!node || this.isSelected(node)){
28741 return; // already selected.
28744 this.clearSelections(true);
28747 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28748 Roo.fly(node).addClass(this.selectedClass);
28749 this.selections.push(node);
28750 if(!suppressEvent){
28751 this.fireEvent("selectionchange", this, this.selections);
28759 * @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
28760 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28761 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28763 unselect : function(nodeInfo, keepExisting, suppressEvent)
28765 if(nodeInfo instanceof Array){
28766 Roo.each(this.selections, function(s) {
28767 this.unselect(s, nodeInfo);
28771 var node = this.getNode(nodeInfo);
28772 if(!node || !this.isSelected(node)){
28773 //Roo.log("not selected");
28774 return; // not selected.
28778 Roo.each(this.selections, function(s) {
28780 Roo.fly(node).removeClass(this.selectedClass);
28787 this.selections= ns;
28788 this.fireEvent("selectionchange", this, this.selections);
28792 * Gets a template node.
28793 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28794 * @return {HTMLElement} The node or null if it wasn't found
28796 getNode : function(nodeInfo){
28797 if(typeof nodeInfo == "string"){
28798 return document.getElementById(nodeInfo);
28799 }else if(typeof nodeInfo == "number"){
28800 return this.nodes[nodeInfo];
28806 * Gets a range template nodes.
28807 * @param {Number} startIndex
28808 * @param {Number} endIndex
28809 * @return {Array} An array of nodes
28811 getNodes : function(start, end){
28812 var ns = this.nodes;
28813 start = start || 0;
28814 end = typeof end == "undefined" ? ns.length - 1 : end;
28817 for(var i = start; i <= end; i++){
28821 for(var i = start; i >= end; i--){
28829 * Finds the index of the passed node
28830 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28831 * @return {Number} The index of the node or -1
28833 indexOf : function(node){
28834 node = this.getNode(node);
28835 if(typeof node.nodeIndex == "number"){
28836 return node.nodeIndex;
28838 var ns = this.nodes;
28839 for(var i = 0, len = ns.length; i < len; i++){
28849 * Ext JS Library 1.1.1
28850 * Copyright(c) 2006-2007, Ext JS, LLC.
28852 * Originally Released Under LGPL - original licence link has changed is not relivant.
28855 * <script type="text/javascript">
28859 * @class Roo.JsonView
28860 * @extends Roo.View
28861 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28863 var view = new Roo.JsonView({
28864 container: "my-element",
28865 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
28870 // listen for node click?
28871 view.on("click", function(vw, index, node, e){
28872 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28875 // direct load of JSON data
28876 view.load("foobar.php");
28878 // Example from my blog list
28879 var tpl = new Roo.Template(
28880 '<div class="entry">' +
28881 '<a class="entry-title" href="{link}">{title}</a>' +
28882 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
28883 "</div><hr />"
28886 var moreView = new Roo.JsonView({
28887 container : "entry-list",
28891 moreView.on("beforerender", this.sortEntries, this);
28893 url: "/blog/get-posts.php",
28894 params: "allposts=true",
28895 text: "Loading Blog Entries..."
28899 * Note: old code is supported with arguments : (container, template, config)
28903 * Create a new JsonView
28905 * @param {Object} config The config object
28908 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28911 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28913 var um = this.el.getUpdateManager();
28914 um.setRenderer(this);
28915 um.on("update", this.onLoad, this);
28916 um.on("failure", this.onLoadException, this);
28919 * @event beforerender
28920 * Fires before rendering of the downloaded JSON data.
28921 * @param {Roo.JsonView} this
28922 * @param {Object} data The JSON data loaded
28926 * Fires when data is loaded.
28927 * @param {Roo.JsonView} this
28928 * @param {Object} data The JSON data loaded
28929 * @param {Object} response The raw Connect response object
28932 * @event loadexception
28933 * Fires when loading fails.
28934 * @param {Roo.JsonView} this
28935 * @param {Object} response The raw Connect response object
28938 'beforerender' : true,
28940 'loadexception' : true
28943 Roo.extend(Roo.JsonView, Roo.View, {
28945 * @type {String} The root property in the loaded JSON object that contains the data
28950 * Refreshes the view.
28952 refresh : function(){
28953 this.clearSelections();
28954 this.el.update("");
28956 var o = this.jsonData;
28957 if(o && o.length > 0){
28958 for(var i = 0, len = o.length; i < len; i++){
28959 var data = this.prepareData(o[i], i, o);
28960 html[html.length] = this.tpl.apply(data);
28963 html.push(this.emptyText);
28965 this.el.update(html.join(""));
28966 this.nodes = this.el.dom.childNodes;
28967 this.updateIndexes(0);
28971 * 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.
28972 * @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:
28975 url: "your-url.php",
28976 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28977 callback: yourFunction,
28978 scope: yourObject, //(optional scope)
28981 text: "Loading...",
28986 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28987 * 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.
28988 * @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}
28989 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28990 * @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.
28993 var um = this.el.getUpdateManager();
28994 um.update.apply(um, arguments);
28997 // note - render is a standard framework call...
28998 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28999 render : function(el, response){
29001 this.clearSelections();
29002 this.el.update("");
29005 if (response != '') {
29006 o = Roo.util.JSON.decode(response.responseText);
29009 o = o[this.jsonRoot];
29015 * The current JSON data or null
29018 this.beforeRender();
29023 * Get the number of records in the current JSON dataset
29026 getCount : function(){
29027 return this.jsonData ? this.jsonData.length : 0;
29031 * Returns the JSON object for the specified node(s)
29032 * @param {HTMLElement/Array} node The node or an array of nodes
29033 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29034 * you get the JSON object for the node
29036 getNodeData : function(node){
29037 if(node instanceof Array){
29039 for(var i = 0, len = node.length; i < len; i++){
29040 data.push(this.getNodeData(node[i]));
29044 return this.jsonData[this.indexOf(node)] || null;
29047 beforeRender : function(){
29048 this.snapshot = this.jsonData;
29050 this.sort.apply(this, this.sortInfo);
29052 this.fireEvent("beforerender", this, this.jsonData);
29055 onLoad : function(el, o){
29056 this.fireEvent("load", this, this.jsonData, o);
29059 onLoadException : function(el, o){
29060 this.fireEvent("loadexception", this, o);
29064 * Filter the data by a specific property.
29065 * @param {String} property A property on your JSON objects
29066 * @param {String/RegExp} value Either string that the property values
29067 * should start with, or a RegExp to test against the property
29069 filter : function(property, value){
29072 var ss = this.snapshot;
29073 if(typeof value == "string"){
29074 var vlen = value.length;
29076 this.clearFilter();
29079 value = value.toLowerCase();
29080 for(var i = 0, len = ss.length; i < len; i++){
29082 if(o[property].substr(0, vlen).toLowerCase() == value){
29086 } else if(value.exec){ // regex?
29087 for(var i = 0, len = ss.length; i < len; i++){
29089 if(value.test(o[property])){
29096 this.jsonData = data;
29102 * Filter by a function. The passed function will be called with each
29103 * object in the current dataset. If the function returns true the value is kept,
29104 * otherwise it is filtered.
29105 * @param {Function} fn
29106 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29108 filterBy : function(fn, scope){
29111 var ss = this.snapshot;
29112 for(var i = 0, len = ss.length; i < len; i++){
29114 if(fn.call(scope || this, o)){
29118 this.jsonData = data;
29124 * Clears the current filter.
29126 clearFilter : function(){
29127 if(this.snapshot && this.jsonData != this.snapshot){
29128 this.jsonData = this.snapshot;
29135 * Sorts the data for this view and refreshes it.
29136 * @param {String} property A property on your JSON objects to sort on
29137 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29138 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29140 sort : function(property, dir, sortType){
29141 this.sortInfo = Array.prototype.slice.call(arguments, 0);
29144 var dsc = dir && dir.toLowerCase() == "desc";
29145 var f = function(o1, o2){
29146 var v1 = sortType ? sortType(o1[p]) : o1[p];
29147 var v2 = sortType ? sortType(o2[p]) : o2[p];
29150 return dsc ? +1 : -1;
29151 } else if(v1 > v2){
29152 return dsc ? -1 : +1;
29157 this.jsonData.sort(f);
29159 if(this.jsonData != this.snapshot){
29160 this.snapshot.sort(f);
29166 * Ext JS Library 1.1.1
29167 * Copyright(c) 2006-2007, Ext JS, LLC.
29169 * Originally Released Under LGPL - original licence link has changed is not relivant.
29172 * <script type="text/javascript">
29177 * @class Roo.ColorPalette
29178 * @extends Roo.Component
29179 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
29180 * Here's an example of typical usage:
29182 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
29183 cp.render('my-div');
29185 cp.on('select', function(palette, selColor){
29186 // do something with selColor
29190 * Create a new ColorPalette
29191 * @param {Object} config The config object
29193 Roo.ColorPalette = function(config){
29194 Roo.ColorPalette.superclass.constructor.call(this, config);
29198 * Fires when a color is selected
29199 * @param {ColorPalette} this
29200 * @param {String} color The 6-digit color hex code (without the # symbol)
29206 this.on("select", this.handler, this.scope, true);
29209 Roo.extend(Roo.ColorPalette, Roo.Component, {
29211 * @cfg {String} itemCls
29212 * The CSS class to apply to the containing element (defaults to "x-color-palette")
29214 itemCls : "x-color-palette",
29216 * @cfg {String} value
29217 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
29218 * the hex codes are case-sensitive.
29221 clickEvent:'click',
29223 ctype: "Roo.ColorPalette",
29226 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29228 allowReselect : false,
29231 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
29232 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
29233 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29234 * of colors with the width setting until the box is symmetrical.</p>
29235 * <p>You can override individual colors if needed:</p>
29237 var cp = new Roo.ColorPalette();
29238 cp.colors[0] = "FF0000"; // change the first box to red
29241 Or you can provide a custom array of your own for complete control:
29243 var cp = new Roo.ColorPalette();
29244 cp.colors = ["000000", "993300", "333300"];
29249 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29250 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29251 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29252 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29253 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29257 onRender : function(container, position){
29258 var t = new Roo.MasterTemplate(
29259 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
29261 var c = this.colors;
29262 for(var i = 0, len = c.length; i < len; i++){
29265 var el = document.createElement("div");
29266 el.className = this.itemCls;
29268 container.dom.insertBefore(el, position);
29269 this.el = Roo.get(el);
29270 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
29271 if(this.clickEvent != 'click'){
29272 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
29277 afterRender : function(){
29278 Roo.ColorPalette.superclass.afterRender.call(this);
29280 var s = this.value;
29287 handleClick : function(e, t){
29288 e.preventDefault();
29289 if(!this.disabled){
29290 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29291 this.select(c.toUpperCase());
29296 * Selects the specified color in the palette (fires the select event)
29297 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29299 select : function(color){
29300 color = color.replace("#", "");
29301 if(color != this.value || this.allowReselect){
29304 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29306 el.child("a.color-"+color).addClass("x-color-palette-sel");
29307 this.value = color;
29308 this.fireEvent("select", this, color);
29313 * Ext JS Library 1.1.1
29314 * Copyright(c) 2006-2007, Ext JS, LLC.
29316 * Originally Released Under LGPL - original licence link has changed is not relivant.
29319 * <script type="text/javascript">
29323 * @class Roo.DatePicker
29324 * @extends Roo.Component
29325 * Simple date picker class.
29327 * Create a new DatePicker
29328 * @param {Object} config The config object
29330 Roo.DatePicker = function(config){
29331 Roo.DatePicker.superclass.constructor.call(this, config);
29333 this.value = config && config.value ?
29334 config.value.clearTime() : new Date().clearTime();
29339 * Fires when a date is selected
29340 * @param {DatePicker} this
29341 * @param {Date} date The selected date
29345 * @event monthchange
29346 * Fires when the displayed month changes
29347 * @param {DatePicker} this
29348 * @param {Date} date The selected month
29350 'monthchange': true
29354 this.on("select", this.handler, this.scope || this);
29356 // build the disabledDatesRE
29357 if(!this.disabledDatesRE && this.disabledDates){
29358 var dd = this.disabledDates;
29360 for(var i = 0; i < dd.length; i++){
29362 if(i != dd.length-1) {
29366 this.disabledDatesRE = new RegExp(re + ")");
29370 Roo.extend(Roo.DatePicker, Roo.Component, {
29372 * @cfg {String} todayText
29373 * The text to display on the button that selects the current date (defaults to "Today")
29375 todayText : "Today",
29377 * @cfg {String} okText
29378 * The text to display on the ok button
29380 okText : " OK ", //   to give the user extra clicking room
29382 * @cfg {String} cancelText
29383 * The text to display on the cancel button
29385 cancelText : "Cancel",
29387 * @cfg {String} todayTip
29388 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29390 todayTip : "{0} (Spacebar)",
29392 * @cfg {Date} minDate
29393 * Minimum allowable date (JavaScript date object, defaults to null)
29397 * @cfg {Date} maxDate
29398 * Maximum allowable date (JavaScript date object, defaults to null)
29402 * @cfg {String} minText
29403 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29405 minText : "This date is before the minimum date",
29407 * @cfg {String} maxText
29408 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29410 maxText : "This date is after the maximum date",
29412 * @cfg {String} format
29413 * The default date format string which can be overriden for localization support. The format must be
29414 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29418 * @cfg {Array} disabledDays
29419 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29421 disabledDays : null,
29423 * @cfg {String} disabledDaysText
29424 * The tooltip to display when the date falls on a disabled day (defaults to "")
29426 disabledDaysText : "",
29428 * @cfg {RegExp} disabledDatesRE
29429 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29431 disabledDatesRE : null,
29433 * @cfg {String} disabledDatesText
29434 * The tooltip text to display when the date falls on a disabled date (defaults to "")
29436 disabledDatesText : "",
29438 * @cfg {Boolean} constrainToViewport
29439 * True to constrain the date picker to the viewport (defaults to true)
29441 constrainToViewport : true,
29443 * @cfg {Array} monthNames
29444 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29446 monthNames : Date.monthNames,
29448 * @cfg {Array} dayNames
29449 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29451 dayNames : Date.dayNames,
29453 * @cfg {String} nextText
29454 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29456 nextText: 'Next Month (Control+Right)',
29458 * @cfg {String} prevText
29459 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29461 prevText: 'Previous Month (Control+Left)',
29463 * @cfg {String} monthYearText
29464 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29466 monthYearText: 'Choose a month (Control+Up/Down to move years)',
29468 * @cfg {Number} startDay
29469 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29473 * @cfg {Bool} showClear
29474 * Show a clear button (usefull for date form elements that can be blank.)
29480 * Sets the value of the date field
29481 * @param {Date} value The date to set
29483 setValue : function(value){
29484 var old = this.value;
29486 if (typeof(value) == 'string') {
29488 value = Date.parseDate(value, this.format);
29491 value = new Date();
29494 this.value = value.clearTime(true);
29496 this.update(this.value);
29501 * Gets the current selected value of the date field
29502 * @return {Date} The selected date
29504 getValue : function(){
29509 focus : function(){
29511 this.update(this.activeDate);
29516 onRender : function(container, position){
29519 '<table cellspacing="0">',
29520 '<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>',
29521 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29522 var dn = this.dayNames;
29523 for(var i = 0; i < 7; i++){
29524 var d = this.startDay+i;
29528 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29530 m[m.length] = "</tr></thead><tbody><tr>";
29531 for(var i = 0; i < 42; i++) {
29532 if(i % 7 == 0 && i != 0){
29533 m[m.length] = "</tr><tr>";
29535 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29537 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29538 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29540 var el = document.createElement("div");
29541 el.className = "x-date-picker";
29542 el.innerHTML = m.join("");
29544 container.dom.insertBefore(el, position);
29546 this.el = Roo.get(el);
29547 this.eventEl = Roo.get(el.firstChild);
29549 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29550 handler: this.showPrevMonth,
29552 preventDefault:true,
29556 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29557 handler: this.showNextMonth,
29559 preventDefault:true,
29563 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
29565 this.monthPicker = this.el.down('div.x-date-mp');
29566 this.monthPicker.enableDisplayMode('block');
29568 var kn = new Roo.KeyNav(this.eventEl, {
29569 "left" : function(e){
29571 this.showPrevMonth() :
29572 this.update(this.activeDate.add("d", -1));
29575 "right" : function(e){
29577 this.showNextMonth() :
29578 this.update(this.activeDate.add("d", 1));
29581 "up" : function(e){
29583 this.showNextYear() :
29584 this.update(this.activeDate.add("d", -7));
29587 "down" : function(e){
29589 this.showPrevYear() :
29590 this.update(this.activeDate.add("d", 7));
29593 "pageUp" : function(e){
29594 this.showNextMonth();
29597 "pageDown" : function(e){
29598 this.showPrevMonth();
29601 "enter" : function(e){
29602 e.stopPropagation();
29609 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
29611 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
29613 this.el.unselectable();
29615 this.cells = this.el.select("table.x-date-inner tbody td");
29616 this.textNodes = this.el.query("table.x-date-inner tbody span");
29618 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29620 tooltip: this.monthYearText
29623 this.mbtn.on('click', this.showMonthPicker, this);
29624 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29627 var today = (new Date()).dateFormat(this.format);
29629 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29630 if (this.showClear) {
29631 baseTb.add( new Roo.Toolbar.Fill());
29634 text: String.format(this.todayText, today),
29635 tooltip: String.format(this.todayTip, today),
29636 handler: this.selectToday,
29640 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29643 if (this.showClear) {
29645 baseTb.add( new Roo.Toolbar.Fill());
29648 cls: 'x-btn-icon x-btn-clear',
29649 handler: function() {
29651 this.fireEvent("select", this, '');
29661 this.update(this.value);
29664 createMonthPicker : function(){
29665 if(!this.monthPicker.dom.firstChild){
29666 var buf = ['<table border="0" cellspacing="0">'];
29667 for(var i = 0; i < 6; i++){
29669 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29670 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29672 '<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>' :
29673 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29677 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29679 '</button><button type="button" class="x-date-mp-cancel">',
29681 '</button></td></tr>',
29684 this.monthPicker.update(buf.join(''));
29685 this.monthPicker.on('click', this.onMonthClick, this);
29686 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29688 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29689 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29691 this.mpMonths.each(function(m, a, i){
29694 m.dom.xmonth = 5 + Math.round(i * .5);
29696 m.dom.xmonth = Math.round((i-1) * .5);
29702 showMonthPicker : function(){
29703 this.createMonthPicker();
29704 var size = this.el.getSize();
29705 this.monthPicker.setSize(size);
29706 this.monthPicker.child('table').setSize(size);
29708 this.mpSelMonth = (this.activeDate || this.value).getMonth();
29709 this.updateMPMonth(this.mpSelMonth);
29710 this.mpSelYear = (this.activeDate || this.value).getFullYear();
29711 this.updateMPYear(this.mpSelYear);
29713 this.monthPicker.slideIn('t', {duration:.2});
29716 updateMPYear : function(y){
29718 var ys = this.mpYears.elements;
29719 for(var i = 1; i <= 10; i++){
29720 var td = ys[i-1], y2;
29722 y2 = y + Math.round(i * .5);
29723 td.firstChild.innerHTML = y2;
29726 y2 = y - (5-Math.round(i * .5));
29727 td.firstChild.innerHTML = y2;
29730 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29734 updateMPMonth : function(sm){
29735 this.mpMonths.each(function(m, a, i){
29736 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29740 selectMPMonth: function(m){
29744 onMonthClick : function(e, t){
29746 var el = new Roo.Element(t), pn;
29747 if(el.is('button.x-date-mp-cancel')){
29748 this.hideMonthPicker();
29750 else if(el.is('button.x-date-mp-ok')){
29751 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29752 this.hideMonthPicker();
29754 else if(pn = el.up('td.x-date-mp-month', 2)){
29755 this.mpMonths.removeClass('x-date-mp-sel');
29756 pn.addClass('x-date-mp-sel');
29757 this.mpSelMonth = pn.dom.xmonth;
29759 else if(pn = el.up('td.x-date-mp-year', 2)){
29760 this.mpYears.removeClass('x-date-mp-sel');
29761 pn.addClass('x-date-mp-sel');
29762 this.mpSelYear = pn.dom.xyear;
29764 else if(el.is('a.x-date-mp-prev')){
29765 this.updateMPYear(this.mpyear-10);
29767 else if(el.is('a.x-date-mp-next')){
29768 this.updateMPYear(this.mpyear+10);
29772 onMonthDblClick : function(e, t){
29774 var el = new Roo.Element(t), pn;
29775 if(pn = el.up('td.x-date-mp-month', 2)){
29776 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29777 this.hideMonthPicker();
29779 else if(pn = el.up('td.x-date-mp-year', 2)){
29780 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29781 this.hideMonthPicker();
29785 hideMonthPicker : function(disableAnim){
29786 if(this.monthPicker){
29787 if(disableAnim === true){
29788 this.monthPicker.hide();
29790 this.monthPicker.slideOut('t', {duration:.2});
29796 showPrevMonth : function(e){
29797 this.update(this.activeDate.add("mo", -1));
29801 showNextMonth : function(e){
29802 this.update(this.activeDate.add("mo", 1));
29806 showPrevYear : function(){
29807 this.update(this.activeDate.add("y", -1));
29811 showNextYear : function(){
29812 this.update(this.activeDate.add("y", 1));
29816 handleMouseWheel : function(e){
29817 var delta = e.getWheelDelta();
29819 this.showPrevMonth();
29821 } else if(delta < 0){
29822 this.showNextMonth();
29828 handleDateClick : function(e, t){
29830 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29831 this.setValue(new Date(t.dateValue));
29832 this.fireEvent("select", this, this.value);
29837 selectToday : function(){
29838 this.setValue(new Date().clearTime());
29839 this.fireEvent("select", this, this.value);
29843 update : function(date)
29845 var vd = this.activeDate;
29846 this.activeDate = date;
29848 var t = date.getTime();
29849 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29850 this.cells.removeClass("x-date-selected");
29851 this.cells.each(function(c){
29852 if(c.dom.firstChild.dateValue == t){
29853 c.addClass("x-date-selected");
29854 setTimeout(function(){
29855 try{c.dom.firstChild.focus();}catch(e){}
29864 var days = date.getDaysInMonth();
29865 var firstOfMonth = date.getFirstDateOfMonth();
29866 var startingPos = firstOfMonth.getDay()-this.startDay;
29868 if(startingPos <= this.startDay){
29872 var pm = date.add("mo", -1);
29873 var prevStart = pm.getDaysInMonth()-startingPos;
29875 var cells = this.cells.elements;
29876 var textEls = this.textNodes;
29877 days += startingPos;
29879 // convert everything to numbers so it's fast
29880 var day = 86400000;
29881 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29882 var today = new Date().clearTime().getTime();
29883 var sel = date.clearTime().getTime();
29884 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29885 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29886 var ddMatch = this.disabledDatesRE;
29887 var ddText = this.disabledDatesText;
29888 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29889 var ddaysText = this.disabledDaysText;
29890 var format = this.format;
29892 var setCellClass = function(cal, cell){
29894 var t = d.getTime();
29895 cell.firstChild.dateValue = t;
29897 cell.className += " x-date-today";
29898 cell.title = cal.todayText;
29901 cell.className += " x-date-selected";
29902 setTimeout(function(){
29903 try{cell.firstChild.focus();}catch(e){}
29908 cell.className = " x-date-disabled";
29909 cell.title = cal.minText;
29913 cell.className = " x-date-disabled";
29914 cell.title = cal.maxText;
29918 if(ddays.indexOf(d.getDay()) != -1){
29919 cell.title = ddaysText;
29920 cell.className = " x-date-disabled";
29923 if(ddMatch && format){
29924 var fvalue = d.dateFormat(format);
29925 if(ddMatch.test(fvalue)){
29926 cell.title = ddText.replace("%0", fvalue);
29927 cell.className = " x-date-disabled";
29933 for(; i < startingPos; i++) {
29934 textEls[i].innerHTML = (++prevStart);
29935 d.setDate(d.getDate()+1);
29936 cells[i].className = "x-date-prevday";
29937 setCellClass(this, cells[i]);
29939 for(; i < days; i++){
29940 intDay = i - startingPos + 1;
29941 textEls[i].innerHTML = (intDay);
29942 d.setDate(d.getDate()+1);
29943 cells[i].className = "x-date-active";
29944 setCellClass(this, cells[i]);
29947 for(; i < 42; i++) {
29948 textEls[i].innerHTML = (++extraDays);
29949 d.setDate(d.getDate()+1);
29950 cells[i].className = "x-date-nextday";
29951 setCellClass(this, cells[i]);
29954 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29955 this.fireEvent('monthchange', this, date);
29957 if(!this.internalRender){
29958 var main = this.el.dom.firstChild;
29959 var w = main.offsetWidth;
29960 this.el.setWidth(w + this.el.getBorderWidth("lr"));
29961 Roo.fly(main).setWidth(w);
29962 this.internalRender = true;
29963 // opera does not respect the auto grow header center column
29964 // then, after it gets a width opera refuses to recalculate
29965 // without a second pass
29966 if(Roo.isOpera && !this.secondPass){
29967 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29968 this.secondPass = true;
29969 this.update.defer(10, this, [date]);
29981 * @class Roo.panel.Cropbox
29982 * @extends Roo.BoxComponent
29983 * Panel Cropbox class
29984 * @cfg {String} emptyText show when image has been loaded
29985 * @cfg {String} rotateNotify show when image too small to rotate
29986 * @cfg {Number} errorTimeout default 3000
29987 * @cfg {Number} minWidth default 300
29988 * @cfg {Number} minHeight default 300
29989 * @cfg {Number} outputMaxWidth default 1200
29990 * @cfg {Number} windowSize default 300
29991 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29992 * @cfg {Boolean} isDocument (true|false) default false
29993 * @cfg {String} url action url
29994 * @cfg {String} paramName default 'imageUpload'
29995 * @cfg {String} method default POST
29996 * @cfg {Boolean} loadMask (true|false) default true
29997 * @cfg {Boolean} loadingText default 'Loading...'
30000 * Create a new Cropbox
30001 * @param {Object} config The config object
30004 Roo.panel.Cropbox = function(config){
30005 Roo.panel.Cropbox.superclass.constructor.call(this, config);
30009 * @event beforeselectfile
30010 * Fire before select file
30011 * @param {Roo.panel.Cropbox} this
30013 "beforeselectfile" : true,
30016 * Fire after initEvent
30017 * @param {Roo.panel.Cropbox} this
30022 * Fire after initEvent
30023 * @param {Roo.panel.Cropbox} this
30024 * @param {String} data
30029 * Fire when preparing the file data
30030 * @param {Roo.panel.Cropbox} this
30031 * @param {Object} file
30036 * Fire when get exception
30037 * @param {Roo.panel.Cropbox} this
30038 * @param {XMLHttpRequest} xhr
30040 "exception" : true,
30042 * @event beforeloadcanvas
30043 * Fire before load the canvas
30044 * @param {Roo.panel.Cropbox} this
30045 * @param {String} src
30047 "beforeloadcanvas" : true,
30050 * Fire when trash image
30051 * @param {Roo.panel.Cropbox} this
30056 * Fire when download the image
30057 * @param {Roo.panel.Cropbox} this
30061 * @event footerbuttonclick
30062 * Fire when footerbuttonclick
30063 * @param {Roo.panel.Cropbox} this
30064 * @param {String} type
30066 "footerbuttonclick" : true,
30070 * @param {Roo.panel.Cropbox} this
30075 * Fire when rotate the image
30076 * @param {Roo.panel.Cropbox} this
30077 * @param {String} pos
30082 * Fire when inspect the file
30083 * @param {Roo.panel.Cropbox} this
30084 * @param {Object} file
30089 * Fire when xhr upload the file
30090 * @param {Roo.panel.Cropbox} this
30091 * @param {Object} data
30096 * Fire when arrange the file data
30097 * @param {Roo.panel.Cropbox} this
30098 * @param {Object} formData
30102 * @event loadcanvas
30103 * Fire after load the canvas
30104 * @param {Roo.panel.Cropbox}
30105 * @param {Object} imgEl
30107 "loadcanvas" : true
30110 this.buttons = this.buttons || Roo.panel.Cropbox.footer.STANDARD;
30113 Roo.extend(Roo.panel.Cropbox, Roo.Component, {
30115 emptyText : 'Click to upload image',
30116 rotateNotify : 'Image is too small to rotate',
30117 errorTimeout : 3000,
30128 outputMaxWidth : 1200,
30133 cropType : 'image/jpeg',
30135 canvasLoaded : false,
30136 isDocument : false,
30138 paramName : 'imageUpload',
30140 loadingText : 'Loading...',
30143 getAutoCreate : function()
30147 cls : 'roo-upload-cropbox',
30151 cls : 'roo-upload-cropbox-selector',
30156 cls : 'roo-upload-cropbox-body',
30157 style : 'cursor:pointer',
30161 cls : 'roo-upload-cropbox-preview'
30165 cls : 'roo-upload-cropbox-thumb'
30169 cls : 'roo-upload-cropbox-empty-notify',
30170 html : this.emptyText
30174 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30175 html : this.rotateNotify
30181 cls : 'roo-upload-cropbox-footer',
30184 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30194 onRender : function(ct, position)
30196 Roo.panel.Cropbox.superclass.onRender.call(this, ct, position);
30199 if (this.el.attr('xtype')) {
30200 this.el.attr('xtypex', this.el.attr('xtype'));
30201 this.el.dom.removeAttribute('xtype');
30207 var cfg = Roo.apply({}, this.getAutoCreate());
30209 cfg.id = this.id || Roo.id();
30212 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
30215 if (this.style) { // fixme needs to support more complex style data.
30216 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
30219 this.el = ct.createChild(cfg, position);
30224 if (this.buttons.length) {
30226 Roo.each(this.buttons, function(bb) {
30228 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30230 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30236 this.maskEl = this.el;
30240 initEvents : function()
30242 this.urlAPI = (window.createObjectURL && window) ||
30243 (window.URL && URL.revokeObjectURL && URL) ||
30244 (window.webkitURL && webkitURL);
30246 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30247 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30249 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30250 this.selectorEl.hide();
30252 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30253 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30255 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30256 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30257 this.thumbEl.hide();
30259 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30260 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30262 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30263 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30264 this.errorEl.hide();
30266 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30267 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30268 this.footerEl.hide();
30270 this.setThumbBoxSize();
30276 this.fireEvent('initial', this);
30283 window.addEventListener("resize", function() { _this.resize(); } );
30285 this.bodyEl.on('click', this.beforeSelectFile, this);
30288 this.bodyEl.on('touchstart', this.onTouchStart, this);
30289 this.bodyEl.on('touchmove', this.onTouchMove, this);
30290 this.bodyEl.on('touchend', this.onTouchEnd, this);
30294 this.bodyEl.on('mousedown', this.onMouseDown, this);
30295 this.bodyEl.on('mousemove', this.onMouseMove, this);
30296 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30297 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30298 Roo.get(document).on('mouseup', this.onMouseUp, this);
30301 this.selectorEl.on('change', this.onFileSelected, this);
30307 this.baseScale = 1;
30309 this.baseRotate = 1;
30310 this.dragable = false;
30311 this.pinching = false;
30314 this.cropData = false;
30315 this.notifyEl.dom.innerHTML = this.emptyText;
30317 // this.selectorEl.dom.value = '';
30321 resize : function()
30323 if(this.fireEvent('resize', this) != false){
30324 this.setThumbBoxPosition();
30325 this.setCanvasPosition();
30329 onFooterButtonClick : function(e, el, o, type)
30332 case 'rotate-left' :
30333 this.onRotateLeft(e);
30335 case 'rotate-right' :
30336 this.onRotateRight(e);
30339 this.beforeSelectFile(e);
30357 this.fireEvent('footerbuttonclick', this, type);
30360 beforeSelectFile : function(e)
30362 e.preventDefault();
30364 if(this.fireEvent('beforeselectfile', this) != false){
30365 this.selectorEl.dom.click();
30369 onFileSelected : function(e)
30371 e.preventDefault();
30373 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30377 var file = this.selectorEl.dom.files[0];
30379 if(this.fireEvent('inspect', this, file) != false){
30380 this.prepare(file);
30385 trash : function(e)
30387 this.fireEvent('trash', this);
30390 download : function(e)
30392 this.fireEvent('download', this);
30395 center : function(e)
30397 this.setCanvasPosition();
30400 loadCanvas : function(src)
30402 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30406 this.imageEl = document.createElement('img');
30410 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30412 this.imageEl.src = src;
30416 onLoadCanvas : function()
30418 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30419 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30421 if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
30423 this.bodyEl.un('click', this.beforeSelectFile, this);
30425 this.notifyEl.hide();
30426 this.thumbEl.show();
30427 this.footerEl.show();
30429 this.baseRotateLevel();
30431 if(this.isDocument){
30432 this.setThumbBoxSize();
30435 this.setThumbBoxPosition();
30437 this.baseScaleLevel();
30443 this.canvasLoaded = true;
30448 this.maskEl.unmask();
30453 setCanvasPosition : function(center = true)
30455 if(!this.canvasEl){
30459 var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30460 var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30463 this.previewEl.setLeft(newCenterLeft);
30464 this.previewEl.setTop(newCenterTop);
30469 var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
30470 var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
30471 var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
30473 var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
30474 var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
30476 var leftDiff = newCenterLeft - oldCenterLeft;
30477 var topDiff = newCenterTop - oldCenterTop;
30479 var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
30480 var newPreviewTop = this.previewEl.getTop(true) + topDiff;
30482 this.previewEl.setLeft(newPreviewLeft);
30483 this.previewEl.setTop(newPreviewTop);
30487 onMouseDown : function(e)
30491 this.dragable = true;
30492 this.pinching = false;
30494 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30495 this.dragable = false;
30499 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30500 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30504 onMouseMove : function(e)
30508 if(!this.canvasLoaded){
30512 if (!this.dragable){
30516 var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
30517 var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
30519 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
30520 maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
30523 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
30524 maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
30527 var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
30528 var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
30530 var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
30531 var maxY = Math.ceil(this.thumbEl.getTop(true) + maxPaddingTop);
30545 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30546 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30548 x = x - this.mouseX;
30549 y = y - this.mouseY;
30551 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30552 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30554 bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
30555 bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
30557 this.previewEl.setLeft(bgX);
30558 this.previewEl.setTop(bgY);
30560 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30561 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30564 onMouseUp : function(e)
30568 this.dragable = false;
30571 onMouseWheel : function(e)
30575 this.startScale = this.scale;
30576 this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
30578 if(!this.zoomable()){
30579 this.scale = this.startScale;
30589 zoomable : function()
30591 var minScale = this.thumbEl.getWidth() / this.minWidth;
30593 if(this.minWidth < this.minHeight){
30594 minScale = this.thumbEl.getHeight() / this.minHeight;
30597 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30598 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30600 var maxWidth = this.imageEl.OriginWidth;
30601 var maxHeight = this.imageEl.OriginHeight;
30604 var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
30605 var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
30607 var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30608 var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30610 var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
30611 var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
30613 var leftDiff = newCenterLeft - oldCenterLeft;
30614 var topDiff = newCenterTop - oldCenterTop;
30616 var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
30617 var newPreviewTop = this.previewEl.getTop(true) + topDiff;
30619 var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
30620 var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
30622 var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
30623 var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
30625 var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
30626 var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
30628 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
30629 maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
30632 if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
30633 maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
30638 (this.rotate == 0 || this.rotate == 180) &&
30640 width > this.imageEl.OriginWidth ||
30641 height > this.imageEl.OriginHeight ||
30642 (width < this.minWidth && height < this.minHeight)
30650 (this.rotate == 90 || this.rotate == 270) &&
30652 width > this.imageEl.OriginWidth ||
30653 height > this.imageEl.OriginHeight ||
30654 (width < this.minHeight && height < this.minWidth)
30661 !this.isDocument &&
30662 (this.rotate == 0 || this.rotate == 180) &&
30665 paddingLeft > maxPaddingLeft ||
30666 paddingRight > maxPaddingLeft ||
30667 paddingTop > maxPaddingTop ||
30668 paddingBottom > maxPaddingTop ||
30670 width > maxWidth ||
30678 !this.isDocument &&
30679 (this.rotate == 90 || this.rotate == 270) &&
30681 width < this.minHeight ||
30682 width > this.imageEl.OriginWidth ||
30683 height < this.minWidth ||
30684 height > this.imageEl.OriginHeight
30694 onRotateLeft : function(e)
30696 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30698 var minScale = this.thumbEl.getWidth() / this.minWidth;
30700 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30701 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30703 this.startScale = this.scale;
30705 while (this.getScaleLevel() < minScale){
30707 this.scale = this.scale + 1;
30709 if(!this.zoomable()){
30714 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30715 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30720 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30727 this.scale = this.startScale;
30729 this.onRotateFail();
30734 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30736 if(this.isDocument){
30737 this.setThumbBoxSize();
30738 this.setThumbBoxPosition();
30739 this.setCanvasPosition();
30744 this.fireEvent('rotate', this, 'left');
30748 onRotateRight : function(e)
30750 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30752 var minScale = this.thumbEl.getWidth() / this.minWidth;
30754 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30755 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30757 this.startScale = this.scale;
30759 while (this.getScaleLevel() < minScale){
30761 this.scale = this.scale + 1;
30763 if(!this.zoomable()){
30768 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30769 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30774 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30781 this.scale = this.startScale;
30783 this.onRotateFail();
30788 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30790 if(this.isDocument){
30791 this.setThumbBoxSize();
30792 this.setThumbBoxPosition();
30793 this.setCanvasPosition();
30798 this.fireEvent('rotate', this, 'right');
30801 onRotateFail : function()
30803 this.errorEl.show(true);
30807 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30812 this.previewEl.dom.innerHTML = '';
30814 var canvasEl = document.createElement("canvas");
30816 var contextEl = canvasEl.getContext("2d");
30818 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30819 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30820 var center = this.imageEl.OriginWidth / 2;
30822 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30823 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30824 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30825 center = this.imageEl.OriginHeight / 2;
30828 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30830 contextEl.translate(center, center);
30831 contextEl.rotate(this.rotate * Math.PI / 180);
30833 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30835 this.canvasEl = document.createElement("canvas");
30837 this.contextEl = this.canvasEl.getContext("2d");
30839 switch (this.rotate) {
30842 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30843 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30845 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30850 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30851 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30853 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30854 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30858 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30863 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30864 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30866 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30867 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30871 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30876 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30877 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30879 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30880 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30884 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30891 this.previewEl.appendChild(this.canvasEl);
30893 this.setCanvasPosition(false);
30898 if(!this.canvasLoaded){
30902 var imageCanvas = document.createElement("canvas");
30904 var imageContext = imageCanvas.getContext("2d");
30906 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30907 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30909 var center = imageCanvas.width / 2;
30911 imageContext.translate(center, center);
30913 imageContext.rotate(this.rotate * Math.PI / 180);
30915 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30917 var canvas = document.createElement("canvas");
30919 var context = canvas.getContext("2d");
30921 canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
30923 canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
30925 switch (this.rotate) {
30928 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30929 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30931 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30932 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30934 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
30935 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
30937 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30938 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30940 if(canvas.width > this.outputMaxWidth) {
30941 var scale = this.outputMaxWidth / canvas.width;
30942 canvas.width = canvas.width * scale;
30943 canvas.height = canvas.height * scale;
30944 context.scale(scale, scale);
30947 context.fillStyle = 'white';
30948 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
30951 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30956 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30957 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30959 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30960 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30962 var targetWidth = this.minWidth - 2 * x;
30963 var targetHeight = this.minHeight - 2 * y;
30967 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30968 scale = targetWidth / width;
30971 if(x > 0 && y == 0){
30972 scale = targetHeight / height;
30975 if(x > 0 && y > 0){
30976 scale = targetWidth / width;
30978 if(width < height){
30979 scale = targetHeight / height;
30983 context.scale(scale, scale);
30985 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30986 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30988 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30989 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30991 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30993 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30998 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30999 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31001 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31002 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31004 var targetWidth = this.minWidth - 2 * x;
31005 var targetHeight = this.minHeight - 2 * y;
31009 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31010 scale = targetWidth / width;
31013 if(x > 0 && y == 0){
31014 scale = targetHeight / height;
31017 if(x > 0 && y > 0){
31018 scale = targetWidth / width;
31020 if(width < height){
31021 scale = targetHeight / height;
31025 context.scale(scale, scale);
31027 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31028 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31030 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31031 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31033 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31034 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31036 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31041 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31042 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31044 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31045 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31047 var targetWidth = this.minWidth - 2 * x;
31048 var targetHeight = this.minHeight - 2 * y;
31052 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31053 scale = targetWidth / width;
31056 if(x > 0 && y == 0){
31057 scale = targetHeight / height;
31060 if(x > 0 && y > 0){
31061 scale = targetWidth / width;
31063 if(width < height){
31064 scale = targetHeight / height;
31068 context.scale(scale, scale);
31069 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31070 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31072 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31073 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31075 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31077 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31084 this.cropData = canvas.toDataURL(this.cropType);
31086 if(this.fireEvent('crop', this, this.cropData) !== false){
31087 this.process(this.file, this.cropData);
31094 setThumbBoxSize : function()
31098 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31099 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31100 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31102 this.minWidth = width;
31103 this.minHeight = height;
31105 if(this.rotate == 90 || this.rotate == 270){
31106 this.minWidth = height;
31107 this.minHeight = width;
31111 height = this.windowSize;
31112 width = Math.ceil(this.minWidth * height / this.minHeight);
31114 if(this.minWidth > this.minHeight){
31115 width = this.windowSize;
31116 height = Math.ceil(this.minHeight * width / this.minWidth);
31119 this.thumbEl.setStyle({
31120 width : width + 'px',
31121 height : height + 'px'
31128 setThumbBoxPosition : function()
31130 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31131 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31133 this.thumbEl.setLeft(x);
31134 this.thumbEl.setTop(y);
31138 baseRotateLevel : function()
31140 this.baseRotate = 1;
31143 typeof(this.exif) != 'undefined' &&
31144 typeof(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != 'undefined' &&
31145 [1, 3, 6, 8].indexOf(this.exif[Roo.panel.Cropbox['tags']['Orientation']]) != -1
31147 this.baseRotate = this.exif[Roo.panel.Cropbox['tags']['Orientation']];
31150 this.rotate = Roo.panel.Cropbox['Orientation'][this.baseRotate];
31154 baseScaleLevel : function()
31158 if(this.isDocument){
31160 if(this.baseRotate == 6 || this.baseRotate == 8){
31162 height = this.thumbEl.getHeight();
31163 this.baseScale = height / this.imageEl.OriginWidth;
31165 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31166 width = this.thumbEl.getWidth();
31167 this.baseScale = width / this.imageEl.OriginHeight;
31173 height = this.thumbEl.getHeight();
31174 this.baseScale = height / this.imageEl.OriginHeight;
31176 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31177 width = this.thumbEl.getWidth();
31178 this.baseScale = width / this.imageEl.OriginWidth;
31184 if(this.baseRotate == 6 || this.baseRotate == 8){
31186 width = this.thumbEl.getHeight();
31187 this.baseScale = width / this.imageEl.OriginHeight;
31189 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31190 height = this.thumbEl.getWidth();
31191 this.baseScale = height / this.imageEl.OriginHeight;
31194 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31195 height = this.thumbEl.getWidth();
31196 this.baseScale = height / this.imageEl.OriginHeight;
31198 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31199 width = this.thumbEl.getHeight();
31200 this.baseScale = width / this.imageEl.OriginWidth;
31207 width = this.thumbEl.getWidth();
31208 this.baseScale = width / this.imageEl.OriginWidth;
31210 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31211 height = this.thumbEl.getHeight();
31212 this.baseScale = height / this.imageEl.OriginHeight;
31215 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31217 height = this.thumbEl.getHeight();
31218 this.baseScale = height / this.imageEl.OriginHeight;
31220 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31221 width = this.thumbEl.getWidth();
31222 this.baseScale = width / this.imageEl.OriginWidth;
31227 if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
31228 this.baseScale = width / this.minWidth;
31234 getScaleLevel : function()
31236 return this.baseScale * Math.pow(1.02, this.scale);
31239 onTouchStart : function(e)
31241 if(!this.canvasLoaded){
31242 this.beforeSelectFile(e);
31246 var touches = e.browserEvent.touches;
31252 if(touches.length == 1){
31253 this.onMouseDown(e);
31257 if(touches.length != 2){
31263 for(var i = 0, finger; finger = touches[i]; i++){
31264 coords.push(finger.pageX, finger.pageY);
31267 var x = Math.pow(coords[0] - coords[2], 2);
31268 var y = Math.pow(coords[1] - coords[3], 2);
31270 this.startDistance = Math.sqrt(x + y);
31272 this.startScale = this.scale;
31274 this.pinching = true;
31275 this.dragable = false;
31279 onTouchMove : function(e)
31281 if(!this.pinching && !this.dragable){
31285 var touches = e.browserEvent.touches;
31292 this.onMouseMove(e);
31298 for(var i = 0, finger; finger = touches[i]; i++){
31299 coords.push(finger.pageX, finger.pageY);
31302 var x = Math.pow(coords[0] - coords[2], 2);
31303 var y = Math.pow(coords[1] - coords[3], 2);
31305 this.endDistance = Math.sqrt(x + y);
31307 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31309 if(!this.zoomable()){
31310 this.scale = this.startScale;
31318 onTouchEnd : function(e)
31320 this.pinching = false;
31321 this.dragable = false;
31325 process : function(file, crop)
31328 this.maskEl.mask(this.loadingText);
31331 this.xhr = new XMLHttpRequest();
31333 file.xhr = this.xhr;
31335 this.xhr.open(this.method, this.url, true);
31338 "Accept": "application/json",
31339 "Cache-Control": "no-cache",
31340 "X-Requested-With": "XMLHttpRequest"
31343 for (var headerName in headers) {
31344 var headerValue = headers[headerName];
31346 this.xhr.setRequestHeader(headerName, headerValue);
31352 this.xhr.onload = function()
31354 _this.xhrOnLoad(_this.xhr);
31357 this.xhr.onerror = function()
31359 _this.xhrOnError(_this.xhr);
31362 var formData = new FormData();
31364 formData.append('returnHTML', 'NO');
31367 formData.append('crop', crop);
31368 var blobBin = atob(crop.split(',')[1]);
31370 for(var i = 0; i < blobBin.length; i++) {
31371 array.push(blobBin.charCodeAt(i));
31373 var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
31374 formData.append(this.paramName, croppedFile, file.name);
31377 if(typeof(file.filename) != 'undefined'){
31378 formData.append('filename', file.filename);
31381 if(typeof(file.mimetype) != 'undefined'){
31382 formData.append('mimetype', file.mimetype);
31385 if(this.fireEvent('arrange', this, formData) != false){
31386 this.xhr.send(formData);
31390 xhrOnLoad : function(xhr)
31393 this.maskEl.unmask();
31396 if (xhr.readyState !== 4) {
31397 this.fireEvent('exception', this, xhr);
31401 var response = Roo.decode(xhr.responseText);
31403 if(!response.success){
31404 this.fireEvent('exception', this, xhr);
31408 var response = Roo.decode(xhr.responseText);
31410 this.fireEvent('upload', this, response);
31414 xhrOnError : function()
31417 this.maskEl.unmask();
31420 Roo.log('xhr on error');
31422 var response = Roo.decode(xhr.responseText);
31428 prepare : function(file)
31431 this.maskEl.mask(this.loadingText);
31437 if(typeof(file) === 'string'){
31438 this.loadCanvas(file);
31442 if(!file || !this.urlAPI){
31447 if(typeof(file.type) != 'undefined' && file.type.length != 0) {
31448 this.cropType = file.type;
31453 if(this.fireEvent('prepare', this, this.file) != false){
31455 var reader = new FileReader();
31457 reader.onload = function (e) {
31458 if (e.target.error) {
31459 Roo.log(e.target.error);
31463 var buffer = e.target.result,
31464 dataView = new DataView(buffer),
31466 maxOffset = dataView.byteLength - 4,
31470 if (dataView.getUint16(0) === 0xffd8) {
31471 while (offset < maxOffset) {
31472 markerBytes = dataView.getUint16(offset);
31474 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31475 markerLength = dataView.getUint16(offset + 2) + 2;
31476 if (offset + markerLength > dataView.byteLength) {
31477 Roo.log('Invalid meta data: Invalid segment size.');
31481 if(markerBytes == 0xffe1){
31482 _this.parseExifData(
31489 offset += markerLength;
31499 var url = _this.urlAPI.createObjectURL(_this.file);
31501 _this.loadCanvas(url);
31506 reader.readAsArrayBuffer(this.file);
31512 parseExifData : function(dataView, offset, length)
31514 var tiffOffset = offset + 10,
31518 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31519 // No Exif data, might be XMP data instead
31523 // Check for the ASCII code for "Exif" (0x45786966):
31524 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31525 // No Exif data, might be XMP data instead
31528 if (tiffOffset + 8 > dataView.byteLength) {
31529 Roo.log('Invalid Exif data: Invalid segment size.');
31532 // Check for the two null bytes:
31533 if (dataView.getUint16(offset + 8) !== 0x0000) {
31534 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31537 // Check the byte alignment:
31538 switch (dataView.getUint16(tiffOffset)) {
31540 littleEndian = true;
31543 littleEndian = false;
31546 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31549 // Check for the TIFF tag marker (0x002A):
31550 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31551 Roo.log('Invalid Exif data: Missing TIFF marker.');
31554 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31555 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31557 this.parseExifTags(
31560 tiffOffset + dirOffset,
31565 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31570 if (dirOffset + 6 > dataView.byteLength) {
31571 Roo.log('Invalid Exif data: Invalid directory offset.');
31574 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31575 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31576 if (dirEndOffset + 4 > dataView.byteLength) {
31577 Roo.log('Invalid Exif data: Invalid directory size.');
31580 for (i = 0; i < tagsNumber; i += 1) {
31584 dirOffset + 2 + 12 * i, // tag offset
31588 // Return the offset to the next directory:
31589 return dataView.getUint32(dirEndOffset, littleEndian);
31592 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31594 var tag = dataView.getUint16(offset, littleEndian);
31596 this.exif[tag] = this.getExifValue(
31600 dataView.getUint16(offset + 2, littleEndian), // tag type
31601 dataView.getUint32(offset + 4, littleEndian), // tag length
31606 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31608 var tagType = Roo.panel.Cropbox.exifTagTypes[type],
31617 Roo.log('Invalid Exif data: Invalid tag type.');
31621 tagSize = tagType.size * length;
31622 // Determine if the value is contained in the dataOffset bytes,
31623 // or if the value at the dataOffset is a pointer to the actual data:
31624 dataOffset = tagSize > 4 ?
31625 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31626 if (dataOffset + tagSize > dataView.byteLength) {
31627 Roo.log('Invalid Exif data: Invalid data offset.');
31630 if (length === 1) {
31631 return tagType.getValue(dataView, dataOffset, littleEndian);
31634 for (i = 0; i < length; i += 1) {
31635 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31638 if (tagType.ascii) {
31640 // Concatenate the chars:
31641 for (i = 0; i < values.length; i += 1) {
31643 // Ignore the terminating NULL byte(s):
31644 if (c === '\u0000') {
31656 Roo.apply(Roo.panel.Cropbox, {
31658 'Orientation': 0x0112
31662 1: 0, //'top-left',
31664 3: 180, //'bottom-right',
31665 // 4: 'bottom-left',
31667 6: 90, //'right-top',
31668 // 7: 'right-bottom',
31669 8: 270 //'left-bottom'
31673 // byte, 8-bit unsigned int:
31675 getValue: function (dataView, dataOffset) {
31676 return dataView.getUint8(dataOffset);
31680 // ascii, 8-bit byte:
31682 getValue: function (dataView, dataOffset) {
31683 return String.fromCharCode(dataView.getUint8(dataOffset));
31688 // short, 16 bit int:
31690 getValue: function (dataView, dataOffset, littleEndian) {
31691 return dataView.getUint16(dataOffset, littleEndian);
31695 // long, 32 bit int:
31697 getValue: function (dataView, dataOffset, littleEndian) {
31698 return dataView.getUint32(dataOffset, littleEndian);
31702 // rational = two long values, first is numerator, second is denominator:
31704 getValue: function (dataView, dataOffset, littleEndian) {
31705 return dataView.getUint32(dataOffset, littleEndian) /
31706 dataView.getUint32(dataOffset + 4, littleEndian);
31710 // slong, 32 bit signed int:
31712 getValue: function (dataView, dataOffset, littleEndian) {
31713 return dataView.getInt32(dataOffset, littleEndian);
31717 // srational, two slongs, first is numerator, second is denominator:
31719 getValue: function (dataView, dataOffset, littleEndian) {
31720 return dataView.getInt32(dataOffset, littleEndian) /
31721 dataView.getInt32(dataOffset + 4, littleEndian);
31731 cls : 'btn-group roo-upload-cropbox-rotate-left',
31732 action : 'rotate-left',
31736 cls : 'btn btn-default',
31737 html : '<i class="fa fa-undo"></i>'
31743 cls : 'btn-group roo-upload-cropbox-picture',
31744 action : 'picture',
31748 cls : 'btn btn-default',
31749 html : '<i class="fa fa-picture-o"></i>'
31755 cls : 'btn-group roo-upload-cropbox-rotate-right',
31756 action : 'rotate-right',
31760 cls : 'btn btn-default',
31761 html : '<i class="fa fa-repeat"></i>'
31769 cls : 'btn-group roo-upload-cropbox-rotate-left',
31770 action : 'rotate-left',
31774 cls : 'btn btn-default',
31775 html : '<i class="fa fa-undo"></i>'
31781 cls : 'btn-group roo-upload-cropbox-download',
31782 action : 'download',
31786 cls : 'btn btn-default',
31787 html : '<i class="fa fa-download"></i>'
31793 cls : 'btn-group roo-upload-cropbox-crop',
31798 cls : 'btn btn-default',
31799 html : '<i class="fa fa-crop"></i>'
31805 cls : 'btn-group roo-upload-cropbox-trash',
31810 cls : 'btn btn-default',
31811 html : '<i class="fa fa-trash"></i>'
31817 cls : 'btn-group roo-upload-cropbox-rotate-right',
31818 action : 'rotate-right',
31822 cls : 'btn btn-default',
31823 html : '<i class="fa fa-repeat"></i>'
31831 cls : 'btn-group roo-upload-cropbox-rotate-left',
31832 action : 'rotate-left',
31836 cls : 'btn btn-default',
31837 html : '<i class="fa fa-undo"></i>'
31843 cls : 'btn-group roo-upload-cropbox-rotate-right',
31844 action : 'rotate-right',
31848 cls : 'btn btn-default',
31849 html : '<i class="fa fa-repeat"></i>'
31857 cls : 'btn-group roo-upload-cropbox-center',
31862 cls : 'btn btn-default',
31872 * Ext JS Library 1.1.1
31873 * Copyright(c) 2006-2007, Ext JS, LLC.
31875 * Originally Released Under LGPL - original licence link has changed is not relivant.
31878 * <script type="text/javascript">
31881 * @class Roo.panel.Tab
31882 * @extends Roo.util.Observable
31883 * A lightweight tab container.
31887 // basic tabs 1, built from existing content
31888 var tabs = new Roo.panel.Tab("tabs1");
31889 tabs.addTab("script", "View Script");
31890 tabs.addTab("markup", "View Markup");
31891 tabs.activate("script");
31893 // more advanced tabs, built from javascript
31894 var jtabs = new Roo.panel.Tab("jtabs");
31895 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
31897 // set up the UpdateManager
31898 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
31899 var updater = tab2.getUpdateManager();
31900 updater.setDefaultUrl("ajax1.htm");
31901 tab2.on('activate', updater.refresh, updater, true);
31903 // Use setUrl for Ajax loading
31904 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
31905 tab3.setUrl("ajax2.htm", null, true);
31908 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
31911 jtabs.activate("jtabs-1");
31914 * Create a new TabPanel.
31915 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
31916 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
31918 Roo.panel.Tab = function(container, config){
31920 * The container element for this TabPanel.
31921 * @type Roo.Element
31923 this.el = Roo.get(container, true);
31925 if(typeof config == "boolean"){
31926 this.tabPosition = config ? "bottom" : "top";
31928 Roo.apply(this, config);
31931 if(this.tabPosition == "bottom"){
31932 this.bodyEl = Roo.get(this.createBody(this.el.dom));
31933 this.el.addClass("x-tabs-bottom");
31935 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
31936 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
31937 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
31939 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
31941 if(this.tabPosition != "bottom"){
31942 /** The body element that contains {@link Roo.panel.TabItem} bodies. +
31943 * @type Roo.Element
31945 this.bodyEl = Roo.get(this.createBody(this.el.dom));
31946 this.el.addClass("x-tabs-top");
31950 this.bodyEl.setStyle("position", "relative");
31952 this.active = null;
31953 this.activateDelegate = this.activate.createDelegate(this);
31958 * Fires when the active tab changes
31959 * @param {Roo.panel.Tab} this
31960 * @param {Roo.panel.TabItem} activePanel The new active tab
31964 * @event beforetabchange
31965 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
31966 * @param {Roo.panel.Tab} this
31967 * @param {Object} e Set cancel to true on this object to cancel the tab change
31968 * @param {Roo.panel.TabItem} tab The tab being changed to
31970 "beforetabchange" : true
31973 Roo.EventManager.onWindowResize(this.onResize, this);
31974 this.cpad = this.el.getPadding("lr");
31975 this.hiddenCount = 0;
31978 // toolbar on the tabbar support...
31979 if (this.toolbar) {
31980 var tcfg = this.toolbar;
31981 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
31982 this.toolbar = new Roo.Toolbar(tcfg);
31983 if (Roo.isSafari) {
31984 var tbl = tcfg.container.child('table', true);
31985 tbl.setAttribute('width', '100%');
31992 Roo.panel.Tab.superclass.constructor.call(this);
31995 Roo.extend(Roo.panel.Tab, Roo.util.Observable, {
31997 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
31999 tabPosition : "top",
32001 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
32003 currentTabWidth : 0,
32005 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
32009 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
32013 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
32015 preferredTabWidth : 175,
32017 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
32019 resizeTabs : false,
32021 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
32023 monitorResize : true,
32025 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
32030 * Creates a new {@link Roo.panel.TabItem} by looking for an existing element with the provided id -- if it's not found it creates one.
32031 * @param {String} id The id of the div to use <b>or create</b>
32032 * @param {String} text The text for the tab
32033 * @param {String} content (optional) Content to put in the TabPanelItem body
32034 * @param {Boolean} closable (optional) True to create a close icon on the tab
32035 * @return {Roo.panel.TabItem} The created TabPanelItem
32037 addTab : function(id, text, content, closable){
32038 var item = new Roo.panel.TabItem(this, id, text, closable);
32039 this.addTabItem(item);
32041 item.setContent(content);
32047 * Returns the {@link Roo.panel.TabItem} with the specified id/index
32048 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
32049 * @return {Roo.panel.TabItem}
32051 getTab : function(id){
32052 return this.items[id];
32056 * Hides the {@link Roo.panel.TabItem} with the specified id/index
32057 * @param {String/Number} id The id or index of the TabPanelItem to hide.
32059 hideTab : function(id){
32060 var t = this.items[id];
32063 this.hiddenCount++;
32064 this.autoSizeTabs();
32069 * "Unhides" the {@link Roo.panel.TabItem} with the specified id/index.
32070 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
32072 unhideTab : function(id){
32073 var t = this.items[id];
32075 t.setHidden(false);
32076 this.hiddenCount--;
32077 this.autoSizeTabs();
32082 * Adds an existing {@link Roo.panel.TabItem}.
32083 * @param {Roo.panel.TabItem} item The TabPanelItem to add
32085 addTabItem : function(item){
32086 this.items[item.id] = item;
32087 this.items.push(item);
32088 if(this.resizeTabs){
32089 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
32090 this.autoSizeTabs();
32097 * Removes a {@link Roo.panel.TabItem}.
32098 * @param {String/Number} id The id or index of the TabPanelItem to remove.
32100 removeTab : function(id){
32101 var items = this.items;
32102 var tab = items[id];
32103 if(!tab) { return; }
32104 var index = items.indexOf(tab);
32105 if(this.active == tab && items.length > 1){
32106 var newTab = this.getNextAvailable(index);
32111 this.stripEl.dom.removeChild(tab.pnode.dom);
32112 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
32113 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
32115 items.splice(index, 1);
32116 delete this.items[tab.id];
32117 tab.fireEvent("close", tab);
32118 tab.purgeListeners();
32119 this.autoSizeTabs();
32122 getNextAvailable : function(start){
32123 var items = this.items;
32125 // look for a next tab that will slide over to
32126 // replace the one being removed
32127 while(index < items.length){
32128 var item = items[++index];
32129 if(item && !item.isHidden()){
32133 // if one isn't found select the previous tab (on the left)
32136 var item = items[--index];
32137 if(item && !item.isHidden()){
32145 * Disables a {@link Roo.panel.TabItem}. It cannot be the active tab, if it is this call is ignored.
32146 * @param {String/Number} id The id or index of the TabPanelItem to disable.
32148 disableTab : function(id){
32149 var tab = this.items[id];
32150 if(tab && this.active != tab){
32156 * Enables a {@link Roo.panel.TabItem} that is disabled.
32157 * @param {String/Number} id The id or index of the TabPanelItem to enable.
32159 enableTab : function(id){
32160 var tab = this.items[id];
32165 * Activates a {@link Roo.panel.TabItem}. The currently active one will be deactivated.
32166 * @param {String/Number} id The id or index of the TabPanelItem to activate.
32167 * @return {Roo.panel.TabItem} The TabPanelItem.
32169 activate : function(id){
32170 var tab = this.items[id];
32174 if(tab == this.active || tab.disabled){
32178 this.fireEvent("beforetabchange", this, e, tab);
32179 if(e.cancel !== true && !tab.disabled){
32181 this.active.hide();
32183 this.active = this.items[id];
32184 this.active.show();
32185 this.fireEvent("tabchange", this, this.active);
32191 * Gets the active {@link Roo.panel.TabItem}.
32192 * @return {Roo.panel.TabItem} The active TabPanelItem or null if none are active.
32194 getActiveTab : function(){
32195 return this.active;
32199 * Updates the tab body element to fit the height of the container element
32200 * for overflow scrolling
32201 * @param {Number} targetHeight (optional) Override the starting height from the elements height
32203 syncHeight : function(targetHeight){
32204 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32205 var bm = this.bodyEl.getMargins();
32206 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
32207 this.bodyEl.setHeight(newHeight);
32211 onResize : function(){
32212 if(this.monitorResize){
32213 this.autoSizeTabs();
32218 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
32220 beginUpdate : function(){
32221 this.updating = true;
32225 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
32227 endUpdate : function(){
32228 this.updating = false;
32229 this.autoSizeTabs();
32233 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
32235 autoSizeTabs : function(){
32236 var count = this.items.length;
32237 var vcount = count - this.hiddenCount;
32238 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
32241 var w = Math.max(this.el.getWidth() - this.cpad, 10);
32242 var availWidth = Math.floor(w / vcount);
32243 var b = this.stripBody;
32244 if(b.getWidth() > w){
32245 var tabs = this.items;
32246 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
32247 if(availWidth < this.minTabWidth){
32248 /*if(!this.sleft){ // incomplete scrolling code
32249 this.createScrollButtons();
32252 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
32255 if(this.currentTabWidth < this.preferredTabWidth){
32256 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
32262 * Returns the number of tabs in this TabPanel.
32265 getCount : function(){
32266 return this.items.length;
32270 * Resizes all the tabs to the passed width
32271 * @param {Number} The new width
32273 setTabWidth : function(width){
32274 this.currentTabWidth = width;
32275 for(var i = 0, len = this.items.length; i < len; i++) {
32276 if(!this.items[i].isHidden()) {
32277 this.items[i].setWidth(width);
32283 * Destroys this TabPanel
32284 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
32286 destroy : function(removeEl){
32287 Roo.EventManager.removeResizeListener(this.onResize, this);
32288 for(var i = 0, len = this.items.length; i < len; i++){
32289 this.items[i].purgeListeners();
32291 if(removeEl === true){
32292 this.el.update("");
32300 Roo.panel.Tab.prototype.createStripList = function(strip){
32301 // div wrapper for retard IE
32302 // returns the "tr" element.
32303 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
32304 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
32305 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
32306 return strip.firstChild.firstChild.firstChild.firstChild;
32309 Roo.panel.Tab.prototype.createBody = function(container){
32310 var body = document.createElement("div");
32311 Roo.id(body, "tab-body");
32312 Roo.fly(body).addClass("x-tabs-body");
32313 container.appendChild(body);
32317 Roo.panel.Tab.prototype.createItemBody = function(bodyEl, id){
32318 var body = Roo.getDom(id);
32320 body = document.createElement("div");
32323 Roo.fly(body).addClass("x-tabs-item-body");
32324 bodyEl.insertBefore(body, bodyEl.firstChild);
32328 Roo.panel.Tab.prototype.createStripElements = function(stripEl, text, closable){
32329 var td = document.createElement("td");
32330 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
32331 //stripEl.appendChild(td);
32333 td.className = "x-tabs-closable";
32334 if(!this.closeTpl){
32335 this.closeTpl = new Roo.Template(
32336 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
32337 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
32338 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
32341 var el = this.closeTpl.overwrite(td, {"text": text});
32342 var close = el.getElementsByTagName("div")[0];
32343 var inner = el.getElementsByTagName("em")[0];
32344 return {"el": el, "close": close, "inner": inner};
32347 this.tabTpl = new Roo.Template(
32348 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
32349 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
32352 var el = this.tabTpl.overwrite(td, {"text": text});
32353 var inner = el.getElementsByTagName("em")[0];
32354 return {"el": el, "inner": inner};
32357 * @class Roo.panel.TabItem
32358 * @extends Roo.util.Observable
32359 * Represents an individual item (tab plus body) in a TabPanel.
32360 * @param {Roo.panel.Tab} tabPanel The {@link Roo.panel.Tab} this TabPanelItem belongs to
32361 * @param {String} id The id of this TabPanelItem
32362 * @param {String} text The text for the tab of this TabPanelItem
32363 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
32365 Roo.panel.TabItem = function(tabPanel, id, text, closable){
32367 * The {@link Roo.panel.Tab} this TabPanelItem belongs to
32368 * @type Roo.panel.Tab
32370 this.tabPanel = tabPanel;
32372 * The id for this TabPanelItem
32377 this.disabled = false;
32381 this.loaded = false;
32382 this.closable = closable;
32385 * The body element for this TabPanelItem.
32386 * @type Roo.Element
32388 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
32389 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
32390 this.bodyEl.setStyle("display", "block");
32391 this.bodyEl.setStyle("zoom", "1");
32394 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
32396 this.el = Roo.get(els.el, true);
32397 this.inner = Roo.get(els.inner, true);
32398 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
32399 this.pnode = Roo.get(els.el.parentNode, true);
32400 this.el.on("mousedown", this.onTabMouseDown, this);
32401 this.el.on("click", this.onTabClick, this);
32404 var c = Roo.get(els.close, true);
32405 c.dom.title = this.closeText;
32406 c.addClassOnOver("close-over");
32407 c.on("click", this.closeClick, this);
32413 * Fires when this tab becomes the active tab.
32414 * @param {Roo.panel.Tab} tabPanel The parent TabPanel
32415 * @param {Roo.panel.TabItem} this
32419 * @event beforeclose
32420 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
32421 * @param {Roo.panel.TabItem} this
32422 * @param {Object} e Set cancel to true on this object to cancel the close.
32424 "beforeclose": true,
32427 * Fires when this tab is closed.
32428 * @param {Roo.panel.TabItem} this
32432 * @event deactivate
32433 * Fires when this tab is no longer the active tab.
32434 * @param {Roo.panel.Tab} tabPanel The parent TabPanel
32435 * @param {Roo.panel.TabItem} this
32437 "deactivate" : true
32439 this.hidden = false;
32441 Roo.panel.TabItem.superclass.constructor.call(this);
32444 Roo.extend(Roo.panel.TabItem, Roo.util.Observable, {
32445 purgeListeners : function(){
32446 Roo.util.Observable.prototype.purgeListeners.call(this);
32447 this.el.removeAllListeners();
32450 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
32453 this.pnode.addClass("on");
32456 this.tabPanel.stripWrap.repaint();
32458 this.fireEvent("activate", this.tabPanel, this);
32462 * Returns true if this tab is the active tab.
32463 * @return {Boolean}
32465 isActive : function(){
32466 return this.tabPanel.getActiveTab() == this;
32470 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
32473 this.pnode.removeClass("on");
32475 this.fireEvent("deactivate", this.tabPanel, this);
32478 hideAction : function(){
32479 this.bodyEl.hide();
32480 this.bodyEl.setStyle("position", "absolute");
32481 this.bodyEl.setLeft("-20000px");
32482 this.bodyEl.setTop("-20000px");
32485 showAction : function(){
32486 this.bodyEl.setStyle("position", "relative");
32487 this.bodyEl.setTop("");
32488 this.bodyEl.setLeft("");
32489 this.bodyEl.show();
32493 * Set the tooltip for the tab.
32494 * @param {String} tooltip The tab's tooltip
32496 setTooltip : function(text){
32497 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
32498 this.textEl.dom.qtip = text;
32499 this.textEl.dom.removeAttribute('title');
32501 this.textEl.dom.title = text;
32505 onTabClick : function(e){
32506 e.preventDefault();
32507 this.tabPanel.activate(this.id);
32510 onTabMouseDown : function(e){
32511 e.preventDefault();
32512 this.tabPanel.activate(this.id);
32515 getWidth : function(){
32516 return this.inner.getWidth();
32519 setWidth : function(width){
32520 var iwidth = width - this.pnode.getPadding("lr");
32521 this.inner.setWidth(iwidth);
32522 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
32523 this.pnode.setWidth(width);
32527 * Show or hide the tab
32528 * @param {Boolean} hidden True to hide or false to show.
32530 setHidden : function(hidden){
32531 this.hidden = hidden;
32532 this.pnode.setStyle("display", hidden ? "none" : "");
32536 * Returns true if this tab is "hidden"
32537 * @return {Boolean}
32539 isHidden : function(){
32540 return this.hidden;
32544 * Returns the text for this tab
32547 getText : function(){
32551 autoSize : function(){
32552 //this.el.beginMeasure();
32553 this.textEl.setWidth(1);
32555 * #2804 [new] Tabs in Roojs
32556 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
32558 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
32559 //this.el.endMeasure();
32563 * Sets the text for the tab (Note: this also sets the tooltip text)
32564 * @param {String} text The tab's text and tooltip
32566 setText : function(text){
32568 this.textEl.update(text);
32569 this.setTooltip(text);
32570 if(!this.tabPanel.resizeTabs){
32575 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
32577 activate : function(){
32578 this.tabPanel.activate(this.id);
32582 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
32584 disable : function(){
32585 if(this.tabPanel.active != this){
32586 this.disabled = true;
32587 this.pnode.addClass("disabled");
32592 * Enables this TabPanelItem if it was previously disabled.
32594 enable : function(){
32595 this.disabled = false;
32596 this.pnode.removeClass("disabled");
32600 * Sets the content for this TabPanelItem.
32601 * @param {String} content The content
32602 * @param {Boolean} loadScripts true to look for and load scripts
32604 setContent : function(content, loadScripts){
32605 this.bodyEl.update(content, loadScripts);
32609 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
32610 * @return {Roo.UpdateManager} The UpdateManager
32612 getUpdateManager : function(){
32613 return this.bodyEl.getUpdateManager();
32617 * Set a URL to be used to load the content for this TabPanelItem.
32618 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
32619 * @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)
32620 * @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)
32621 * @return {Roo.UpdateManager} The UpdateManager
32623 setUrl : function(url, params, loadOnce){
32624 if(this.refreshDelegate){
32625 this.un('activate', this.refreshDelegate);
32627 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32628 this.on("activate", this.refreshDelegate);
32629 return this.bodyEl.getUpdateManager();
32633 _handleRefresh : function(url, params, loadOnce){
32634 if(!loadOnce || !this.loaded){
32635 var updater = this.bodyEl.getUpdateManager();
32636 updater.update(url, params, this._setLoaded.createDelegate(this));
32641 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
32642 * Will fail silently if the setUrl method has not been called.
32643 * This does not activate the panel, just updates its content.
32645 refresh : function(){
32646 if(this.refreshDelegate){
32647 this.loaded = false;
32648 this.refreshDelegate();
32653 _setLoaded : function(){
32654 this.loaded = true;
32658 closeClick : function(e){
32661 this.fireEvent("beforeclose", this, o);
32662 if(o.cancel !== true){
32663 this.tabPanel.removeTab(this.id);
32667 * The text displayed in the tooltip for the close icon.
32670 closeText : "Close this tab"
32674 Roo.panel.Tab.prototype.createStrip = function(container){
32675 var strip = document.createElement("div");
32676 strip.className = "x-tabs-wrap";
32677 container.appendChild(strip);
32681 * Ext JS Library 1.1.1
32682 * Copyright(c) 2006-2007, Ext JS, LLC.
32684 * Originally Released Under LGPL - original licence link has changed is not relivant.
32687 * <script type="text/javascript">
32691 * @class Roo.Button
32692 * @extends Roo.util.Observable
32693 * Simple Button class
32694 * @cfg {String} text The button text
32695 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
32696 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
32697 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
32698 * @cfg {Object} scope The scope of the handler
32699 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
32700 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
32701 * @cfg {Boolean} hidden True to start hidden (defaults to false)
32702 * @cfg {Boolean} disabled True to start disabled (defaults to false)
32703 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
32704 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
32705 applies if enableToggle = true)
32706 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
32707 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
32708 an {@link Roo.util.ClickRepeater} config object (defaults to false).
32710 * Create a new button
32711 * @param {Object} config The config object
32713 Roo.Button = function(renderTo, config)
32717 renderTo = config.renderTo || false;
32720 Roo.apply(this, config);
32724 * Fires when this button is clicked
32725 * @param {Button} this
32726 * @param {EventObject} e The click event
32731 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
32732 * @param {Button} this
32733 * @param {Boolean} pressed
32738 * Fires when the mouse hovers over the button
32739 * @param {Button} this
32740 * @param {Event} e The event object
32742 'mouseover' : true,
32745 * Fires when the mouse exits the button
32746 * @param {Button} this
32747 * @param {Event} e The event object
32752 * Fires when the button is rendered
32753 * @param {Button} this
32758 this.menu = Roo.menu.MenuMgr.get(this.menu);
32760 // register listeners first!! - so render can be captured..
32761 Roo.util.Observable.call(this);
32763 this.render(renderTo);
32769 Roo.extend(Roo.Button, Roo.util.Observable, {
32775 * Read-only. True if this button is hidden
32780 * Read-only. True if this button is disabled
32785 * Read-only. True if this button is pressed (only if enableToggle = true)
32791 * @cfg {Number} tabIndex
32792 * The DOM tabIndex for this button (defaults to undefined)
32794 tabIndex : undefined,
32797 * @cfg {Boolean} enableToggle
32798 * True to enable pressed/not pressed toggling (defaults to false)
32800 enableToggle: false,
32802 * @cfg {Roo.menu.Menu} menu
32803 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
32807 * @cfg {String} menuAlign
32808 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
32810 menuAlign : "tl-bl?",
32813 * @cfg {String} iconCls
32814 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
32816 iconCls : undefined,
32818 * @cfg {String} type
32819 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
32824 menuClassTarget: 'tr',
32827 * @cfg {String} clickEvent
32828 * The type of event to map to the button's event handler (defaults to 'click')
32830 clickEvent : 'click',
32833 * @cfg {Boolean} handleMouseEvents
32834 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
32836 handleMouseEvents : true,
32839 * @cfg {String} tooltipType
32840 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
32842 tooltipType : 'qtip',
32845 * @cfg {String} cls
32846 * A CSS class to apply to the button's main element.
32850 * @cfg {Roo.Template} template (Optional)
32851 * An {@link Roo.Template} with which to create the Button's main element. This Template must
32852 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
32853 * require code modifications if required elements (e.g. a button) aren't present.
32857 render : function(renderTo){
32859 if(this.hideParent){
32860 this.parentEl = Roo.get(renderTo);
32862 if(!this.dhconfig){
32863 if(!this.template){
32864 if(!Roo.Button.buttonTemplate){
32865 // hideous table template
32866 Roo.Button.buttonTemplate = new Roo.Template(
32867 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
32868 '<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>',
32869 "</tr></tbody></table>");
32871 this.template = Roo.Button.buttonTemplate;
32873 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
32874 var btnEl = btn.child("button:first");
32875 btnEl.on('focus', this.onFocus, this);
32876 btnEl.on('blur', this.onBlur, this);
32878 btn.addClass(this.cls);
32881 btnEl.setStyle('background-image', 'url(' +this.icon +')');
32884 btnEl.addClass(this.iconCls);
32886 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
32889 if(this.tabIndex !== undefined){
32890 btnEl.dom.tabIndex = this.tabIndex;
32893 if(typeof this.tooltip == 'object'){
32894 Roo.QuickTips.tips(Roo.apply({
32898 btnEl.dom[this.tooltipType] = this.tooltip;
32902 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
32906 this.el.dom.id = this.el.id = this.id;
32909 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
32910 this.menu.on("show", this.onMenuShow, this);
32911 this.menu.on("hide", this.onMenuHide, this);
32913 btn.addClass("x-btn");
32914 if(Roo.isIE && !Roo.isIE7){
32915 this.autoWidth.defer(1, this);
32919 if(this.handleMouseEvents){
32920 btn.on("mouseover", this.onMouseOver, this);
32921 btn.on("mouseout", this.onMouseOut, this);
32922 btn.on("mousedown", this.onMouseDown, this);
32924 btn.on(this.clickEvent, this.onClick, this);
32925 //btn.on("mouseup", this.onMouseUp, this);
32932 Roo.ButtonToggleMgr.register(this);
32934 this.el.addClass("x-btn-pressed");
32937 var repeater = new Roo.util.ClickRepeater(btn,
32938 typeof this.repeat == "object" ? this.repeat : {}
32940 repeater.on("click", this.onClick, this);
32943 this.fireEvent('render', this);
32947 * Returns the button's underlying element
32948 * @return {Roo.Element} The element
32950 getEl : function(){
32955 * Destroys this Button and removes any listeners.
32957 destroy : function(){
32958 Roo.ButtonToggleMgr.unregister(this);
32959 this.el.removeAllListeners();
32960 this.purgeListeners();
32965 autoWidth : function(){
32967 this.el.setWidth("auto");
32968 if(Roo.isIE7 && Roo.isStrict){
32969 var ib = this.el.child('button');
32970 if(ib && ib.getWidth() > 20){
32972 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
32977 this.el.beginMeasure();
32979 if(this.el.getWidth() < this.minWidth){
32980 this.el.setWidth(this.minWidth);
32983 this.el.endMeasure();
32990 * Assigns this button's click handler
32991 * @param {Function} handler The function to call when the button is clicked
32992 * @param {Object} scope (optional) Scope for the function passed in
32994 setHandler : function(handler, scope){
32995 this.handler = handler;
32996 this.scope = scope;
33000 * Sets this button's text
33001 * @param {String} text The button text
33003 setText : function(text){
33006 this.el.child("td.x-btn-center button.x-btn-text").update(text);
33012 * Gets the text for this button
33013 * @return {String} The button text
33015 getText : function(){
33023 this.hidden = false;
33025 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
33033 this.hidden = true;
33035 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
33040 * Convenience function for boolean show/hide
33041 * @param {Boolean} visible True to show, false to hide
33043 setVisible: function(visible){
33051 * Similar to toggle, but does not trigger event.
33052 * @param {Boolean} state [required] Force a particular state
33054 setPressed : function(state)
33056 if(state != this.pressed){
33058 this.el.addClass("x-btn-pressed");
33059 this.pressed = true;
33061 this.el.removeClass("x-btn-pressed");
33062 this.pressed = false;
33068 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
33069 * @param {Boolean} state (optional) Force a particular state
33071 toggle : function(state){
33072 state = state === undefined ? !this.pressed : state;
33073 if(state != this.pressed){
33075 this.el.addClass("x-btn-pressed");
33076 this.pressed = true;
33077 this.fireEvent("toggle", this, true);
33079 this.el.removeClass("x-btn-pressed");
33080 this.pressed = false;
33081 this.fireEvent("toggle", this, false);
33083 if(this.toggleHandler){
33084 this.toggleHandler.call(this.scope || this, this, state);
33094 focus : function(){
33095 this.el.child('button:first').focus();
33099 * Disable this button
33101 disable : function(){
33103 this.el.addClass("x-btn-disabled");
33105 this.disabled = true;
33109 * Enable this button
33111 enable : function(){
33113 this.el.removeClass("x-btn-disabled");
33115 this.disabled = false;
33119 * Convenience function for boolean enable/disable
33120 * @param {Boolean} enabled True to enable, false to disable
33122 setDisabled : function(v){
33123 this[v !== true ? "enable" : "disable"]();
33127 onClick : function(e)
33130 e.preventDefault();
33135 if(!this.disabled){
33136 if(this.enableToggle){
33139 if(this.menu && !this.menu.isVisible()){
33140 this.menu.show(this.el, this.menuAlign);
33142 this.fireEvent("click", this, e);
33144 this.el.removeClass("x-btn-over");
33145 this.handler.call(this.scope || this, this, e);
33150 onMouseOver : function(e){
33151 if(!this.disabled){
33152 this.el.addClass("x-btn-over");
33153 this.fireEvent('mouseover', this, e);
33157 onMouseOut : function(e){
33158 if(!e.within(this.el, true)){
33159 this.el.removeClass("x-btn-over");
33160 this.fireEvent('mouseout', this, e);
33164 onFocus : function(e){
33165 if(!this.disabled){
33166 this.el.addClass("x-btn-focus");
33170 onBlur : function(e){
33171 this.el.removeClass("x-btn-focus");
33174 onMouseDown : function(e){
33175 if(!this.disabled && e.button == 0){
33176 this.el.addClass("x-btn-click");
33177 Roo.get(document).on('mouseup', this.onMouseUp, this);
33181 onMouseUp : function(e){
33183 this.el.removeClass("x-btn-click");
33184 Roo.get(document).un('mouseup', this.onMouseUp, this);
33188 onMenuShow : function(e){
33189 this.el.addClass("x-btn-menu-active");
33192 onMenuHide : function(e){
33193 this.el.removeClass("x-btn-menu-active");
33197 // Private utility class used by Button
33198 Roo.ButtonToggleMgr = function(){
33201 function toggleGroup(btn, state){
33203 var g = groups[btn.toggleGroup];
33204 for(var i = 0, l = g.length; i < l; i++){
33206 g[i].toggle(false);
33213 register : function(btn){
33214 if(!btn.toggleGroup){
33217 var g = groups[btn.toggleGroup];
33219 g = groups[btn.toggleGroup] = [];
33222 btn.on("toggle", toggleGroup);
33225 unregister : function(btn){
33226 if(!btn.toggleGroup){
33229 var g = groups[btn.toggleGroup];
33232 btn.un("toggle", toggleGroup);
33238 * Ext JS Library 1.1.1
33239 * Copyright(c) 2006-2007, Ext JS, LLC.
33241 * Originally Released Under LGPL - original licence link has changed is not relivant.
33244 * <script type="text/javascript">
33248 * @class Roo.SplitButton
33249 * @extends Roo.Button
33250 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
33251 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
33252 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
33253 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
33254 * @cfg {String} arrowTooltip The title attribute of the arrow
33256 * Create a new menu button
33257 * @param {String/HTMLElement/Element} renderTo The element to append the button to
33258 * @param {Object} config The config object
33260 Roo.SplitButton = function(renderTo, config){
33261 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
33263 * @event arrowclick
33264 * Fires when this button's arrow is clicked
33265 * @param {SplitButton} this
33266 * @param {EventObject} e The click event
33268 this.addEvents({"arrowclick":true});
33271 Roo.extend(Roo.SplitButton, Roo.Button, {
33272 render : function(renderTo){
33273 // this is one sweet looking template!
33274 var tpl = new Roo.Template(
33275 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
33276 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
33277 '<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>',
33278 "</tbody></table></td><td>",
33279 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
33280 '<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>',
33281 "</tbody></table></td></tr></table>"
33283 var btn = tpl.append(renderTo, [this.text, this.type], true);
33284 var btnEl = btn.child("button");
33286 btn.addClass(this.cls);
33289 btnEl.setStyle('background-image', 'url(' +this.icon +')');
33292 btnEl.addClass(this.iconCls);
33294 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
33298 if(this.handleMouseEvents){
33299 btn.on("mouseover", this.onMouseOver, this);
33300 btn.on("mouseout", this.onMouseOut, this);
33301 btn.on("mousedown", this.onMouseDown, this);
33302 btn.on("mouseup", this.onMouseUp, this);
33304 btn.on(this.clickEvent, this.onClick, this);
33306 if(typeof this.tooltip == 'object'){
33307 Roo.QuickTips.tips(Roo.apply({
33311 btnEl.dom[this.tooltipType] = this.tooltip;
33314 if(this.arrowTooltip){
33315 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
33324 this.el.addClass("x-btn-pressed");
33326 if(Roo.isIE && !Roo.isIE7){
33327 this.autoWidth.defer(1, this);
33332 this.menu.on("show", this.onMenuShow, this);
33333 this.menu.on("hide", this.onMenuHide, this);
33335 this.fireEvent('render', this);
33339 autoWidth : function(){
33341 var tbl = this.el.child("table:first");
33342 var tbl2 = this.el.child("table:last");
33343 this.el.setWidth("auto");
33344 tbl.setWidth("auto");
33345 if(Roo.isIE7 && Roo.isStrict){
33346 var ib = this.el.child('button:first');
33347 if(ib && ib.getWidth() > 20){
33349 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
33354 this.el.beginMeasure();
33356 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
33357 tbl.setWidth(this.minWidth-tbl2.getWidth());
33360 this.el.endMeasure();
33363 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
33367 * Sets this button's click handler
33368 * @param {Function} handler The function to call when the button is clicked
33369 * @param {Object} scope (optional) Scope for the function passed above
33371 setHandler : function(handler, scope){
33372 this.handler = handler;
33373 this.scope = scope;
33377 * Sets this button's arrow click handler
33378 * @param {Function} handler The function to call when the arrow is clicked
33379 * @param {Object} scope (optional) Scope for the function passed above
33381 setArrowHandler : function(handler, scope){
33382 this.arrowHandler = handler;
33383 this.scope = scope;
33389 focus : function(){
33391 this.el.child("button:first").focus();
33396 onClick : function(e){
33397 e.preventDefault();
33398 if(!this.disabled){
33399 if(e.getTarget(".x-btn-menu-arrow-wrap")){
33400 if(this.menu && !this.menu.isVisible()){
33401 this.menu.show(this.el, this.menuAlign);
33403 this.fireEvent("arrowclick", this, e);
33404 if(this.arrowHandler){
33405 this.arrowHandler.call(this.scope || this, this, e);
33408 this.fireEvent("click", this, e);
33410 this.handler.call(this.scope || this, this, e);
33416 onMouseDown : function(e){
33417 if(!this.disabled){
33418 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
33422 onMouseUp : function(e){
33423 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
33428 // backwards compat
33429 Roo.MenuButton = Roo.SplitButton;/*
33431 * Ext JS Library 1.1.1
33432 * Copyright(c) 2006-2007, Ext JS, LLC.
33434 * Originally Released Under LGPL - original licence link has changed is not relivant.
33437 * <script type="text/javascript">
33441 * @class Roo.Toolbar
33442 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
33443 * Basic Toolbar class.
33445 * Creates a new Toolbar
33446 * @param {Object} container The config object
33448 Roo.Toolbar = function(container, buttons, config)
33450 /// old consturctor format still supported..
33451 if(container instanceof Array){ // omit the container for later rendering
33452 buttons = container;
33456 if (typeof(container) == 'object' && container.xtype) {
33457 config = container;
33458 container = config.container;
33459 buttons = config.buttons || []; // not really - use items!!
33462 if (config && config.items) {
33463 xitems = config.items;
33464 delete config.items;
33466 Roo.apply(this, config);
33467 this.buttons = buttons;
33470 this.render(container);
33472 this.xitems = xitems;
33473 Roo.each(xitems, function(b) {
33479 Roo.Toolbar.prototype = {
33481 * @cfg {Array} items
33482 * array of button configs or elements to add (will be converted to a MixedCollection)
33486 * @cfg {String/HTMLElement/Element} container
33487 * The id or element that will contain the toolbar
33490 render : function(ct){
33491 this.el = Roo.get(ct);
33493 this.el.addClass(this.cls);
33495 // using a table allows for vertical alignment
33496 // 100% width is needed by Safari...
33497 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
33498 this.tr = this.el.child("tr", true);
33500 this.items = new Roo.util.MixedCollection(false, function(o){
33501 return o.id || ("item" + (++autoId));
33504 this.add.apply(this, this.buttons);
33505 delete this.buttons;
33510 * Adds element(s) to the toolbar -- this function takes a variable number of
33511 * arguments of mixed type and adds them to the toolbar.
33512 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
33514 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
33515 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
33516 * <li>Field: Any form field (equivalent to {@link #addField})</li>
33517 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
33518 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
33519 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
33520 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
33521 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
33522 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
33524 * @param {Mixed} arg2
33525 * @param {Mixed} etc.
33528 var a = arguments, l = a.length;
33529 for(var i = 0; i < l; i++){
33534 _add : function(el) {
33537 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
33540 if (el.applyTo){ // some kind of form field
33541 return this.addField(el);
33543 if (el.render){ // some kind of Toolbar.Item
33544 return this.addItem(el);
33546 if (typeof el == "string"){ // string
33547 if(el == "separator" || el == "-"){
33548 return this.addSeparator();
33551 return this.addSpacer();
33554 return this.addFill();
33556 return this.addText(el);
33559 if(el.tagName){ // element
33560 return this.addElement(el);
33562 if(typeof el == "object"){ // must be button config?
33563 return this.addButton(el);
33565 // and now what?!?!
33571 * Add an Xtype element
33572 * @param {Object} xtype Xtype Object
33573 * @return {Object} created Object
33575 addxtype : function(e){
33576 return this.add(e);
33580 * Returns the Element for this toolbar.
33581 * @return {Roo.Element}
33583 getEl : function(){
33589 * @return {Roo.Toolbar.Item} The separator item
33591 addSeparator : function(){
33592 return this.addItem(new Roo.Toolbar.Separator());
33596 * Adds a spacer element
33597 * @return {Roo.Toolbar.Spacer} The spacer item
33599 addSpacer : function(){
33600 return this.addItem(new Roo.Toolbar.Spacer());
33604 * Adds a fill element that forces subsequent additions to the right side of the toolbar
33605 * @return {Roo.Toolbar.Fill} The fill item
33607 addFill : function(){
33608 return this.addItem(new Roo.Toolbar.Fill());
33612 * Adds any standard HTML element to the toolbar
33613 * @param {String/HTMLElement/Element} el The element or id of the element to add
33614 * @return {Roo.Toolbar.Item} The element's item
33616 addElement : function(el){
33617 return this.addItem(new Roo.Toolbar.Item(el));
33620 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
33621 * @type Roo.util.MixedCollection
33626 * Adds any Toolbar.Item or subclass
33627 * @param {Roo.Toolbar.Item} item
33628 * @return {Roo.Toolbar.Item} The item
33630 addItem : function(item){
33631 var td = this.nextBlock();
33633 this.items.add(item);
33638 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
33639 * @param {Object/Array} config A button config or array of configs
33640 * @return {Roo.Toolbar.Button/Array}
33642 addButton : function(config){
33643 if(config instanceof Array){
33645 for(var i = 0, len = config.length; i < len; i++) {
33646 buttons.push(this.addButton(config[i]));
33651 if(!(config instanceof Roo.Toolbar.Button)){
33653 new Roo.Toolbar.SplitButton(config) :
33654 new Roo.Toolbar.Button(config);
33656 var td = this.nextBlock();
33663 * Adds text to the toolbar
33664 * @param {String} text The text to add
33665 * @return {Roo.Toolbar.Item} The element's item
33667 addText : function(text){
33668 return this.addItem(new Roo.Toolbar.TextItem(text));
33672 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
33673 * @param {Number} index The index where the item is to be inserted
33674 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
33675 * @return {Roo.Toolbar.Button/Item}
33677 insertButton : function(index, item){
33678 if(item instanceof Array){
33680 for(var i = 0, len = item.length; i < len; i++) {
33681 buttons.push(this.insertButton(index + i, item[i]));
33685 if (!(item instanceof Roo.Toolbar.Button)){
33686 item = new Roo.Toolbar.Button(item);
33688 var td = document.createElement("td");
33689 this.tr.insertBefore(td, this.tr.childNodes[index]);
33691 this.items.insert(index, item);
33696 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
33697 * @param {Object} config
33698 * @return {Roo.Toolbar.Item} The element's item
33700 addDom : function(config, returnEl){
33701 var td = this.nextBlock();
33702 Roo.DomHelper.overwrite(td, config);
33703 var ti = new Roo.Toolbar.Item(td.firstChild);
33705 this.items.add(ti);
33710 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
33711 * @type Roo.util.MixedCollection
33716 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
33717 * Note: the field should not have been rendered yet. For a field that has already been
33718 * rendered, use {@link #addElement}.
33719 * @param {Roo.form.Field} field
33720 * @return {Roo.ToolbarItem}
33724 addField : function(field) {
33725 if (!this.fields) {
33727 this.fields = new Roo.util.MixedCollection(false, function(o){
33728 return o.id || ("item" + (++autoId));
33733 var td = this.nextBlock();
33735 var ti = new Roo.Toolbar.Item(td.firstChild);
33737 this.items.add(ti);
33738 this.fields.add(field);
33749 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
33750 this.el.child('div').hide();
33758 this.el.child('div').show();
33762 nextBlock : function(){
33763 var td = document.createElement("td");
33764 this.tr.appendChild(td);
33769 destroy : function(){
33770 if(this.items){ // rendered?
33771 Roo.destroy.apply(Roo, this.items.items);
33773 if(this.fields){ // rendered?
33774 Roo.destroy.apply(Roo, this.fields.items);
33776 Roo.Element.uncache(this.el, this.tr);
33781 * @class Roo.Toolbar.Item
33782 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
33784 * Creates a new Item
33785 * @param {HTMLElement} el
33787 Roo.Toolbar.Item = function(el){
33789 if (typeof (el.xtype) != 'undefined') {
33794 this.el = Roo.getDom(el);
33795 this.id = Roo.id(this.el);
33796 this.hidden = false;
33801 * Fires when the button is rendered
33802 * @param {Button} this
33806 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
33808 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
33809 //Roo.Toolbar.Item.prototype = {
33812 * Get this item's HTML Element
33813 * @return {HTMLElement}
33815 getEl : function(){
33820 render : function(td){
33823 td.appendChild(this.el);
33825 this.fireEvent('render', this);
33829 * Removes and destroys this item.
33831 destroy : function(){
33832 this.td.parentNode.removeChild(this.td);
33839 this.hidden = false;
33840 this.td.style.display = "";
33847 this.hidden = true;
33848 this.td.style.display = "none";
33852 * Convenience function for boolean show/hide.
33853 * @param {Boolean} visible true to show/false to hide
33855 setVisible: function(visible){
33864 * Try to focus this item.
33866 focus : function(){
33867 Roo.fly(this.el).focus();
33871 * Disables this item.
33873 disable : function(){
33874 Roo.fly(this.td).addClass("x-item-disabled");
33875 this.disabled = true;
33876 this.el.disabled = true;
33880 * Enables this item.
33882 enable : function(){
33883 Roo.fly(this.td).removeClass("x-item-disabled");
33884 this.disabled = false;
33885 this.el.disabled = false;
33891 * @class Roo.Toolbar.Separator
33892 * @extends Roo.Toolbar.Item
33893 * A simple toolbar separator class
33895 * Creates a new Separator
33897 Roo.Toolbar.Separator = function(cfg){
33899 var s = document.createElement("span");
33900 s.className = "ytb-sep";
33905 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
33907 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
33908 enable:Roo.emptyFn,
33909 disable:Roo.emptyFn,
33914 * @class Roo.Toolbar.Spacer
33915 * @extends Roo.Toolbar.Item
33916 * A simple element that adds extra horizontal space to a toolbar.
33918 * Creates a new Spacer
33920 Roo.Toolbar.Spacer = function(cfg){
33921 var s = document.createElement("div");
33922 s.className = "ytb-spacer";
33926 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
33928 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
33929 enable:Roo.emptyFn,
33930 disable:Roo.emptyFn,
33935 * @class Roo.Toolbar.Fill
33936 * @extends Roo.Toolbar.Spacer
33937 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
33939 * Creates a new Spacer
33941 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
33943 render : function(td){
33944 td.style.width = '100%';
33945 Roo.Toolbar.Fill.superclass.render.call(this, td);
33950 * @class Roo.Toolbar.TextItem
33951 * @extends Roo.Toolbar.Item
33952 * A simple class that renders text directly into a toolbar.
33954 * Creates a new TextItem
33955 * @cfg {string} text
33957 Roo.Toolbar.TextItem = function(cfg){
33958 var text = cfg || "";
33959 if (typeof(cfg) == 'object') {
33960 text = cfg.text || "";
33964 var s = document.createElement("span");
33965 s.className = "ytb-text";
33966 s.innerHTML = text;
33971 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
33973 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
33976 enable:Roo.emptyFn,
33977 disable:Roo.emptyFn,
33980 * Shows this button
33983 this.hidden = false;
33984 this.el.style.display = "";
33988 * Hides this button
33991 this.hidden = true;
33992 this.el.style.display = "none";
33998 * @class Roo.Toolbar.Button
33999 * @extends Roo.Button
34000 * A button that renders into a toolbar.
34002 * Creates a new Button
34003 * @param {Object} config A standard {@link Roo.Button} config object
34005 Roo.Toolbar.Button = function(config){
34006 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
34008 Roo.extend(Roo.Toolbar.Button, Roo.Button,
34012 render : function(td){
34014 Roo.Toolbar.Button.superclass.render.call(this, td);
34018 * Removes and destroys this button
34020 destroy : function(){
34021 Roo.Toolbar.Button.superclass.destroy.call(this);
34022 this.td.parentNode.removeChild(this.td);
34026 * Shows this button
34029 this.hidden = false;
34030 this.td.style.display = "";
34034 * Hides this button
34037 this.hidden = true;
34038 this.td.style.display = "none";
34042 * Disables this item
34044 disable : function(){
34045 Roo.fly(this.td).addClass("x-item-disabled");
34046 this.disabled = true;
34050 * Enables this item
34052 enable : function(){
34053 Roo.fly(this.td).removeClass("x-item-disabled");
34054 this.disabled = false;
34057 // backwards compat
34058 Roo.ToolbarButton = Roo.Toolbar.Button;
34061 * @class Roo.Toolbar.SplitButton
34062 * @extends Roo.SplitButton
34063 * A menu button that renders into a toolbar.
34065 * Creates a new SplitButton
34066 * @param {Object} config A standard {@link Roo.SplitButton} config object
34068 Roo.Toolbar.SplitButton = function(config){
34069 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
34071 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
34072 render : function(td){
34074 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
34078 * Removes and destroys this button
34080 destroy : function(){
34081 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
34082 this.td.parentNode.removeChild(this.td);
34086 * Shows this button
34089 this.hidden = false;
34090 this.td.style.display = "";
34094 * Hides this button
34097 this.hidden = true;
34098 this.td.style.display = "none";
34102 // backwards compat
34103 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
34105 * Ext JS Library 1.1.1
34106 * Copyright(c) 2006-2007, Ext JS, LLC.
34108 * Originally Released Under LGPL - original licence link has changed is not relivant.
34111 * <script type="text/javascript">
34115 * @class Roo.PagingToolbar
34116 * @extends Roo.Toolbar
34117 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
34118 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
34120 * Create a new PagingToolbar
34121 * @param {Object} config The config object
34123 Roo.PagingToolbar = function(el, ds, config)
34125 // old args format still supported... - xtype is prefered..
34126 if (typeof(el) == 'object' && el.xtype) {
34127 // created from xtype...
34129 ds = el.dataSource;
34130 el = config.container;
34133 if (config.items) {
34134 items = config.items;
34138 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
34141 this.renderButtons(this.el);
34144 // supprot items array.
34146 Roo.each(items, function(e) {
34147 this.add(Roo.factory(e));
34152 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
34155 * @cfg {String/HTMLElement/Element} container
34156 * container The id or element that will contain the toolbar
34159 * @cfg {Boolean} displayInfo
34160 * True to display the displayMsg (defaults to false)
34165 * @cfg {Number} pageSize
34166 * The number of records to display per page (defaults to 20)
34170 * @cfg {String} displayMsg
34171 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
34173 displayMsg : 'Displaying {0} - {1} of {2}',
34175 * @cfg {String} emptyMsg
34176 * The message to display when no records are found (defaults to "No data to display")
34178 emptyMsg : 'No data to display',
34180 * Customizable piece of the default paging text (defaults to "Page")
34183 beforePageText : "Page",
34185 * Customizable piece of the default paging text (defaults to "of %0")
34188 afterPageText : "of {0}",
34190 * Customizable piece of the default paging text (defaults to "First Page")
34193 firstText : "First Page",
34195 * Customizable piece of the default paging text (defaults to "Previous Page")
34198 prevText : "Previous Page",
34200 * Customizable piece of the default paging text (defaults to "Next Page")
34203 nextText : "Next Page",
34205 * Customizable piece of the default paging text (defaults to "Last Page")
34208 lastText : "Last Page",
34210 * Customizable piece of the default paging text (defaults to "Refresh")
34213 refreshText : "Refresh",
34216 renderButtons : function(el){
34217 Roo.PagingToolbar.superclass.render.call(this, el);
34218 this.first = this.addButton({
34219 tooltip: this.firstText,
34220 cls: "x-btn-icon x-grid-page-first",
34222 handler: this.onClick.createDelegate(this, ["first"])
34224 this.prev = this.addButton({
34225 tooltip: this.prevText,
34226 cls: "x-btn-icon x-grid-page-prev",
34228 handler: this.onClick.createDelegate(this, ["prev"])
34230 //this.addSeparator();
34231 this.add(this.beforePageText);
34232 this.field = Roo.get(this.addDom({
34237 cls: "x-grid-page-number"
34239 this.field.on("keydown", this.onPagingKeydown, this);
34240 this.field.on("focus", function(){this.dom.select();});
34241 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
34242 this.field.setHeight(18);
34243 //this.addSeparator();
34244 this.next = this.addButton({
34245 tooltip: this.nextText,
34246 cls: "x-btn-icon x-grid-page-next",
34248 handler: this.onClick.createDelegate(this, ["next"])
34250 this.last = this.addButton({
34251 tooltip: this.lastText,
34252 cls: "x-btn-icon x-grid-page-last",
34254 handler: this.onClick.createDelegate(this, ["last"])
34256 //this.addSeparator();
34257 this.loading = this.addButton({
34258 tooltip: this.refreshText,
34259 cls: "x-btn-icon x-grid-loading",
34260 handler: this.onClick.createDelegate(this, ["refresh"])
34263 if(this.displayInfo){
34264 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
34269 updateInfo : function(){
34270 if(this.displayEl){
34271 var count = this.ds.getCount();
34272 var msg = count == 0 ?
34276 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
34278 this.displayEl.update(msg);
34283 onLoad : function(ds, r, o){
34284 this.cursor = o.params ? o.params.start : 0;
34285 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
34287 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
34288 this.field.dom.value = ap;
34289 this.first.setDisabled(ap == 1);
34290 this.prev.setDisabled(ap == 1);
34291 this.next.setDisabled(ap == ps);
34292 this.last.setDisabled(ap == ps);
34293 this.loading.enable();
34298 getPageData : function(){
34299 var total = this.ds.getTotalCount();
34302 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
34303 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
34308 onLoadError : function(){
34309 this.loading.enable();
34313 onPagingKeydown : function(e){
34314 var k = e.getKey();
34315 var d = this.getPageData();
34317 var v = this.field.dom.value, pageNum;
34318 if(!v || isNaN(pageNum = parseInt(v, 10))){
34319 this.field.dom.value = d.activePage;
34322 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
34323 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34326 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))
34328 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
34329 this.field.dom.value = pageNum;
34330 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
34333 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
34335 var v = this.field.dom.value, pageNum;
34336 var increment = (e.shiftKey) ? 10 : 1;
34337 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
34340 if(!v || isNaN(pageNum = parseInt(v, 10))) {
34341 this.field.dom.value = d.activePage;
34344 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
34346 this.field.dom.value = parseInt(v, 10) + increment;
34347 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
34348 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34355 beforeLoad : function(){
34357 this.loading.disable();
34361 * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
34362 * @param {String} which (first|prev|next|last|refresh) which button to press.
34366 onClick : function(which){
34370 ds.load({params:{start: 0, limit: this.pageSize}});
34373 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
34376 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
34379 var total = ds.getTotalCount();
34380 var extra = total % this.pageSize;
34381 var lastStart = extra ? (total - extra) : total-this.pageSize;
34382 ds.load({params:{start: lastStart, limit: this.pageSize}});
34385 ds.load({params:{start: this.cursor, limit: this.pageSize}});
34391 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
34392 * @param {Roo.data.Store} store The data store to unbind
34394 unbind : function(ds){
34395 ds.un("beforeload", this.beforeLoad, this);
34396 ds.un("load", this.onLoad, this);
34397 ds.un("loadexception", this.onLoadError, this);
34398 ds.un("remove", this.updateInfo, this);
34399 ds.un("add", this.updateInfo, this);
34400 this.ds = undefined;
34404 * Binds the paging toolbar to the specified {@link Roo.data.Store}
34405 * @param {Roo.data.Store} store The data store to bind
34407 bind : function(ds){
34408 ds.on("beforeload", this.beforeLoad, this);
34409 ds.on("load", this.onLoad, this);
34410 ds.on("loadexception", this.onLoadError, this);
34411 ds.on("remove", this.updateInfo, this);
34412 ds.on("add", this.updateInfo, this);
34417 * Ext JS Library 1.1.1
34418 * Copyright(c) 2006-2007, Ext JS, LLC.
34420 * Originally Released Under LGPL - original licence link has changed is not relivant.
34423 * <script type="text/javascript">
34427 * @class Roo.Resizable
34428 * @extends Roo.util.Observable
34429 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
34430 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
34431 * 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
34432 * the element will be wrapped for you automatically.</p>
34433 * <p>Here is the list of valid resize handles:</p>
34436 ------ -------------------
34445 'hd' horizontal drag
34448 * <p>Here's an example showing the creation of a typical Resizable:</p>
34450 var resizer = new Roo.Resizable("element-id", {
34458 resizer.on("resize", myHandler);
34460 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
34461 * resizer.east.setDisplayed(false);</p>
34462 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
34463 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
34464 * resize operation's new size (defaults to [0, 0])
34465 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
34466 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
34467 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
34468 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
34469 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
34470 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
34471 * @cfg {Number} width The width of the element in pixels (defaults to null)
34472 * @cfg {Number} height The height of the element in pixels (defaults to null)
34473 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
34474 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
34475 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
34476 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
34477 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
34478 * in favor of the handles config option (defaults to false)
34479 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
34480 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
34481 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
34482 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
34483 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
34484 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
34485 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
34486 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
34487 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
34488 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
34489 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
34491 * Create a new resizable component
34492 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
34493 * @param {Object} config configuration options
34495 Roo.Resizable = function(el, config)
34497 this.el = Roo.get(el);
34499 if(config && config.wrap){
34500 config.resizeChild = this.el;
34501 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
34502 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
34503 this.el.setStyle("overflow", "hidden");
34504 this.el.setPositioning(config.resizeChild.getPositioning());
34505 config.resizeChild.clearPositioning();
34506 if(!config.width || !config.height){
34507 var csize = config.resizeChild.getSize();
34508 this.el.setSize(csize.width, csize.height);
34510 if(config.pinned && !config.adjustments){
34511 config.adjustments = "auto";
34515 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
34516 this.proxy.unselectable();
34517 this.proxy.enableDisplayMode('block');
34519 Roo.apply(this, config);
34522 this.disableTrackOver = true;
34523 this.el.addClass("x-resizable-pinned");
34525 // if the element isn't positioned, make it relative
34526 var position = this.el.getStyle("position");
34527 if(position != "absolute" && position != "fixed"){
34528 this.el.setStyle("position", "relative");
34530 if(!this.handles){ // no handles passed, must be legacy style
34531 this.handles = 's,e,se';
34532 if(this.multiDirectional){
34533 this.handles += ',n,w';
34536 if(this.handles == "all"){
34537 this.handles = "n s e w ne nw se sw";
34539 var hs = this.handles.split(/\s*?[,;]\s*?| /);
34540 var ps = Roo.Resizable.positions;
34541 for(var i = 0, len = hs.length; i < len; i++){
34542 if(hs[i] && ps[hs[i]]){
34543 var pos = ps[hs[i]];
34544 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
34548 this.corner = this.southeast;
34550 // updateBox = the box can move..
34551 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
34552 this.updateBox = true;
34555 this.activeHandle = null;
34557 if(this.resizeChild){
34558 if(typeof this.resizeChild == "boolean"){
34559 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
34561 this.resizeChild = Roo.get(this.resizeChild, true);
34565 if(this.adjustments == "auto"){
34566 var rc = this.resizeChild;
34567 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
34568 if(rc && (hw || hn)){
34569 rc.position("relative");
34570 rc.setLeft(hw ? hw.el.getWidth() : 0);
34571 rc.setTop(hn ? hn.el.getHeight() : 0);
34573 this.adjustments = [
34574 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
34575 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
34579 if(this.draggable){
34580 this.dd = this.dynamic ?
34581 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
34582 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
34588 * @event beforeresize
34589 * Fired before resize is allowed. Set enabled to false to cancel resize.
34590 * @param {Roo.Resizable} this
34591 * @param {Roo.EventObject} e The mousedown event
34593 "beforeresize" : true,
34596 * Fired a resizing.
34597 * @param {Roo.Resizable} this
34598 * @param {Number} x The new x position
34599 * @param {Number} y The new y position
34600 * @param {Number} w The new w width
34601 * @param {Number} h The new h hight
34602 * @param {Roo.EventObject} e The mouseup event
34607 * Fired after a resize.
34608 * @param {Roo.Resizable} this
34609 * @param {Number} width The new width
34610 * @param {Number} height The new height
34611 * @param {Roo.EventObject} e The mouseup event
34616 if(this.width !== null && this.height !== null){
34617 this.resizeTo(this.width, this.height);
34619 this.updateChildSize();
34622 this.el.dom.style.zoom = 1;
34624 Roo.Resizable.superclass.constructor.call(this);
34627 Roo.extend(Roo.Resizable, Roo.util.Observable, {
34628 resizeChild : false,
34629 adjustments : [0, 0],
34639 multiDirectional : false,
34640 disableTrackOver : false,
34641 easing : 'easeOutStrong',
34642 widthIncrement : 0,
34643 heightIncrement : 0,
34647 preserveRatio : false,
34648 transparent: false,
34654 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
34656 constrainTo: undefined,
34658 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
34660 resizeRegion: undefined,
34664 * Perform a manual resize
34665 * @param {Number} width
34666 * @param {Number} height
34668 resizeTo : function(width, height){
34669 this.el.setSize(width, height);
34670 this.updateChildSize();
34671 this.fireEvent("resize", this, width, height, null);
34675 startSizing : function(e, handle){
34676 this.fireEvent("beforeresize", this, e);
34677 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
34680 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
34681 this.overlay.unselectable();
34682 this.overlay.enableDisplayMode("block");
34683 this.overlay.on("mousemove", this.onMouseMove, this);
34684 this.overlay.on("mouseup", this.onMouseUp, this);
34686 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
34688 this.resizing = true;
34689 this.startBox = this.el.getBox();
34690 this.startPoint = e.getXY();
34691 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
34692 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
34694 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34695 this.overlay.show();
34697 if(this.constrainTo) {
34698 var ct = Roo.get(this.constrainTo);
34699 this.resizeRegion = ct.getRegion().adjust(
34700 ct.getFrameWidth('t'),
34701 ct.getFrameWidth('l'),
34702 -ct.getFrameWidth('b'),
34703 -ct.getFrameWidth('r')
34707 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
34709 this.proxy.setBox(this.startBox);
34711 this.proxy.setStyle('visibility', 'visible');
34717 onMouseDown : function(handle, e){
34720 this.activeHandle = handle;
34721 this.startSizing(e, handle);
34726 onMouseUp : function(e){
34727 var size = this.resizeElement();
34728 this.resizing = false;
34730 this.overlay.hide();
34732 this.fireEvent("resize", this, size.width, size.height, e);
34736 updateChildSize : function(){
34738 if(this.resizeChild){
34740 var child = this.resizeChild;
34741 var adj = this.adjustments;
34742 if(el.dom.offsetWidth){
34743 var b = el.getSize(true);
34744 child.setSize(b.width+adj[0], b.height+adj[1]);
34746 // Second call here for IE
34747 // The first call enables instant resizing and
34748 // the second call corrects scroll bars if they
34751 setTimeout(function(){
34752 if(el.dom.offsetWidth){
34753 var b = el.getSize(true);
34754 child.setSize(b.width+adj[0], b.height+adj[1]);
34762 snap : function(value, inc, min){
34763 if(!inc || !value) {
34766 var newValue = value;
34767 var m = value % inc;
34770 newValue = value + (inc-m);
34772 newValue = value - m;
34775 return Math.max(min, newValue);
34779 resizeElement : function(){
34780 var box = this.proxy.getBox();
34781 if(this.updateBox){
34782 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
34784 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
34786 this.updateChildSize();
34794 constrain : function(v, diff, m, mx){
34797 }else if(v - diff > mx){
34804 onMouseMove : function(e){
34807 try{// try catch so if something goes wrong the user doesn't get hung
34809 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
34813 //var curXY = this.startPoint;
34814 var curSize = this.curSize || this.startBox;
34815 var x = this.startBox.x, y = this.startBox.y;
34816 var ox = x, oy = y;
34817 var w = curSize.width, h = curSize.height;
34818 var ow = w, oh = h;
34819 var mw = this.minWidth, mh = this.minHeight;
34820 var mxw = this.maxWidth, mxh = this.maxHeight;
34821 var wi = this.widthIncrement;
34822 var hi = this.heightIncrement;
34824 var eventXY = e.getXY();
34825 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
34826 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
34828 var pos = this.activeHandle.position;
34833 w = Math.min(Math.max(mw, w), mxw);
34838 h = Math.min(Math.max(mh, h), mxh);
34843 w = Math.min(Math.max(mw, w), mxw);
34844 h = Math.min(Math.max(mh, h), mxh);
34847 diffY = this.constrain(h, diffY, mh, mxh);
34854 var adiffX = Math.abs(diffX);
34855 var sub = (adiffX % wi); // how much
34856 if (sub > (wi/2)) { // far enough to snap
34857 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
34859 // remove difference..
34860 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
34864 x = Math.max(this.minX, x);
34867 diffX = this.constrain(w, diffX, mw, mxw);
34873 w = Math.min(Math.max(mw, w), mxw);
34874 diffY = this.constrain(h, diffY, mh, mxh);
34879 diffX = this.constrain(w, diffX, mw, mxw);
34880 diffY = this.constrain(h, diffY, mh, mxh);
34887 diffX = this.constrain(w, diffX, mw, mxw);
34889 h = Math.min(Math.max(mh, h), mxh);
34895 var sw = this.snap(w, wi, mw);
34896 var sh = this.snap(h, hi, mh);
34897 if(sw != w || sh != h){
34920 if(this.preserveRatio){
34925 h = Math.min(Math.max(mh, h), mxh);
34930 w = Math.min(Math.max(mw, w), mxw);
34935 w = Math.min(Math.max(mw, w), mxw);
34941 w = Math.min(Math.max(mw, w), mxw);
34947 h = Math.min(Math.max(mh, h), mxh);
34955 h = Math.min(Math.max(mh, h), mxh);
34965 h = Math.min(Math.max(mh, h), mxh);
34973 if (pos == 'hdrag') {
34976 this.proxy.setBounds(x, y, w, h);
34978 this.resizeElement();
34982 this.fireEvent("resizing", this, x, y, w, h, e);
34986 handleOver : function(){
34988 this.el.addClass("x-resizable-over");
34993 handleOut : function(){
34994 if(!this.resizing){
34995 this.el.removeClass("x-resizable-over");
35000 * Returns the element this component is bound to.
35001 * @return {Roo.Element}
35003 getEl : function(){
35008 * Returns the resizeChild element (or null).
35009 * @return {Roo.Element}
35011 getResizeChild : function(){
35012 return this.resizeChild;
35014 groupHandler : function()
35019 * Destroys this resizable. If the element was wrapped and
35020 * removeEl is not true then the element remains.
35021 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
35023 destroy : function(removeEl){
35024 this.proxy.remove();
35026 this.overlay.removeAllListeners();
35027 this.overlay.remove();
35029 var ps = Roo.Resizable.positions;
35031 if(typeof ps[k] != "function" && this[ps[k]]){
35032 var h = this[ps[k]];
35033 h.el.removeAllListeners();
35038 this.el.update("");
35045 // hash to map config positions to true positions
35046 Roo.Resizable.positions = {
35047 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
35052 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
35054 // only initialize the template if resizable is used
35055 var tpl = Roo.DomHelper.createTemplate(
35056 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
35059 Roo.Resizable.Handle.prototype.tpl = tpl;
35061 this.position = pos;
35063 // show north drag fro topdra
35064 var handlepos = pos == 'hdrag' ? 'north' : pos;
35066 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
35067 if (pos == 'hdrag') {
35068 this.el.setStyle('cursor', 'pointer');
35070 this.el.unselectable();
35072 this.el.setOpacity(0);
35074 this.el.on("mousedown", this.onMouseDown, this);
35075 if(!disableTrackOver){
35076 this.el.on("mouseover", this.onMouseOver, this);
35077 this.el.on("mouseout", this.onMouseOut, this);
35082 Roo.Resizable.Handle.prototype = {
35083 afterResize : function(rz){
35088 onMouseDown : function(e){
35089 this.rz.onMouseDown(this, e);
35092 onMouseOver : function(e){
35093 this.rz.handleOver(this, e);
35096 onMouseOut : function(e){
35097 this.rz.handleOut(this, e);
35101 * Ext JS Library 1.1.1
35102 * Copyright(c) 2006-2007, Ext JS, LLC.
35104 * Originally Released Under LGPL - original licence link has changed is not relivant.
35107 * <script type="text/javascript">
35111 * @class Roo.Editor
35112 * @extends Roo.Component
35113 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
35115 * Create a new Editor
35116 * @param {Roo.form.Field} field The Field object (or descendant)
35117 * @param {Object} config The config object
35119 Roo.Editor = function(field, config){
35120 Roo.Editor.superclass.constructor.call(this, config);
35121 this.field = field;
35124 * @event beforestartedit
35125 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
35126 * false from the handler of this event.
35127 * @param {Editor} this
35128 * @param {Roo.Element} boundEl The underlying element bound to this editor
35129 * @param {Mixed} value The field value being set
35131 "beforestartedit" : true,
35134 * Fires when this editor is displayed
35135 * @param {Roo.Element} boundEl The underlying element bound to this editor
35136 * @param {Mixed} value The starting field value
35138 "startedit" : true,
35140 * @event beforecomplete
35141 * Fires after a change has been made to the field, but before the change is reflected in the underlying
35142 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
35143 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
35144 * event will not fire since no edit actually occurred.
35145 * @param {Editor} this
35146 * @param {Mixed} value The current field value
35147 * @param {Mixed} startValue The original field value
35149 "beforecomplete" : true,
35152 * Fires after editing is complete and any changed value has been written to the underlying field.
35153 * @param {Editor} this
35154 * @param {Mixed} value The current field value
35155 * @param {Mixed} startValue The original field value
35159 * @event specialkey
35160 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
35161 * {@link Roo.EventObject#getKey} to determine which key was pressed.
35162 * @param {Roo.form.Field} this
35163 * @param {Roo.EventObject} e The event object
35165 "specialkey" : true
35169 Roo.extend(Roo.Editor, Roo.Component, {
35171 * @cfg {Boolean/String} autosize
35172 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
35173 * or "height" to adopt the height only (defaults to false)
35176 * @cfg {Boolean} revertInvalid
35177 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
35178 * validation fails (defaults to true)
35181 * @cfg {Boolean} ignoreNoChange
35182 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
35183 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
35184 * will never be ignored.
35187 * @cfg {Boolean} hideEl
35188 * False to keep the bound element visible while the editor is displayed (defaults to true)
35191 * @cfg {Mixed} value
35192 * The data value of the underlying field (defaults to "")
35196 * @cfg {String} alignment
35197 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
35201 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
35202 * for bottom-right shadow (defaults to "frame")
35206 * @cfg {Boolean} constrain True to constrain the editor to the viewport
35210 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
35212 completeOnEnter : false,
35214 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
35216 cancelOnEsc : false,
35218 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
35223 onRender : function(ct, position){
35224 this.el = new Roo.Layer({
35225 shadow: this.shadow,
35231 constrain: this.constrain
35233 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
35234 if(this.field.msgTarget != 'title'){
35235 this.field.msgTarget = 'qtip';
35237 this.field.render(this.el);
35239 this.field.el.dom.setAttribute('autocomplete', 'off');
35241 this.field.on("specialkey", this.onSpecialKey, this);
35242 if(this.swallowKeys){
35243 this.field.el.swallowEvent(['keydown','keypress']);
35246 this.field.on("blur", this.onBlur, this);
35247 if(this.field.grow){
35248 this.field.on("autosize", this.el.sync, this.el, {delay:1});
35252 onSpecialKey : function(field, e)
35254 //Roo.log('editor onSpecialKey');
35255 if(this.completeOnEnter && e.getKey() == e.ENTER){
35257 this.completeEdit();
35260 // do not fire special key otherwise it might hide close the editor...
35261 if(e.getKey() == e.ENTER){
35264 if(this.cancelOnEsc && e.getKey() == e.ESC){
35268 this.fireEvent('specialkey', field, e);
35273 * Starts the editing process and shows the editor.
35274 * @param {String/HTMLElement/Element} el The element to edit
35275 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
35276 * to the innerHTML of el.
35278 startEdit : function(el, value){
35280 this.completeEdit();
35282 this.boundEl = Roo.get(el);
35283 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
35284 if(!this.rendered){
35285 this.render(this.parentEl || document.body);
35287 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
35290 this.startValue = v;
35291 this.field.setValue(v);
35293 var sz = this.boundEl.getSize();
35294 switch(this.autoSize){
35296 this.setSize(sz.width, "");
35299 this.setSize("", sz.height);
35302 this.setSize(sz.width, sz.height);
35305 this.el.alignTo(this.boundEl, this.alignment);
35306 this.editing = true;
35308 Roo.QuickTips.disable();
35314 * Sets the height and width of this editor.
35315 * @param {Number} width The new width
35316 * @param {Number} height The new height
35318 setSize : function(w, h){
35319 this.field.setSize(w, h);
35326 * Realigns the editor to the bound field based on the current alignment config value.
35328 realign : function(){
35329 this.el.alignTo(this.boundEl, this.alignment);
35333 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
35334 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
35336 completeEdit : function(remainVisible){
35340 var v = this.getValue();
35341 if(this.revertInvalid !== false && !this.field.isValid()){
35342 v = this.startValue;
35343 this.cancelEdit(true);
35345 if(String(v) === String(this.startValue) && this.ignoreNoChange){
35346 this.editing = false;
35350 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
35351 this.editing = false;
35352 if(this.updateEl && this.boundEl){
35353 this.boundEl.update(v);
35355 if(remainVisible !== true){
35358 this.fireEvent("complete", this, v, this.startValue);
35363 onShow : function(){
35365 if(this.hideEl !== false){
35366 this.boundEl.hide();
35369 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
35370 this.fixIEFocus = true;
35371 this.deferredFocus.defer(50, this);
35373 this.field.focus();
35375 this.fireEvent("startedit", this.boundEl, this.startValue);
35378 deferredFocus : function(){
35380 this.field.focus();
35385 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
35386 * reverted to the original starting value.
35387 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
35388 * cancel (defaults to false)
35390 cancelEdit : function(remainVisible){
35392 this.setValue(this.startValue);
35393 if(remainVisible !== true){
35400 onBlur : function(){
35401 if(this.allowBlur !== true && this.editing){
35402 this.completeEdit();
35407 onHide : function(){
35409 this.completeEdit();
35413 if(this.field.collapse){
35414 this.field.collapse();
35417 if(this.hideEl !== false){
35418 this.boundEl.show();
35421 Roo.QuickTips.enable();
35426 * Sets the data value of the editor
35427 * @param {Mixed} value Any valid value supported by the underlying field
35429 setValue : function(v){
35430 this.field.setValue(v);
35434 * Gets the data value of the editor
35435 * @return {Mixed} The data value
35437 getValue : function(){
35438 return this.field.getValue();
35442 * Ext JS Library 1.1.1
35443 * Copyright(c) 2006-2007, Ext JS, LLC.
35445 * Originally Released Under LGPL - original licence link has changed is not relivant.
35448 * <script type="text/javascript">
35452 * @class Roo.BasicDialog
35453 * @extends Roo.util.Observable
35454 * @parent none builder
35455 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
35457 var dlg = new Roo.BasicDialog("my-dlg", {
35466 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
35467 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
35468 dlg.addButton('Cancel', dlg.hide, dlg);
35471 <b>A Dialog should always be a direct child of the body element.</b>
35472 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
35473 * @cfg {String} title Default text to display in the title bar (defaults to null)
35474 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
35475 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
35476 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
35477 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
35478 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
35479 * (defaults to null with no animation)
35480 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
35481 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
35482 * property for valid values (defaults to 'all')
35483 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
35484 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
35485 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
35486 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
35487 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
35488 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
35489 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
35490 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
35491 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
35492 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
35493 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
35494 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
35495 * draggable = true (defaults to false)
35496 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
35497 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
35498 * shadow (defaults to false)
35499 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
35500 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
35501 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
35502 * @cfg {Array} buttons Array of buttons
35503 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
35505 * Create a new BasicDialog.
35506 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
35507 * @param {Object} config Configuration options
35509 Roo.BasicDialog = function(el, config){
35510 this.el = Roo.get(el);
35511 var dh = Roo.DomHelper;
35512 if(!this.el && config && config.autoCreate){
35513 if(typeof config.autoCreate == "object"){
35514 if(!config.autoCreate.id){
35515 config.autoCreate.id = el;
35517 this.el = dh.append(document.body,
35518 config.autoCreate, true);
35520 this.el = dh.append(document.body,
35521 {tag: "div", id: el, style:'visibility:hidden;'}, true);
35525 el.setDisplayed(true);
35526 el.hide = this.hideAction;
35528 el.addClass("x-dlg");
35530 Roo.apply(this, config);
35532 this.proxy = el.createProxy("x-dlg-proxy");
35533 this.proxy.hide = this.hideAction;
35534 this.proxy.setOpacity(.5);
35538 el.setWidth(config.width);
35541 el.setHeight(config.height);
35543 this.size = el.getSize();
35544 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
35545 this.xy = [config.x,config.y];
35547 this.xy = el.getCenterXY(true);
35549 /** The header element @type Roo.Element */
35550 this.header = el.child("> .x-dlg-hd");
35551 /** The body element @type Roo.Element */
35552 this.body = el.child("> .x-dlg-bd");
35553 /** The footer element @type Roo.Element */
35554 this.footer = el.child("> .x-dlg-ft");
35557 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
35560 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
35563 this.header.unselectable();
35565 this.header.update(this.title);
35567 // this element allows the dialog to be focused for keyboard event
35568 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
35569 this.focusEl.swallowEvent("click", true);
35571 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
35573 // wrap the body and footer for special rendering
35574 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
35576 this.bwrap.dom.appendChild(this.footer.dom);
35579 this.bg = this.el.createChild({
35580 tag: "div", cls:"x-dlg-bg",
35581 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
35583 this.centerBg = this.bg.child("div.x-dlg-bg-center");
35586 if(this.autoScroll !== false && !this.autoTabs){
35587 this.body.setStyle("overflow", "auto");
35590 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
35592 if(this.closable !== false){
35593 this.el.addClass("x-dlg-closable");
35594 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
35595 this.close.on("click", this.closeClick, this);
35596 this.close.addClassOnOver("x-dlg-close-over");
35598 if(this.collapsible !== false){
35599 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
35600 this.collapseBtn.on("click", this.collapseClick, this);
35601 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
35602 this.header.on("dblclick", this.collapseClick, this);
35604 if(this.resizable !== false){
35605 this.el.addClass("x-dlg-resizable");
35606 this.resizer = new Roo.Resizable(el, {
35607 minWidth: this.minWidth || 80,
35608 minHeight:this.minHeight || 80,
35609 handles: this.resizeHandles || "all",
35612 this.resizer.on("beforeresize", this.beforeResize, this);
35613 this.resizer.on("resize", this.onResize, this);
35615 if(this.draggable !== false){
35616 el.addClass("x-dlg-draggable");
35617 if (!this.proxyDrag) {
35618 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
35621 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
35623 dd.setHandleElId(this.header.id);
35624 dd.endDrag = this.endMove.createDelegate(this);
35625 dd.startDrag = this.startMove.createDelegate(this);
35626 dd.onDrag = this.onDrag.createDelegate(this);
35631 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
35632 this.mask.enableDisplayMode("block");
35634 this.el.addClass("x-dlg-modal");
35637 this.shadow = new Roo.Shadow({
35638 mode : typeof this.shadow == "string" ? this.shadow : "sides",
35639 offset : this.shadowOffset
35642 this.shadowOffset = 0;
35644 if(Roo.useShims && this.shim !== false){
35645 this.shim = this.el.createShim();
35646 this.shim.hide = this.hideAction;
35654 if (this.buttons) {
35655 var bts= this.buttons;
35657 Roo.each(bts, function(b) {
35666 * Fires when a key is pressed
35667 * @param {Roo.BasicDialog} this
35668 * @param {Roo.EventObject} e
35673 * Fires when this dialog is moved by the user.
35674 * @param {Roo.BasicDialog} this
35675 * @param {Number} x The new page X
35676 * @param {Number} y The new page Y
35681 * Fires when this dialog is resized by the user.
35682 * @param {Roo.BasicDialog} this
35683 * @param {Number} width The new width
35684 * @param {Number} height The new height
35688 * @event beforehide
35689 * Fires before this dialog is hidden.
35690 * @param {Roo.BasicDialog} this
35692 "beforehide" : true,
35695 * Fires when this dialog is hidden.
35696 * @param {Roo.BasicDialog} this
35700 * @event beforeshow
35701 * Fires before this dialog is shown.
35702 * @param {Roo.BasicDialog} this
35704 "beforeshow" : true,
35707 * Fires when this dialog is shown.
35708 * @param {Roo.BasicDialog} this
35712 el.on("keydown", this.onKeyDown, this);
35713 el.on("mousedown", this.toFront, this);
35714 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
35716 Roo.DialogManager.register(this);
35717 Roo.BasicDialog.superclass.constructor.call(this);
35720 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
35721 shadowOffset: Roo.isIE ? 6 : 5,
35724 minButtonWidth: 75,
35725 defaultButton: null,
35726 buttonAlign: "right",
35731 * Sets the dialog title text
35732 * @param {String} text The title text to display
35733 * @return {Roo.BasicDialog} this
35735 setTitle : function(text){
35736 this.header.update(text);
35741 closeClick : function(){
35746 collapseClick : function(){
35747 this[this.collapsed ? "expand" : "collapse"]();
35751 * Collapses the dialog to its minimized state (only the title bar is visible).
35752 * Equivalent to the user clicking the collapse dialog button.
35754 collapse : function(){
35755 if(!this.collapsed){
35756 this.collapsed = true;
35757 this.el.addClass("x-dlg-collapsed");
35758 this.restoreHeight = this.el.getHeight();
35759 this.resizeTo(this.el.getWidth(), this.header.getHeight());
35764 * Expands a collapsed dialog back to its normal state. Equivalent to the user
35765 * clicking the expand dialog button.
35767 expand : function(){
35768 if(this.collapsed){
35769 this.collapsed = false;
35770 this.el.removeClass("x-dlg-collapsed");
35771 this.resizeTo(this.el.getWidth(), this.restoreHeight);
35776 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
35777 * @return {Roo.panel.Tab} The tabs component
35779 initTabs : function(){
35780 var tabs = this.getTabs();
35781 while(tabs.getTab(0)){
35784 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
35786 tabs.addTab(Roo.id(dom), dom.title);
35794 beforeResize : function(){
35795 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
35799 onResize : function(){
35800 this.refreshSize();
35801 this.syncBodyHeight();
35802 this.adjustAssets();
35804 this.fireEvent("resize", this, this.size.width, this.size.height);
35808 onKeyDown : function(e){
35809 if(this.isVisible()){
35810 this.fireEvent("keydown", this, e);
35815 * Resizes the dialog.
35816 * @param {Number} width
35817 * @param {Number} height
35818 * @return {Roo.BasicDialog} this
35820 resizeTo : function(width, height){
35821 this.el.setSize(width, height);
35822 this.size = {width: width, height: height};
35823 this.syncBodyHeight();
35824 if(this.fixedcenter){
35827 if(this.isVisible()){
35828 this.constrainXY();
35829 this.adjustAssets();
35831 this.fireEvent("resize", this, width, height);
35837 * Resizes the dialog to fit the specified content size.
35838 * @param {Number} width
35839 * @param {Number} height
35840 * @return {Roo.BasicDialog} this
35842 setContentSize : function(w, h){
35843 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
35844 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
35845 //if(!this.el.isBorderBox()){
35846 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
35847 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
35850 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
35851 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
35853 this.resizeTo(w, h);
35858 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
35859 * executed in response to a particular key being pressed while the dialog is active.
35860 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
35861 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
35862 * @param {Function} fn The function to call
35863 * @param {Object} scope (optional) The scope of the function
35864 * @return {Roo.BasicDialog} this
35866 addKeyListener : function(key, fn, scope){
35867 var keyCode, shift, ctrl, alt;
35868 if(typeof key == "object" && !(key instanceof Array)){
35869 keyCode = key["key"];
35870 shift = key["shift"];
35871 ctrl = key["ctrl"];
35876 var handler = function(dlg, e){
35877 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
35878 var k = e.getKey();
35879 if(keyCode instanceof Array){
35880 for(var i = 0, len = keyCode.length; i < len; i++){
35881 if(keyCode[i] == k){
35882 fn.call(scope || window, dlg, k, e);
35888 fn.call(scope || window, dlg, k, e);
35893 this.on("keydown", handler);
35898 * Returns the panel.Tab component (creates it if it doesn't exist).
35899 * Note: If you wish to simply check for the existence of tabs without creating them,
35900 * check for a null 'tabs' property.
35901 * @return {Roo.panel.Tab} The tabs component
35903 getTabs : function(){
35905 this.el.addClass("x-dlg-auto-tabs");
35906 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
35907 this.tabs = new Roo.panel.Tab(this.body.dom, this.tabPosition == "bottom");
35913 * Adds a button to the footer section of the dialog.
35914 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
35915 * object or a valid Roo.DomHelper element config
35916 * @param {Function} handler The function called when the button is clicked
35917 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
35918 * @return {Roo.Button} The new button
35920 addButton : function(config, handler, scope){
35921 var dh = Roo.DomHelper;
35923 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
35925 if(!this.btnContainer){
35926 var tb = this.footer.createChild({
35928 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
35929 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
35931 this.btnContainer = tb.firstChild.firstChild.firstChild;
35936 minWidth: this.minButtonWidth,
35939 if(typeof config == "string"){
35940 bconfig.text = config;
35943 bconfig.dhconfig = config;
35945 Roo.apply(bconfig, config);
35949 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
35950 bconfig.position = Math.max(0, bconfig.position);
35951 fc = this.btnContainer.childNodes[bconfig.position];
35954 var btn = new Roo.Button(
35956 this.btnContainer.insertBefore(document.createElement("td"),fc)
35957 : this.btnContainer.appendChild(document.createElement("td")),
35958 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
35961 this.syncBodyHeight();
35964 * Array of all the buttons that have been added to this dialog via addButton
35969 this.buttons.push(btn);
35974 * Sets the default button to be focused when the dialog is displayed.
35975 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
35976 * @return {Roo.BasicDialog} this
35978 setDefaultButton : function(btn){
35979 this.defaultButton = btn;
35984 getHeaderFooterHeight : function(safe){
35987 height += this.header.getHeight();
35990 var fm = this.footer.getMargins();
35991 height += (this.footer.getHeight()+fm.top+fm.bottom);
35993 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
35994 height += this.centerBg.getPadding("tb");
35999 syncBodyHeight : function()
36001 var bd = this.body, // the text
36002 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
36004 var height = this.size.height - this.getHeaderFooterHeight(false);
36005 bd.setHeight(height-bd.getMargins("tb"));
36006 var hh = this.header.getHeight();
36007 var h = this.size.height-hh;
36010 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
36011 bw.setHeight(h-cb.getPadding("tb"));
36013 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
36014 bd.setWidth(bw.getWidth(true));
36016 this.tabs.syncHeight();
36018 this.tabs.el.repaint();
36024 * Restores the previous state of the dialog if Roo.state is configured.
36025 * @return {Roo.BasicDialog} this
36027 restoreState : function(){
36028 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
36029 if(box && box.width){
36030 this.xy = [box.x, box.y];
36031 this.resizeTo(box.width, box.height);
36037 beforeShow : function(){
36039 if(this.fixedcenter){
36040 this.xy = this.el.getCenterXY(true);
36043 Roo.get(document.body).addClass("x-body-masked");
36044 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36047 this.constrainXY();
36051 animShow : function(){
36052 var b = Roo.get(this.animateTarget).getBox();
36053 this.proxy.setSize(b.width, b.height);
36054 this.proxy.setLocation(b.x, b.y);
36056 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
36057 true, .35, this.showEl.createDelegate(this));
36061 * Shows the dialog.
36062 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
36063 * @return {Roo.BasicDialog} this
36065 show : function(animateTarget){
36066 if (this.fireEvent("beforeshow", this) === false){
36069 if(this.syncHeightBeforeShow){
36070 this.syncBodyHeight();
36071 }else if(this.firstShow){
36072 this.firstShow = false;
36073 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
36075 this.animateTarget = animateTarget || this.animateTarget;
36076 if(!this.el.isVisible()){
36078 if(this.animateTarget && Roo.get(this.animateTarget)){
36088 showEl : function(){
36090 this.el.setXY(this.xy);
36092 this.adjustAssets(true);
36095 // IE peekaboo bug - fix found by Dave Fenwick
36099 this.fireEvent("show", this);
36103 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
36104 * dialog itself will receive focus.
36106 focus : function(){
36107 if(this.defaultButton){
36108 this.defaultButton.focus();
36110 this.focusEl.focus();
36115 constrainXY : function(){
36116 if(this.constraintoviewport !== false){
36117 if(!this.viewSize){
36118 if(this.container){
36119 var s = this.container.getSize();
36120 this.viewSize = [s.width, s.height];
36122 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
36125 var s = Roo.get(this.container||document).getScroll();
36127 var x = this.xy[0], y = this.xy[1];
36128 var w = this.size.width, h = this.size.height;
36129 var vw = this.viewSize[0], vh = this.viewSize[1];
36130 // only move it if it needs it
36132 // first validate right/bottom
36133 if(x + w > vw+s.left){
36137 if(y + h > vh+s.top){
36141 // then make sure top/left isn't negative
36153 if(this.isVisible()){
36154 this.el.setLocation(x, y);
36155 this.adjustAssets();
36162 onDrag : function(){
36163 if(!this.proxyDrag){
36164 this.xy = this.el.getXY();
36165 this.adjustAssets();
36170 adjustAssets : function(doShow){
36171 var x = this.xy[0], y = this.xy[1];
36172 var w = this.size.width, h = this.size.height;
36173 if(doShow === true){
36175 this.shadow.show(this.el);
36181 if(this.shadow && this.shadow.isVisible()){
36182 this.shadow.show(this.el);
36184 if(this.shim && this.shim.isVisible()){
36185 this.shim.setBounds(x, y, w, h);
36190 adjustViewport : function(w, h){
36192 w = Roo.lib.Dom.getViewWidth();
36193 h = Roo.lib.Dom.getViewHeight();
36196 this.viewSize = [w, h];
36197 if(this.modal && this.mask.isVisible()){
36198 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
36199 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36201 if(this.isVisible()){
36202 this.constrainXY();
36207 * Destroys this dialog and all its supporting elements (including any tabs, shim,
36208 * shadow, proxy, mask, etc.) Also removes all event listeners.
36209 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
36211 destroy : function(removeEl){
36212 if(this.isVisible()){
36213 this.animateTarget = null;
36216 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
36218 this.tabs.destroy(removeEl);
36231 for(var i = 0, len = this.buttons.length; i < len; i++){
36232 this.buttons[i].destroy();
36235 this.el.removeAllListeners();
36236 if(removeEl === true){
36237 this.el.update("");
36240 Roo.DialogManager.unregister(this);
36244 startMove : function(){
36245 if(this.proxyDrag){
36248 if(this.constraintoviewport !== false){
36249 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
36254 endMove : function(){
36255 if(!this.proxyDrag){
36256 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
36258 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
36261 this.refreshSize();
36262 this.adjustAssets();
36264 this.fireEvent("move", this, this.xy[0], this.xy[1]);
36268 * Brings this dialog to the front of any other visible dialogs
36269 * @return {Roo.BasicDialog} this
36271 toFront : function(){
36272 Roo.DialogManager.bringToFront(this);
36277 * Sends this dialog to the back (under) of any other visible dialogs
36278 * @return {Roo.BasicDialog} this
36280 toBack : function(){
36281 Roo.DialogManager.sendToBack(this);
36286 * Centers this dialog in the viewport
36287 * @return {Roo.BasicDialog} this
36289 center : function(){
36290 var xy = this.el.getCenterXY(true);
36291 this.moveTo(xy[0], xy[1]);
36296 * Moves the dialog's top-left corner to the specified point
36297 * @param {Number} x
36298 * @param {Number} y
36299 * @return {Roo.BasicDialog} this
36301 moveTo : function(x, y){
36303 if(this.isVisible()){
36304 this.el.setXY(this.xy);
36305 this.adjustAssets();
36311 * Aligns the dialog to the specified element
36312 * @param {String/HTMLElement/Roo.Element} element The element to align to.
36313 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
36314 * @param {Array} offsets (optional) Offset the positioning by [x, y]
36315 * @return {Roo.BasicDialog} this
36317 alignTo : function(element, position, offsets){
36318 this.xy = this.el.getAlignToXY(element, position, offsets);
36319 if(this.isVisible()){
36320 this.el.setXY(this.xy);
36321 this.adjustAssets();
36327 * Anchors an element to another element and realigns it when the window is resized.
36328 * @param {String/HTMLElement/Roo.Element} element The element to align to.
36329 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
36330 * @param {Array} offsets (optional) Offset the positioning by [x, y]
36331 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
36332 * is a number, it is used as the buffer delay (defaults to 50ms).
36333 * @return {Roo.BasicDialog} this
36335 anchorTo : function(el, alignment, offsets, monitorScroll){
36336 var action = function(){
36337 this.alignTo(el, alignment, offsets);
36339 Roo.EventManager.onWindowResize(action, this);
36340 var tm = typeof monitorScroll;
36341 if(tm != 'undefined'){
36342 Roo.EventManager.on(window, 'scroll', action, this,
36343 {buffer: tm == 'number' ? monitorScroll : 50});
36350 * Returns true if the dialog is visible
36351 * @return {Boolean}
36353 isVisible : function(){
36354 return this.el.isVisible();
36358 animHide : function(callback){
36359 var b = Roo.get(this.animateTarget).getBox();
36361 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
36363 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
36364 this.hideEl.createDelegate(this, [callback]));
36368 * Hides the dialog.
36369 * @param {Function} callback (optional) Function to call when the dialog is hidden
36370 * @return {Roo.BasicDialog} this
36372 hide : function(callback){
36373 if (this.fireEvent("beforehide", this) === false){
36377 this.shadow.hide();
36382 // sometimes animateTarget seems to get set.. causing problems...
36383 // this just double checks..
36384 if(this.animateTarget && Roo.get(this.animateTarget)) {
36385 this.animHide(callback);
36388 this.hideEl(callback);
36394 hideEl : function(callback){
36398 Roo.get(document.body).removeClass("x-body-masked");
36400 this.fireEvent("hide", this);
36401 if(typeof callback == "function"){
36407 hideAction : function(){
36408 this.setLeft("-10000px");
36409 this.setTop("-10000px");
36410 this.setStyle("visibility", "hidden");
36414 refreshSize : function(){
36415 this.size = this.el.getSize();
36416 this.xy = this.el.getXY();
36417 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
36421 // z-index is managed by the DialogManager and may be overwritten at any time
36422 setZIndex : function(index){
36424 this.mask.setStyle("z-index", index);
36427 this.shim.setStyle("z-index", ++index);
36430 this.shadow.setZIndex(++index);
36432 this.el.setStyle("z-index", ++index);
36434 this.proxy.setStyle("z-index", ++index);
36437 this.resizer.proxy.setStyle("z-index", ++index);
36440 this.lastZIndex = index;
36444 * Returns the element for this dialog
36445 * @return {Roo.Element} The underlying dialog Element
36447 getEl : function(){
36453 * @class Roo.DialogManager
36454 * Provides global access to BasicDialogs that have been created and
36455 * support for z-indexing (layering) multiple open dialogs.
36457 Roo.DialogManager = function(){
36459 var accessList = [];
36463 var sortDialogs = function(d1, d2){
36464 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
36468 var orderDialogs = function(){
36469 accessList.sort(sortDialogs);
36470 var seed = Roo.DialogManager.zseed;
36471 for(var i = 0, len = accessList.length; i < len; i++){
36472 var dlg = accessList[i];
36474 dlg.setZIndex(seed + (i*10));
36481 * The starting z-index for BasicDialogs (defaults to 9000)
36482 * @type Number The z-index value
36487 register : function(dlg){
36488 list[dlg.id] = dlg;
36489 accessList.push(dlg);
36493 unregister : function(dlg){
36494 delete list[dlg.id];
36497 if(!accessList.indexOf){
36498 for( i = 0, len = accessList.length; i < len; i++){
36499 if(accessList[i] == dlg){
36500 accessList.splice(i, 1);
36505 i = accessList.indexOf(dlg);
36507 accessList.splice(i, 1);
36513 * Gets a registered dialog by id
36514 * @param {String/Object} id The id of the dialog or a dialog
36515 * @return {Roo.BasicDialog} this
36517 get : function(id){
36518 return typeof id == "object" ? id : list[id];
36522 * Brings the specified dialog to the front
36523 * @param {String/Object} dlg The id of the dialog or a dialog
36524 * @return {Roo.BasicDialog} this
36526 bringToFront : function(dlg){
36527 dlg = this.get(dlg);
36530 dlg._lastAccess = new Date().getTime();
36537 * Sends the specified dialog to the back
36538 * @param {String/Object} dlg The id of the dialog or a dialog
36539 * @return {Roo.BasicDialog} this
36541 sendToBack : function(dlg){
36542 dlg = this.get(dlg);
36543 dlg._lastAccess = -(new Date().getTime());
36549 * Hides all dialogs
36551 hideAll : function(){
36552 for(var id in list){
36553 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
36562 * @class Roo.LayoutDialog
36563 * @extends Roo.BasicDialog
36564 * @children Roo.panel.Content
36565 * @parent builder none
36566 * Dialog which provides adjustments for working with a layout in a Dialog.
36567 * Add your necessary layout config options to the dialog's config.<br>
36568 * Example usage (including a nested layout):
36571 dialog = new Roo.LayoutDialog("download-dlg", {
36580 // layout config merges with the dialog config
36582 tabPosition: "top",
36583 alwaysShowTabs: true
36586 dialog.addKeyListener(27, dialog.hide, dialog);
36587 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
36588 dialog.addButton("Build It!", this.getDownload, this);
36590 // we can even add nested layouts
36591 var innerLayout = new Roo.layout.Border("dl-inner", {
36601 innerLayout.beginUpdate();
36602 innerLayout.add("east", new Roo.panel.Content("dl-details"));
36603 innerLayout.add("center", new Roo.panel.Content("selection-panel"));
36604 innerLayout.endUpdate(true);
36606 var layout = dialog.getLayout();
36607 layout.beginUpdate();
36608 layout.add("center", new Roo.panel.Content("standard-panel",
36609 {title: "Download the Source", fitToFrame:true}));
36610 layout.add("center", new Roo.panel.NestedLayout(innerLayout,
36611 {title: "Build your own roo.js"}));
36612 layout.getRegion("center").showPanel(sp);
36613 layout.endUpdate();
36617 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
36618 * @param {Object} config configuration options
36620 Roo.LayoutDialog = function(el, cfg){
36623 if (typeof(cfg) == 'undefined') {
36624 config = Roo.apply({}, el);
36625 // not sure why we use documentElement here.. - it should always be body.
36626 // IE7 borks horribly if we use documentElement.
36627 // webkit also does not like documentElement - it creates a body element...
36628 el = Roo.get( document.body || document.documentElement ).createChild();
36629 //config.autoCreate = true;
36633 config.autoTabs = false;
36634 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
36635 this.body.setStyle({overflow:"hidden", position:"relative"});
36636 this.layout = new Roo.layout.Border(this.body.dom, config);
36637 this.layout.monitorWindowResize = false;
36638 this.el.addClass("x-dlg-auto-layout");
36639 // fix case when center region overwrites center function
36640 this.center = Roo.BasicDialog.prototype.center;
36641 this.on("show", this.layout.layout, this.layout, true);
36642 if (config.items) {
36643 var xitems = config.items;
36644 delete config.items;
36645 Roo.each(xitems, this.addxtype, this);
36650 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
36654 * @cfg {Roo.layout.Region} east
36657 * @cfg {Roo.layout.Region} west
36660 * @cfg {Roo.layout.Region} south
36663 * @cfg {Roo.layout.Region} north
36666 * @cfg {Roo.layout.Region} center
36669 * @cfg {Roo.Button} buttons[] Bottom buttons..
36674 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
36677 endUpdate : function(){
36678 this.layout.endUpdate();
36682 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
36685 beginUpdate : function(){
36686 this.layout.beginUpdate();
36690 * Get the BorderLayout for this dialog
36691 * @return {Roo.layout.Border}
36693 getLayout : function(){
36694 return this.layout;
36697 showEl : function(){
36698 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
36700 this.layout.layout();
36705 // Use the syncHeightBeforeShow config option to control this automatically
36706 syncBodyHeight : function(){
36707 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
36708 if(this.layout){this.layout.layout();}
36712 * Add an xtype element (actually adds to the layout.)
36713 * @return {Object} xdata xtype object data.
36716 addxtype : function(c) {
36717 return this.layout.addxtype(c);
36721 * Ext JS Library 1.1.1
36722 * Copyright(c) 2006-2007, Ext JS, LLC.
36724 * Originally Released Under LGPL - original licence link has changed is not relivant.
36727 * <script type="text/javascript">
36731 * @class Roo.MessageBox
36733 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
36737 Roo.Msg.alert('Status', 'Changes saved successfully.');
36739 // Prompt for user data:
36740 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
36742 // process text value...
36746 // Show a dialog using config options:
36748 title:'Save Changes?',
36749 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
36750 buttons: Roo.Msg.YESNOCANCEL,
36757 Roo.MessageBox = function(){
36758 var dlg, opt, mask, waitTimer;
36759 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
36760 var buttons, activeTextEl, bwidth;
36763 var handleButton = function(button){
36765 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
36769 var handleHide = function(){
36770 if(opt && opt.cls){
36771 dlg.el.removeClass(opt.cls);
36774 Roo.TaskMgr.stop(waitTimer);
36780 var updateButtons = function(b){
36783 buttons["ok"].hide();
36784 buttons["cancel"].hide();
36785 buttons["yes"].hide();
36786 buttons["no"].hide();
36787 dlg.footer.dom.style.display = 'none';
36790 dlg.footer.dom.style.display = '';
36791 for(var k in buttons){
36792 if(typeof buttons[k] != "function"){
36795 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
36796 width += buttons[k].el.getWidth()+15;
36806 var handleEsc = function(d, k, e){
36807 if(opt && opt.closable !== false){
36817 * Returns a reference to the underlying {@link Roo.BasicDialog} element
36818 * @return {Roo.BasicDialog} The BasicDialog element
36820 getDialog : function(){
36822 dlg = new Roo.BasicDialog("x-msg-box", {
36827 constraintoviewport:false,
36829 collapsible : false,
36832 width:400, height:100,
36833 buttonAlign:"center",
36834 closeClick : function(){
36835 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
36836 handleButton("no");
36838 handleButton("cancel");
36843 dlg.on("hide", handleHide);
36845 dlg.addKeyListener(27, handleEsc);
36847 var bt = this.buttonText;
36848 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
36849 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
36850 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
36851 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
36852 bodyEl = dlg.body.createChild({
36854 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>'
36856 msgEl = bodyEl.dom.firstChild;
36857 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
36858 textboxEl.enableDisplayMode();
36859 textboxEl.addKeyListener([10,13], function(){
36860 if(dlg.isVisible() && opt && opt.buttons){
36861 if(opt.buttons.ok){
36862 handleButton("ok");
36863 }else if(opt.buttons.yes){
36864 handleButton("yes");
36868 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
36869 textareaEl.enableDisplayMode();
36870 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
36871 progressEl.enableDisplayMode();
36872 var pf = progressEl.dom.firstChild;
36874 pp = Roo.get(pf.firstChild);
36875 pp.setHeight(pf.offsetHeight);
36883 * Updates the message box body text
36884 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
36885 * the XHTML-compliant non-breaking space character '&#160;')
36886 * @return {Roo.MessageBox} This message box
36888 updateText : function(text){
36889 if(!dlg.isVisible() && !opt.width){
36890 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
36892 msgEl.innerHTML = text || ' ';
36894 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
36895 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
36897 Math.min(opt.width || cw , this.maxWidth),
36898 Math.max(opt.minWidth || this.minWidth, bwidth)
36901 activeTextEl.setWidth(w);
36903 if(dlg.isVisible()){
36904 dlg.fixedcenter = false;
36906 // to big, make it scroll. = But as usual stupid IE does not support
36909 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
36910 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
36911 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
36913 bodyEl.dom.style.height = '';
36914 bodyEl.dom.style.overflowY = '';
36917 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
36919 bodyEl.dom.style.overflowX = '';
36922 dlg.setContentSize(w, bodyEl.getHeight());
36923 if(dlg.isVisible()){
36924 dlg.fixedcenter = true;
36930 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
36931 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
36932 * @param {Number} value Any number between 0 and 1 (e.g., .5)
36933 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
36934 * @return {Roo.MessageBox} This message box
36936 updateProgress : function(value, text){
36938 this.updateText(text);
36940 if (pp) { // weird bug on my firefox - for some reason this is not defined
36941 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
36947 * Returns true if the message box is currently displayed
36948 * @return {Boolean} True if the message box is visible, else false
36950 isVisible : function(){
36951 return dlg && dlg.isVisible();
36955 * Hides the message box if it is displayed
36958 if(this.isVisible()){
36964 * Displays a new message box, or reinitializes an existing message box, based on the config options
36965 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
36966 * The following config object properties are supported:
36968 Property Type Description
36969 ---------- --------------- ------------------------------------------------------------------------------------
36970 animEl String/Element An id or Element from which the message box should animate as it opens and
36971 closes (defaults to undefined)
36972 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
36973 cancel:'Bar'}), or false to not show any buttons (defaults to false)
36974 closable Boolean False to hide the top-right close button (defaults to true). Note that
36975 progress and wait dialogs will ignore this property and always hide the
36976 close button as they can only be closed programmatically.
36977 cls String A custom CSS class to apply to the message box element
36978 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
36979 displayed (defaults to 75)
36980 fn Function A callback function to execute after closing the dialog. The arguments to the
36981 function will be btn (the name of the button that was clicked, if applicable,
36982 e.g. "ok"), and text (the value of the active text field, if applicable).
36983 Progress and wait dialogs will ignore this option since they do not respond to
36984 user actions and can only be closed programmatically, so any required function
36985 should be called by the same code after it closes the dialog.
36986 icon String A CSS class that provides a background image to be used as an icon for
36987 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
36988 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
36989 minWidth Number The minimum width in pixels of the message box (defaults to 100)
36990 modal Boolean False to allow user interaction with the page while the message box is
36991 displayed (defaults to true)
36992 msg String A string that will replace the existing message box body text (defaults
36993 to the XHTML-compliant non-breaking space character ' ')
36994 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
36995 progress Boolean True to display a progress bar (defaults to false)
36996 progressText String The text to display inside the progress bar if progress = true (defaults to '')
36997 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
36998 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
36999 title String The title text
37000 value String The string value to set into the active textbox element if displayed
37001 wait Boolean True to display a progress bar (defaults to false)
37002 width Number The width of the dialog in pixels
37009 msg: 'Please enter your address:',
37011 buttons: Roo.MessageBox.OKCANCEL,
37014 animEl: 'addAddressBtn'
37017 * @param {Object} config Configuration options
37018 * @return {Roo.MessageBox} This message box
37020 show : function(options)
37023 // this causes nightmares if you show one dialog after another
37024 // especially on callbacks..
37026 if(this.isVisible()){
37029 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
37030 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
37031 Roo.log("New Dialog Message:" + options.msg )
37032 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
37033 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
37036 var d = this.getDialog();
37038 d.setTitle(opt.title || " ");
37039 d.close.setDisplayed(opt.closable !== false);
37040 activeTextEl = textboxEl;
37041 opt.prompt = opt.prompt || (opt.multiline ? true : false);
37046 textareaEl.setHeight(typeof opt.multiline == "number" ?
37047 opt.multiline : this.defaultTextHeight);
37048 activeTextEl = textareaEl;
37057 progressEl.setDisplayed(opt.progress === true);
37058 this.updateProgress(0);
37059 activeTextEl.dom.value = opt.value || "";
37061 dlg.setDefaultButton(activeTextEl);
37063 var bs = opt.buttons;
37066 db = buttons["ok"];
37067 }else if(bs && bs.yes){
37068 db = buttons["yes"];
37070 dlg.setDefaultButton(db);
37072 bwidth = updateButtons(opt.buttons);
37073 this.updateText(opt.msg);
37075 d.el.addClass(opt.cls);
37077 d.proxyDrag = opt.proxyDrag === true;
37078 d.modal = opt.modal !== false;
37079 d.mask = opt.modal !== false ? mask : false;
37080 if(!d.isVisible()){
37081 // force it to the end of the z-index stack so it gets a cursor in FF
37082 document.body.appendChild(dlg.el.dom);
37083 d.animateTarget = null;
37084 d.show(options.animEl);
37091 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
37092 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
37093 * and closing the message box when the process is complete.
37094 * @param {String} title The title bar text
37095 * @param {String} msg The message box body text
37096 * @return {Roo.MessageBox} This message box
37098 progress : function(title, msg){
37105 minWidth: this.minProgressWidth,
37112 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
37113 * If a callback function is passed it will be called after the user clicks the button, and the
37114 * id of the button that was clicked will be passed as the only parameter to the callback
37115 * (could also be the top-right close button).
37116 * @param {String} title The title bar text
37117 * @param {String} msg The message box body text
37118 * @param {Function} fn (optional) The callback function invoked after the message box is closed
37119 * @param {Object} scope (optional) The scope of the callback function
37120 * @return {Roo.MessageBox} This message box
37122 alert : function(title, msg, fn, scope){
37135 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
37136 * interaction while waiting for a long-running process to complete that does not have defined intervals.
37137 * You are responsible for closing the message box when the process is complete.
37138 * @param {String} msg The message box body text
37139 * @param {String} title (optional) The title bar text
37140 * @return {Roo.MessageBox} This message box
37142 wait : function(msg, title){
37153 waitTimer = Roo.TaskMgr.start({
37155 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
37163 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
37164 * If a callback function is passed it will be called after the user clicks either button, and the id of the
37165 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
37166 * @param {String} title The title bar text
37167 * @param {String} msg The message box body text
37168 * @param {Function} fn (optional) The callback function invoked after the message box is closed
37169 * @param {Object} scope (optional) The scope of the callback function
37170 * @return {Roo.MessageBox} This message box
37172 confirm : function(title, msg, fn, scope){
37176 buttons: this.YESNO,
37185 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
37186 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
37187 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
37188 * (could also be the top-right close button) and the text that was entered will be passed as the two
37189 * parameters to the callback.
37190 * @param {String} title The title bar text
37191 * @param {String} msg The message box body text
37192 * @param {Function} fn (optional) The callback function invoked after the message box is closed
37193 * @param {Object} scope (optional) The scope of the callback function
37194 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
37195 * property, or the height in pixels to create the textbox (defaults to false / single-line)
37196 * @return {Roo.MessageBox} This message box
37198 prompt : function(title, msg, fn, scope, multiline){
37202 buttons: this.OKCANCEL,
37207 multiline: multiline,
37214 * Button config that displays a single OK button
37219 * Button config that displays Yes and No buttons
37222 YESNO : {yes:true, no:true},
37224 * Button config that displays OK and Cancel buttons
37227 OKCANCEL : {ok:true, cancel:true},
37229 * Button config that displays Yes, No and Cancel buttons
37232 YESNOCANCEL : {yes:true, no:true, cancel:true},
37235 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
37238 defaultTextHeight : 75,
37240 * The maximum width in pixels of the message box (defaults to 600)
37245 * The minimum width in pixels of the message box (defaults to 100)
37250 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
37251 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
37254 minProgressWidth : 250,
37256 * An object containing the default button text strings that can be overriden for localized language support.
37257 * Supported properties are: ok, cancel, yes and no.
37258 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
37271 * Shorthand for {@link Roo.MessageBox}
37273 Roo.Msg = Roo.MessageBox;/*
37275 * Ext JS Library 1.1.1
37276 * Copyright(c) 2006-2007, Ext JS, LLC.
37278 * Originally Released Under LGPL - original licence link has changed is not relivant.
37281 * <script type="text/javascript">
37284 * @class Roo.QuickTips
37285 * Provides attractive and customizable tooltips for any element.
37288 Roo.QuickTips = function(){
37289 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
37290 var ce, bd, xy, dd;
37291 var visible = false, disabled = true, inited = false;
37292 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
37294 var onOver = function(e){
37298 var t = e.getTarget();
37299 if(!t || t.nodeType !== 1 || t == document || t == document.body){
37302 if(ce && t == ce.el){
37303 clearTimeout(hideProc);
37306 if(t && tagEls[t.id]){
37307 tagEls[t.id].el = t;
37308 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
37311 var ttp, et = Roo.fly(t);
37312 var ns = cfg.namespace;
37313 if(tm.interceptTitles && t.title){
37316 t.removeAttribute("title");
37317 e.preventDefault();
37319 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
37322 showProc = show.defer(tm.showDelay, tm, [{
37324 text: ttp.replace(/\\n/g,'<br/>'),
37325 width: et.getAttributeNS(ns, cfg.width),
37326 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
37327 title: et.getAttributeNS(ns, cfg.title),
37328 cls: et.getAttributeNS(ns, cfg.cls)
37333 var onOut = function(e){
37334 clearTimeout(showProc);
37335 var t = e.getTarget();
37336 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
37337 hideProc = setTimeout(hide, tm.hideDelay);
37341 var onMove = function(e){
37347 if(tm.trackMouse && ce){
37352 var onDown = function(e){
37353 clearTimeout(showProc);
37354 clearTimeout(hideProc);
37356 if(tm.hideOnClick){
37359 tm.enable.defer(100, tm);
37364 var getPad = function(){
37365 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
37368 var show = function(o){
37372 clearTimeout(dismissProc);
37374 if(removeCls){ // in case manually hidden
37375 el.removeClass(removeCls);
37379 el.addClass(ce.cls);
37380 removeCls = ce.cls;
37383 tipTitle.update(ce.title);
37386 tipTitle.update('');
37389 el.dom.style.width = tm.maxWidth+'px';
37390 //tipBody.dom.style.width = '';
37391 tipBodyText.update(o.text);
37392 var p = getPad(), w = ce.width;
37394 var td = tipBodyText.dom;
37395 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
37396 if(aw > tm.maxWidth){
37398 }else if(aw < tm.minWidth){
37404 //tipBody.setWidth(w);
37405 el.setWidth(parseInt(w, 10) + p);
37406 if(ce.autoHide === false){
37407 close.setDisplayed(true);
37412 close.setDisplayed(false);
37418 el.avoidY = xy[1]-18;
37423 el.setStyle("visibility", "visible");
37424 el.fadeIn({callback: afterShow});
37430 var afterShow = function(){
37434 if(tm.autoDismiss && ce.autoHide !== false){
37435 dismissProc = setTimeout(hide, tm.autoDismissDelay);
37440 var hide = function(noanim){
37441 clearTimeout(dismissProc);
37442 clearTimeout(hideProc);
37444 if(el.isVisible()){
37446 if(noanim !== true && tm.animate){
37447 el.fadeOut({callback: afterHide});
37454 var afterHide = function(){
37457 el.removeClass(removeCls);
37464 * @cfg {Number} minWidth
37465 * The minimum width of the quick tip (defaults to 40)
37469 * @cfg {Number} maxWidth
37470 * The maximum width of the quick tip (defaults to 300)
37474 * @cfg {Boolean} interceptTitles
37475 * True to automatically use the element's DOM title value if available (defaults to false)
37477 interceptTitles : false,
37479 * @cfg {Boolean} trackMouse
37480 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
37482 trackMouse : false,
37484 * @cfg {Boolean} hideOnClick
37485 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
37487 hideOnClick : true,
37489 * @cfg {Number} showDelay
37490 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
37494 * @cfg {Number} hideDelay
37495 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
37499 * @cfg {Boolean} autoHide
37500 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
37501 * Used in conjunction with hideDelay.
37506 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
37507 * (defaults to true). Used in conjunction with autoDismissDelay.
37509 autoDismiss : true,
37512 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
37514 autoDismissDelay : 5000,
37516 * @cfg {Boolean} animate
37517 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
37522 * @cfg {String} title
37523 * Title text to display (defaults to ''). This can be any valid HTML markup.
37527 * @cfg {String} text
37528 * Body text to display (defaults to ''). This can be any valid HTML markup.
37532 * @cfg {String} cls
37533 * A CSS class to apply to the base quick tip element (defaults to '').
37537 * @cfg {Number} width
37538 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
37539 * minWidth or maxWidth.
37544 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
37545 * or display QuickTips in a page.
37548 tm = Roo.QuickTips;
37549 cfg = tm.tagConfig;
37551 if(!Roo.isReady){ // allow calling of init() before onReady
37552 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
37555 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
37556 el.fxDefaults = {stopFx: true};
37557 // maximum custom styling
37558 //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>');
37559 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>');
37560 tipTitle = el.child('h3');
37561 tipTitle.enableDisplayMode("block");
37562 tipBody = el.child('div.x-tip-bd');
37563 tipBodyText = el.child('div.x-tip-bd-inner');
37564 //bdLeft = el.child('div.x-tip-bd-left');
37565 //bdRight = el.child('div.x-tip-bd-right');
37566 close = el.child('div.x-tip-close');
37567 close.enableDisplayMode("block");
37568 close.on("click", hide);
37569 var d = Roo.get(document);
37570 d.on("mousedown", onDown);
37571 d.on("mouseover", onOver);
37572 d.on("mouseout", onOut);
37573 d.on("mousemove", onMove);
37574 esc = d.addKeyListener(27, hide);
37577 dd = el.initDD("default", null, {
37578 onDrag : function(){
37582 dd.setHandleElId(tipTitle.id);
37591 * Configures a new quick tip instance and assigns it to a target element. The following config options
37594 Property Type Description
37595 ---------- --------------------- ------------------------------------------------------------------------
37596 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
37598 * @param {Object} config The config object
37600 register : function(config){
37601 var cs = config instanceof Array ? config : arguments;
37602 for(var i = 0, len = cs.length; i < len; i++) {
37604 var target = c.target;
37606 if(target instanceof Array){
37607 for(var j = 0, jlen = target.length; j < jlen; j++){
37608 tagEls[target[j]] = c;
37611 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
37618 * Removes this quick tip from its element and destroys it.
37619 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
37621 unregister : function(el){
37622 delete tagEls[Roo.id(el)];
37626 * Enable this quick tip.
37628 enable : function(){
37629 if(inited && disabled){
37631 if(locks.length < 1){
37638 * Disable this quick tip.
37640 disable : function(){
37642 clearTimeout(showProc);
37643 clearTimeout(hideProc);
37644 clearTimeout(dismissProc);
37652 * Returns true if the quick tip is enabled, else false.
37654 isEnabled : function(){
37660 namespace : "roo", // was ext?? this may break..
37661 alt_namespace : "ext",
37662 attribute : "qtip",
37672 // backwards compat
37673 Roo.QuickTips.tips = Roo.QuickTips.register;/*
37675 * Ext JS Library 1.1.1
37676 * Copyright(c) 2006-2007, Ext JS, LLC.
37678 * Originally Released Under LGPL - original licence link has changed is not relivant.
37681 * <script type="text/javascript">
37686 * @class Roo.tree.TreePanel
37687 * @extends Roo.data.Tree
37688 * @cfg {Roo.tree.TreeNode} root The root node
37689 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
37690 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
37691 * @cfg {Boolean} enableDD true to enable drag and drop
37692 * @cfg {Boolean} enableDrag true to enable just drag
37693 * @cfg {Boolean} enableDrop true to enable just drop
37694 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
37695 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
37696 * @cfg {String} ddGroup The DD group this TreePanel belongs to
37697 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
37698 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
37699 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
37700 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
37701 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
37702 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
37703 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
37704 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
37705 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
37706 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
37707 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
37708 * @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>
37709 * @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>
37712 * @param {String/HTMLElement/Element} el The container element
37713 * @param {Object} config
37715 Roo.tree.TreePanel = function(el, config){
37717 var loader = false;
37719 root = config.root;
37720 delete config.root;
37722 if (config.loader) {
37723 loader = config.loader;
37724 delete config.loader;
37727 Roo.apply(this, config);
37728 Roo.tree.TreePanel.superclass.constructor.call(this);
37729 this.el = Roo.get(el);
37730 this.el.addClass('x-tree');
37731 //console.log(root);
37733 this.setRootNode( Roo.factory(root, Roo.tree));
37736 this.loader = Roo.factory(loader, Roo.tree);
37739 * Read-only. The id of the container element becomes this TreePanel's id.
37741 this.id = this.el.id;
37744 * @event beforeload
37745 * Fires before a node is loaded, return false to cancel
37746 * @param {Node} node The node being loaded
37748 "beforeload" : true,
37751 * Fires when a node is loaded
37752 * @param {Node} node The node that was loaded
37756 * @event textchange
37757 * Fires when the text for a node is changed
37758 * @param {Node} node The node
37759 * @param {String} text The new text
37760 * @param {String} oldText The old text
37762 "textchange" : true,
37764 * @event beforeexpand
37765 * Fires before a node is expanded, return false to cancel.
37766 * @param {Node} node The node
37767 * @param {Boolean} deep
37768 * @param {Boolean} anim
37770 "beforeexpand" : true,
37772 * @event beforecollapse
37773 * Fires before a node is collapsed, return false to cancel.
37774 * @param {Node} node The node
37775 * @param {Boolean} deep
37776 * @param {Boolean} anim
37778 "beforecollapse" : true,
37781 * Fires when a node is expanded
37782 * @param {Node} node The node
37786 * @event disabledchange
37787 * Fires when the disabled status of a node changes
37788 * @param {Node} node The node
37789 * @param {Boolean} disabled
37791 "disabledchange" : true,
37794 * Fires when a node is collapsed
37795 * @param {Node} node The node
37799 * @event beforeclick
37800 * Fires before click processing on a node. Return false to cancel the default action.
37801 * @param {Node} node The node
37802 * @param {Roo.EventObject} e The event object
37804 "beforeclick":true,
37806 * @event checkchange
37807 * Fires when a node with a checkbox's checked property changes
37808 * @param {Node} this This node
37809 * @param {Boolean} checked
37811 "checkchange":true,
37814 * Fires when a node is clicked
37815 * @param {Node} node The node
37816 * @param {Roo.EventObject} e The event object
37821 * Fires when a node is double clicked
37822 * @param {Node} node The node
37823 * @param {Roo.EventObject} e The event object
37827 * @event contextmenu
37828 * Fires when a node is right clicked
37829 * @param {Node} node The node
37830 * @param {Roo.EventObject} e The event object
37832 "contextmenu":true,
37834 * @event beforechildrenrendered
37835 * Fires right before the child nodes for a node are rendered
37836 * @param {Node} node The node
37838 "beforechildrenrendered":true,
37841 * Fires when a node starts being dragged
37842 * @param {Roo.tree.TreePanel} this
37843 * @param {Roo.tree.TreeNode} node
37844 * @param {event} e The raw browser event
37846 "startdrag" : true,
37849 * Fires when a drag operation is complete
37850 * @param {Roo.tree.TreePanel} this
37851 * @param {Roo.tree.TreeNode} node
37852 * @param {event} e The raw browser event
37857 * Fires when a dragged node is dropped on a valid DD target
37858 * @param {Roo.tree.TreePanel} this
37859 * @param {Roo.tree.TreeNode} node
37860 * @param {DD} dd The dd it was dropped on
37861 * @param {event} e The raw browser event
37865 * @event beforenodedrop
37866 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
37867 * passed to handlers has the following properties:<br />
37868 * <ul style="padding:5px;padding-left:16px;">
37869 * <li>tree - The TreePanel</li>
37870 * <li>target - The node being targeted for the drop</li>
37871 * <li>data - The drag data from the drag source</li>
37872 * <li>point - The point of the drop - append, above or below</li>
37873 * <li>source - The drag source</li>
37874 * <li>rawEvent - Raw mouse event</li>
37875 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
37876 * to be inserted by setting them on this object.</li>
37877 * <li>cancel - Set this to true to cancel the drop.</li>
37879 * @param {Object} dropEvent
37881 "beforenodedrop" : true,
37884 * Fires after a DD object is dropped on a node in this tree. The dropEvent
37885 * passed to handlers has the following properties:<br />
37886 * <ul style="padding:5px;padding-left:16px;">
37887 * <li>tree - The TreePanel</li>
37888 * <li>target - The node being targeted for the drop</li>
37889 * <li>data - The drag data from the drag source</li>
37890 * <li>point - The point of the drop - append, above or below</li>
37891 * <li>source - The drag source</li>
37892 * <li>rawEvent - Raw mouse event</li>
37893 * <li>dropNode - Dropped node(s).</li>
37895 * @param {Object} dropEvent
37899 * @event nodedragover
37900 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
37901 * passed to handlers has the following properties:<br />
37902 * <ul style="padding:5px;padding-left:16px;">
37903 * <li>tree - The TreePanel</li>
37904 * <li>target - The node being targeted for the drop</li>
37905 * <li>data - The drag data from the drag source</li>
37906 * <li>point - The point of the drop - append, above or below</li>
37907 * <li>source - The drag source</li>
37908 * <li>rawEvent - Raw mouse event</li>
37909 * <li>dropNode - Drop node(s) provided by the source.</li>
37910 * <li>cancel - Set this to true to signal drop not allowed.</li>
37912 * @param {Object} dragOverEvent
37914 "nodedragover" : true,
37916 * @event appendnode
37917 * Fires when append node to the tree
37918 * @param {Roo.tree.TreePanel} this
37919 * @param {Roo.tree.TreeNode} node
37920 * @param {Number} index The index of the newly appended node
37922 "appendnode" : true
37925 if(this.singleExpand){
37926 this.on("beforeexpand", this.restrictExpand, this);
37929 this.editor.tree = this;
37930 this.editor = Roo.factory(this.editor, Roo.tree);
37933 if (this.selModel) {
37934 this.selModel = Roo.factory(this.selModel, Roo.tree);
37938 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
37939 rootVisible : true,
37940 animate: Roo.enableFx,
37943 hlDrop : Roo.enableFx,
37947 rendererTip: false,
37949 restrictExpand : function(node){
37950 var p = node.parentNode;
37952 if(p.expandedChild && p.expandedChild.parentNode == p){
37953 p.expandedChild.collapse();
37955 p.expandedChild = node;
37959 // private override
37960 setRootNode : function(node){
37961 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
37962 if(!this.rootVisible){
37963 node.ui = new Roo.tree.RootTreeNodeUI(node);
37969 * Returns the container element for this TreePanel
37971 getEl : function(){
37976 * Returns the default TreeLoader for this TreePanel
37978 getLoader : function(){
37979 return this.loader;
37985 expandAll : function(){
37986 this.root.expand(true);
37990 * Collapse all nodes
37992 collapseAll : function(){
37993 this.root.collapse(true);
37997 * Returns the selection model used by this TreePanel
37999 getSelectionModel : function(){
38000 if(!this.selModel){
38001 this.selModel = new Roo.tree.DefaultSelectionModel();
38003 return this.selModel;
38007 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
38008 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
38009 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
38012 getChecked : function(a, startNode){
38013 startNode = startNode || this.root;
38015 var f = function(){
38016 if(this.attributes.checked){
38017 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
38020 startNode.cascade(f);
38025 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38026 * @param {String} path
38027 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38028 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
38029 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
38031 expandPath : function(path, attr, callback){
38032 attr = attr || "id";
38033 var keys = path.split(this.pathSeparator);
38034 var curNode = this.root;
38035 if(curNode.attributes[attr] != keys[1]){ // invalid root
38037 callback(false, null);
38042 var f = function(){
38043 if(++index == keys.length){
38045 callback(true, curNode);
38049 var c = curNode.findChild(attr, keys[index]);
38052 callback(false, curNode);
38057 c.expand(false, false, f);
38059 curNode.expand(false, false, f);
38063 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38064 * @param {String} path
38065 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38066 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
38067 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
38069 selectPath : function(path, attr, callback){
38070 attr = attr || "id";
38071 var keys = path.split(this.pathSeparator);
38072 var v = keys.pop();
38073 if(keys.length > 0){
38074 var f = function(success, node){
38075 if(success && node){
38076 var n = node.findChild(attr, v);
38082 }else if(callback){
38083 callback(false, n);
38087 callback(false, n);
38091 this.expandPath(keys.join(this.pathSeparator), attr, f);
38093 this.root.select();
38095 callback(true, this.root);
38100 getTreeEl : function(){
38105 * Trigger rendering of this TreePanel
38107 render : function(){
38108 if (this.innerCt) {
38109 return this; // stop it rendering more than once!!
38112 this.innerCt = this.el.createChild({tag:"ul",
38113 cls:"x-tree-root-ct " +
38114 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
38116 if(this.containerScroll){
38117 Roo.dd.ScrollManager.register(this.el);
38119 if((this.enableDD || this.enableDrop) && !this.dropZone){
38121 * The dropZone used by this tree if drop is enabled
38122 * @type Roo.tree.TreeDropZone
38124 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
38125 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
38128 if((this.enableDD || this.enableDrag) && !this.dragZone){
38130 * The dragZone used by this tree if drag is enabled
38131 * @type Roo.tree.TreeDragZone
38133 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
38134 ddGroup: this.ddGroup || "TreeDD",
38135 scroll: this.ddScroll
38138 this.getSelectionModel().init(this);
38140 Roo.log("ROOT not set in tree");
38143 this.root.render();
38144 if(!this.rootVisible){
38145 this.root.renderChildren();
38151 * Ext JS Library 1.1.1
38152 * Copyright(c) 2006-2007, Ext JS, LLC.
38154 * Originally Released Under LGPL - original licence link has changed is not relivant.
38157 * <script type="text/javascript">
38162 * @class Roo.tree.DefaultSelectionModel
38163 * @extends Roo.util.Observable
38164 * The default single selection for a TreePanel.
38165 * @param {Object} cfg Configuration
38167 Roo.tree.DefaultSelectionModel = function(cfg){
38168 this.selNode = null;
38174 * @event selectionchange
38175 * Fires when the selected node changes
38176 * @param {DefaultSelectionModel} this
38177 * @param {TreeNode} node the new selection
38179 "selectionchange" : true,
38182 * @event beforeselect
38183 * Fires before the selected node changes, return false to cancel the change
38184 * @param {DefaultSelectionModel} this
38185 * @param {TreeNode} node the new selection
38186 * @param {TreeNode} node the old selection
38188 "beforeselect" : true
38191 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
38194 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
38195 init : function(tree){
38197 tree.getTreeEl().on("keydown", this.onKeyDown, this);
38198 tree.on("click", this.onNodeClick, this);
38201 onNodeClick : function(node, e){
38202 if (e.ctrlKey && this.selNode == node) {
38203 this.unselect(node);
38211 * @param {TreeNode} node The node to select
38212 * @return {TreeNode} The selected node
38214 select : function(node){
38215 var last = this.selNode;
38216 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
38218 last.ui.onSelectedChange(false);
38220 this.selNode = node;
38221 node.ui.onSelectedChange(true);
38222 this.fireEvent("selectionchange", this, node, last);
38229 * @param {TreeNode} node The node to unselect
38231 unselect : function(node){
38232 if(this.selNode == node){
38233 this.clearSelections();
38238 * Clear all selections
38240 clearSelections : function(){
38241 var n = this.selNode;
38243 n.ui.onSelectedChange(false);
38244 this.selNode = null;
38245 this.fireEvent("selectionchange", this, null);
38251 * Get the selected node
38252 * @return {TreeNode} The selected node
38254 getSelectedNode : function(){
38255 return this.selNode;
38259 * Returns true if the node is selected
38260 * @param {TreeNode} node The node to check
38261 * @return {Boolean}
38263 isSelected : function(node){
38264 return this.selNode == node;
38268 * Selects the node above the selected node in the tree, intelligently walking the nodes
38269 * @return TreeNode The new selection
38271 selectPrevious : function(){
38272 var s = this.selNode || this.lastSelNode;
38276 var ps = s.previousSibling;
38278 if(!ps.isExpanded() || ps.childNodes.length < 1){
38279 return this.select(ps);
38281 var lc = ps.lastChild;
38282 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
38285 return this.select(lc);
38287 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
38288 return this.select(s.parentNode);
38294 * Selects the node above the selected node in the tree, intelligently walking the nodes
38295 * @return TreeNode The new selection
38297 selectNext : function(){
38298 var s = this.selNode || this.lastSelNode;
38302 if(s.firstChild && s.isExpanded()){
38303 return this.select(s.firstChild);
38304 }else if(s.nextSibling){
38305 return this.select(s.nextSibling);
38306 }else if(s.parentNode){
38308 s.parentNode.bubble(function(){
38309 if(this.nextSibling){
38310 newS = this.getOwnerTree().selModel.select(this.nextSibling);
38319 onKeyDown : function(e){
38320 var s = this.selNode || this.lastSelNode;
38321 // undesirable, but required
38326 var k = e.getKey();
38334 this.selectPrevious();
38337 e.preventDefault();
38338 if(s.hasChildNodes()){
38339 if(!s.isExpanded()){
38341 }else if(s.firstChild){
38342 this.select(s.firstChild, e);
38347 e.preventDefault();
38348 if(s.hasChildNodes() && s.isExpanded()){
38350 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
38351 this.select(s.parentNode, e);
38359 * @class Roo.tree.MultiSelectionModel
38360 * @extends Roo.util.Observable
38361 * Multi selection for a TreePanel.
38362 * @param {Object} cfg Configuration
38364 Roo.tree.MultiSelectionModel = function(){
38365 this.selNodes = [];
38369 * @event selectionchange
38370 * Fires when the selected nodes change
38371 * @param {MultiSelectionModel} this
38372 * @param {Array} nodes Array of the selected nodes
38374 "selectionchange" : true
38376 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
38380 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
38381 init : function(tree){
38383 tree.getTreeEl().on("keydown", this.onKeyDown, this);
38384 tree.on("click", this.onNodeClick, this);
38387 onNodeClick : function(node, e){
38388 this.select(node, e, e.ctrlKey);
38393 * @param {TreeNode} node The node to select
38394 * @param {EventObject} e (optional) An event associated with the selection
38395 * @param {Boolean} keepExisting True to retain existing selections
38396 * @return {TreeNode} The selected node
38398 select : function(node, e, keepExisting){
38399 if(keepExisting !== true){
38400 this.clearSelections(true);
38402 if(this.isSelected(node)){
38403 this.lastSelNode = node;
38406 this.selNodes.push(node);
38407 this.selMap[node.id] = node;
38408 this.lastSelNode = node;
38409 node.ui.onSelectedChange(true);
38410 this.fireEvent("selectionchange", this, this.selNodes);
38416 * @param {TreeNode} node The node to unselect
38418 unselect : function(node){
38419 if(this.selMap[node.id]){
38420 node.ui.onSelectedChange(false);
38421 var sn = this.selNodes;
38424 index = sn.indexOf(node);
38426 for(var i = 0, len = sn.length; i < len; i++){
38434 this.selNodes.splice(index, 1);
38436 delete this.selMap[node.id];
38437 this.fireEvent("selectionchange", this, this.selNodes);
38442 * Clear all selections
38444 clearSelections : function(suppressEvent){
38445 var sn = this.selNodes;
38447 for(var i = 0, len = sn.length; i < len; i++){
38448 sn[i].ui.onSelectedChange(false);
38450 this.selNodes = [];
38452 if(suppressEvent !== true){
38453 this.fireEvent("selectionchange", this, this.selNodes);
38459 * Returns true if the node is selected
38460 * @param {TreeNode} node The node to check
38461 * @return {Boolean}
38463 isSelected : function(node){
38464 return this.selMap[node.id] ? true : false;
38468 * Returns an array of the selected nodes
38471 getSelectedNodes : function(){
38472 return this.selNodes;
38475 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
38477 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
38479 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
38482 * Ext JS Library 1.1.1
38483 * Copyright(c) 2006-2007, Ext JS, LLC.
38485 * Originally Released Under LGPL - original licence link has changed is not relivant.
38488 * <script type="text/javascript">
38492 * @class Roo.tree.TreeNode
38493 * @extends Roo.data.Node
38494 * @cfg {String} text The text for this node
38495 * @cfg {Boolean} expanded true to start the node expanded
38496 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
38497 * @cfg {Boolean} allowDrop false if this node cannot be drop on
38498 * @cfg {Boolean} disabled true to start the node disabled
38499 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
38500 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
38501 * @cfg {String} cls A css class to be added to the node
38502 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
38503 * @cfg {String} href URL of the link used for the node (defaults to #)
38504 * @cfg {String} hrefTarget target frame for the link
38505 * @cfg {String} qtip An Ext QuickTip for the node
38506 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
38507 * @cfg {Boolean} singleClickExpand True for single click expand on this node
38508 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
38509 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
38510 * (defaults to undefined with no checkbox rendered)
38512 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
38514 Roo.tree.TreeNode = function(attributes){
38515 attributes = attributes || {};
38516 if(typeof attributes == "string"){
38517 attributes = {text: attributes};
38519 this.childrenRendered = false;
38520 this.rendered = false;
38521 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
38522 this.expanded = attributes.expanded === true;
38523 this.isTarget = attributes.isTarget !== false;
38524 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
38525 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
38528 * Read-only. The text for this node. To change it use setText().
38531 this.text = attributes.text;
38533 * True if this node is disabled.
38536 this.disabled = attributes.disabled === true;
38540 * @event textchange
38541 * Fires when the text for this node is changed
38542 * @param {Node} this This node
38543 * @param {String} text The new text
38544 * @param {String} oldText The old text
38546 "textchange" : true,
38548 * @event beforeexpand
38549 * Fires before this node is expanded, return false to cancel.
38550 * @param {Node} this This node
38551 * @param {Boolean} deep
38552 * @param {Boolean} anim
38554 "beforeexpand" : true,
38556 * @event beforecollapse
38557 * Fires before this node is collapsed, return false to cancel.
38558 * @param {Node} this This node
38559 * @param {Boolean} deep
38560 * @param {Boolean} anim
38562 "beforecollapse" : true,
38565 * Fires when this node is expanded
38566 * @param {Node} this This node
38570 * @event disabledchange
38571 * Fires when the disabled status of this node changes
38572 * @param {Node} this This node
38573 * @param {Boolean} disabled
38575 "disabledchange" : true,
38578 * Fires when this node is collapsed
38579 * @param {Node} this This node
38583 * @event beforeclick
38584 * Fires before click processing. Return false to cancel the default action.
38585 * @param {Node} this This node
38586 * @param {Roo.EventObject} e The event object
38588 "beforeclick":true,
38590 * @event checkchange
38591 * Fires when a node with a checkbox's checked property changes
38592 * @param {Node} this This node
38593 * @param {Boolean} checked
38595 "checkchange":true,
38598 * Fires when this node is clicked
38599 * @param {Node} this This node
38600 * @param {Roo.EventObject} e The event object
38605 * Fires when this node is double clicked
38606 * @param {Node} this This node
38607 * @param {Roo.EventObject} e The event object
38611 * @event contextmenu
38612 * Fires when this node is right clicked
38613 * @param {Node} this This node
38614 * @param {Roo.EventObject} e The event object
38616 "contextmenu":true,
38618 * @event beforechildrenrendered
38619 * Fires right before the child nodes for this node are rendered
38620 * @param {Node} this This node
38622 "beforechildrenrendered":true
38625 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
38628 * Read-only. The UI for this node
38631 this.ui = new uiClass(this);
38633 // finally support items[]
38634 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
38639 Roo.each(this.attributes.items, function(c) {
38640 this.appendChild(Roo.factory(c,Roo.Tree));
38642 delete this.attributes.items;
38647 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
38648 preventHScroll: true,
38650 * Returns true if this node is expanded
38651 * @return {Boolean}
38653 isExpanded : function(){
38654 return this.expanded;
38658 * Returns the UI object for this node
38659 * @return {TreeNodeUI}
38661 getUI : function(){
38665 // private override
38666 setFirstChild : function(node){
38667 var of = this.firstChild;
38668 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
38669 if(this.childrenRendered && of && node != of){
38670 of.renderIndent(true, true);
38673 this.renderIndent(true, true);
38677 // private override
38678 setLastChild : function(node){
38679 var ol = this.lastChild;
38680 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
38681 if(this.childrenRendered && ol && node != ol){
38682 ol.renderIndent(true, true);
38685 this.renderIndent(true, true);
38689 // these methods are overridden to provide lazy rendering support
38690 // private override
38691 appendChild : function()
38693 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
38694 if(node && this.childrenRendered){
38697 this.ui.updateExpandIcon();
38701 // private override
38702 removeChild : function(node){
38703 this.ownerTree.getSelectionModel().unselect(node);
38704 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
38705 // if it's been rendered remove dom node
38706 if(this.childrenRendered){
38709 if(this.childNodes.length < 1){
38710 this.collapse(false, false);
38712 this.ui.updateExpandIcon();
38714 if(!this.firstChild) {
38715 this.childrenRendered = false;
38720 // private override
38721 insertBefore : function(node, refNode){
38722 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
38723 if(newNode && refNode && this.childrenRendered){
38726 this.ui.updateExpandIcon();
38731 * Sets the text for this node
38732 * @param {String} text
38734 setText : function(text){
38735 var oldText = this.text;
38737 this.attributes.text = text;
38738 if(this.rendered){ // event without subscribing
38739 this.ui.onTextChange(this, text, oldText);
38741 this.fireEvent("textchange", this, text, oldText);
38745 * Triggers selection of this node
38747 select : function(){
38748 this.getOwnerTree().getSelectionModel().select(this);
38752 * Triggers deselection of this node
38754 unselect : function(){
38755 this.getOwnerTree().getSelectionModel().unselect(this);
38759 * Returns true if this node is selected
38760 * @return {Boolean}
38762 isSelected : function(){
38763 return this.getOwnerTree().getSelectionModel().isSelected(this);
38767 * Expand this node.
38768 * @param {Boolean} deep (optional) True to expand all children as well
38769 * @param {Boolean} anim (optional) false to cancel the default animation
38770 * @param {Function} callback (optional) A callback to be called when
38771 * expanding this node completes (does not wait for deep expand to complete).
38772 * Called with 1 parameter, this node.
38774 expand : function(deep, anim, callback){
38775 if(!this.expanded){
38776 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
38779 if(!this.childrenRendered){
38780 this.renderChildren();
38782 this.expanded = true;
38784 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
38785 this.ui.animExpand(function(){
38786 this.fireEvent("expand", this);
38787 if(typeof callback == "function"){
38791 this.expandChildNodes(true);
38793 }.createDelegate(this));
38797 this.fireEvent("expand", this);
38798 if(typeof callback == "function"){
38803 if(typeof callback == "function"){
38808 this.expandChildNodes(true);
38812 isHiddenRoot : function(){
38813 return this.isRoot && !this.getOwnerTree().rootVisible;
38817 * Collapse this node.
38818 * @param {Boolean} deep (optional) True to collapse all children as well
38819 * @param {Boolean} anim (optional) false to cancel the default animation
38821 collapse : function(deep, anim){
38822 if(this.expanded && !this.isHiddenRoot()){
38823 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
38826 this.expanded = false;
38827 if((this.getOwnerTree().animate && anim !== false) || anim){
38828 this.ui.animCollapse(function(){
38829 this.fireEvent("collapse", this);
38831 this.collapseChildNodes(true);
38833 }.createDelegate(this));
38836 this.ui.collapse();
38837 this.fireEvent("collapse", this);
38841 var cs = this.childNodes;
38842 for(var i = 0, len = cs.length; i < len; i++) {
38843 cs[i].collapse(true, false);
38849 delayedExpand : function(delay){
38850 if(!this.expandProcId){
38851 this.expandProcId = this.expand.defer(delay, this);
38856 cancelExpand : function(){
38857 if(this.expandProcId){
38858 clearTimeout(this.expandProcId);
38860 this.expandProcId = false;
38864 * Toggles expanded/collapsed state of the node
38866 toggle : function(){
38875 * Ensures all parent nodes are expanded
38877 ensureVisible : function(callback){
38878 var tree = this.getOwnerTree();
38879 tree.expandPath(this.parentNode.getPath(), false, function(){
38880 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
38881 Roo.callback(callback);
38882 }.createDelegate(this));
38886 * Expand all child nodes
38887 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
38889 expandChildNodes : function(deep){
38890 var cs = this.childNodes;
38891 for(var i = 0, len = cs.length; i < len; i++) {
38892 cs[i].expand(deep);
38897 * Collapse all child nodes
38898 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
38900 collapseChildNodes : function(deep){
38901 var cs = this.childNodes;
38902 for(var i = 0, len = cs.length; i < len; i++) {
38903 cs[i].collapse(deep);
38908 * Disables this node
38910 disable : function(){
38911 this.disabled = true;
38913 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
38914 this.ui.onDisableChange(this, true);
38916 this.fireEvent("disabledchange", this, true);
38920 * Enables this node
38922 enable : function(){
38923 this.disabled = false;
38924 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
38925 this.ui.onDisableChange(this, false);
38927 this.fireEvent("disabledchange", this, false);
38931 renderChildren : function(suppressEvent){
38932 if(suppressEvent !== false){
38933 this.fireEvent("beforechildrenrendered", this);
38935 var cs = this.childNodes;
38936 for(var i = 0, len = cs.length; i < len; i++){
38937 cs[i].render(true);
38939 this.childrenRendered = true;
38943 sort : function(fn, scope){
38944 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
38945 if(this.childrenRendered){
38946 var cs = this.childNodes;
38947 for(var i = 0, len = cs.length; i < len; i++){
38948 cs[i].render(true);
38954 render : function(bulkRender){
38955 this.ui.render(bulkRender);
38956 if(!this.rendered){
38957 this.rendered = true;
38959 this.expanded = false;
38960 this.expand(false, false);
38966 renderIndent : function(deep, refresh){
38968 this.ui.childIndent = null;
38970 this.ui.renderIndent();
38971 if(deep === true && this.childrenRendered){
38972 var cs = this.childNodes;
38973 for(var i = 0, len = cs.length; i < len; i++){
38974 cs[i].renderIndent(true, refresh);
38980 * Ext JS Library 1.1.1
38981 * Copyright(c) 2006-2007, Ext JS, LLC.
38983 * Originally Released Under LGPL - original licence link has changed is not relivant.
38986 * <script type="text/javascript">
38990 * @class Roo.tree.AsyncTreeNode
38991 * @extends Roo.tree.TreeNode
38992 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
38994 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
38996 Roo.tree.AsyncTreeNode = function(config){
38997 this.loaded = false;
38998 this.loading = false;
38999 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
39001 * @event beforeload
39002 * Fires before this node is loaded, return false to cancel
39003 * @param {Node} this This node
39005 this.addEvents({'beforeload':true, 'load': true});
39008 * Fires when this node is loaded
39009 * @param {Node} this This node
39012 * The loader used by this node (defaults to using the tree's defined loader)
39017 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
39018 expand : function(deep, anim, callback){
39019 if(this.loading){ // if an async load is already running, waiting til it's done
39021 var f = function(){
39022 if(!this.loading){ // done loading
39023 clearInterval(timer);
39024 this.expand(deep, anim, callback);
39026 }.createDelegate(this);
39027 timer = setInterval(f, 200);
39031 if(this.fireEvent("beforeload", this) === false){
39034 this.loading = true;
39035 this.ui.beforeLoad(this);
39036 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
39038 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
39042 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
39046 * Returns true if this node is currently loading
39047 * @return {Boolean}
39049 isLoading : function(){
39050 return this.loading;
39053 loadComplete : function(deep, anim, callback){
39054 this.loading = false;
39055 this.loaded = true;
39056 this.ui.afterLoad(this);
39057 this.fireEvent("load", this);
39058 this.expand(deep, anim, callback);
39062 * Returns true if this node has been loaded
39063 * @return {Boolean}
39065 isLoaded : function(){
39066 return this.loaded;
39069 hasChildNodes : function(){
39070 if(!this.isLeaf() && !this.loaded){
39073 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
39078 * Trigger a reload for this node
39079 * @param {Function} callback
39081 reload : function(callback){
39082 this.collapse(false, false);
39083 while(this.firstChild){
39084 this.removeChild(this.firstChild);
39086 this.childrenRendered = false;
39087 this.loaded = false;
39088 if(this.isHiddenRoot()){
39089 this.expanded = false;
39091 this.expand(false, false, callback);
39095 * Ext JS Library 1.1.1
39096 * Copyright(c) 2006-2007, Ext JS, LLC.
39098 * Originally Released Under LGPL - original licence link has changed is not relivant.
39101 * <script type="text/javascript">
39105 * @class Roo.tree.TreeNodeUI
39107 * @param {Object} node The node to render
39108 * The TreeNode UI implementation is separate from the
39109 * tree implementation. Unless you are customizing the tree UI,
39110 * you should never have to use this directly.
39112 Roo.tree.TreeNodeUI = function(node){
39114 this.rendered = false;
39115 this.animating = false;
39116 this.emptyIcon = Roo.BLANK_IMAGE_URL;
39119 Roo.tree.TreeNodeUI.prototype = {
39120 removeChild : function(node){
39122 this.ctNode.removeChild(node.ui.getEl());
39126 beforeLoad : function(){
39127 this.addClass("x-tree-node-loading");
39130 afterLoad : function(){
39131 this.removeClass("x-tree-node-loading");
39134 onTextChange : function(node, text, oldText){
39136 this.textNode.innerHTML = text;
39140 onDisableChange : function(node, state){
39141 this.disabled = state;
39143 this.addClass("x-tree-node-disabled");
39145 this.removeClass("x-tree-node-disabled");
39149 onSelectedChange : function(state){
39152 this.addClass("x-tree-selected");
39155 this.removeClass("x-tree-selected");
39159 onMove : function(tree, node, oldParent, newParent, index, refNode){
39160 this.childIndent = null;
39162 var targetNode = newParent.ui.getContainer();
39163 if(!targetNode){//target not rendered
39164 this.holder = document.createElement("div");
39165 this.holder.appendChild(this.wrap);
39168 var insertBefore = refNode ? refNode.ui.getEl() : null;
39170 targetNode.insertBefore(this.wrap, insertBefore);
39172 targetNode.appendChild(this.wrap);
39174 this.node.renderIndent(true);
39178 addClass : function(cls){
39180 Roo.fly(this.elNode).addClass(cls);
39184 removeClass : function(cls){
39186 Roo.fly(this.elNode).removeClass(cls);
39190 remove : function(){
39192 this.holder = document.createElement("div");
39193 this.holder.appendChild(this.wrap);
39197 fireEvent : function(){
39198 return this.node.fireEvent.apply(this.node, arguments);
39201 initEvents : function(){
39202 this.node.on("move", this.onMove, this);
39203 var E = Roo.EventManager;
39204 var a = this.anchor;
39206 var el = Roo.fly(a, '_treeui');
39208 if(Roo.isOpera){ // opera render bug ignores the CSS
39209 el.setStyle("text-decoration", "none");
39212 el.on("click", this.onClick, this);
39213 el.on("dblclick", this.onDblClick, this);
39216 Roo.EventManager.on(this.checkbox,
39217 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
39220 el.on("contextmenu", this.onContextMenu, this);
39222 var icon = Roo.fly(this.iconNode);
39223 icon.on("click", this.onClick, this);
39224 icon.on("dblclick", this.onDblClick, this);
39225 icon.on("contextmenu", this.onContextMenu, this);
39226 E.on(this.ecNode, "click", this.ecClick, this, true);
39228 if(this.node.disabled){
39229 this.addClass("x-tree-node-disabled");
39231 if(this.node.hidden){
39232 this.addClass("x-tree-node-disabled");
39234 var ot = this.node.getOwnerTree();
39235 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
39236 if(dd && (!this.node.isRoot || ot.rootVisible)){
39237 Roo.dd.Registry.register(this.elNode, {
39239 handles: this.getDDHandles(),
39245 getDDHandles : function(){
39246 return [this.iconNode, this.textNode];
39251 this.wrap.style.display = "none";
39257 this.wrap.style.display = "";
39261 onContextMenu : function(e){
39262 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
39263 e.preventDefault();
39265 this.fireEvent("contextmenu", this.node, e);
39269 onClick : function(e){
39274 if(this.fireEvent("beforeclick", this.node, e) !== false){
39275 if(!this.disabled && this.node.attributes.href){
39276 this.fireEvent("click", this.node, e);
39279 e.preventDefault();
39284 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
39285 this.node.toggle();
39288 this.fireEvent("click", this.node, e);
39294 onDblClick : function(e){
39295 e.preventDefault();
39300 this.toggleCheck();
39302 if(!this.animating && this.node.hasChildNodes()){
39303 this.node.toggle();
39305 this.fireEvent("dblclick", this.node, e);
39308 onCheckChange : function(){
39309 var checked = this.checkbox.checked;
39310 this.node.attributes.checked = checked;
39311 this.fireEvent('checkchange', this.node, checked);
39314 ecClick : function(e){
39315 if(!this.animating && this.node.hasChildNodes()){
39316 this.node.toggle();
39320 startDrop : function(){
39321 this.dropping = true;
39324 // delayed drop so the click event doesn't get fired on a drop
39325 endDrop : function(){
39326 setTimeout(function(){
39327 this.dropping = false;
39328 }.createDelegate(this), 50);
39331 expand : function(){
39332 this.updateExpandIcon();
39333 this.ctNode.style.display = "";
39336 focus : function(){
39337 if(!this.node.preventHScroll){
39338 try{this.anchor.focus();
39340 }else if(!Roo.isIE){
39342 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
39343 var l = noscroll.scrollLeft;
39344 this.anchor.focus();
39345 noscroll.scrollLeft = l;
39350 toggleCheck : function(value){
39351 var cb = this.checkbox;
39353 cb.checked = (value === undefined ? !cb.checked : value);
39359 this.anchor.blur();
39363 animExpand : function(callback){
39364 var ct = Roo.get(this.ctNode);
39366 if(!this.node.hasChildNodes()){
39367 this.updateExpandIcon();
39368 this.ctNode.style.display = "";
39369 Roo.callback(callback);
39372 this.animating = true;
39373 this.updateExpandIcon();
39376 callback : function(){
39377 this.animating = false;
39378 Roo.callback(callback);
39381 duration: this.node.ownerTree.duration || .25
39385 highlight : function(){
39386 var tree = this.node.getOwnerTree();
39387 Roo.fly(this.wrap).highlight(
39388 tree.hlColor || "C3DAF9",
39389 {endColor: tree.hlBaseColor}
39393 collapse : function(){
39394 this.updateExpandIcon();
39395 this.ctNode.style.display = "none";
39398 animCollapse : function(callback){
39399 var ct = Roo.get(this.ctNode);
39400 ct.enableDisplayMode('block');
39403 this.animating = true;
39404 this.updateExpandIcon();
39407 callback : function(){
39408 this.animating = false;
39409 Roo.callback(callback);
39412 duration: this.node.ownerTree.duration || .25
39416 getContainer : function(){
39417 return this.ctNode;
39420 getEl : function(){
39424 appendDDGhost : function(ghostNode){
39425 ghostNode.appendChild(this.elNode.cloneNode(true));
39428 getDDRepairXY : function(){
39429 return Roo.lib.Dom.getXY(this.iconNode);
39432 onRender : function(){
39436 render : function(bulkRender){
39437 var n = this.node, a = n.attributes;
39438 var targetNode = n.parentNode ?
39439 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
39441 if(!this.rendered){
39442 this.rendered = true;
39444 this.renderElements(n, a, targetNode, bulkRender);
39447 if(this.textNode.setAttributeNS){
39448 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
39450 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
39453 this.textNode.setAttribute("ext:qtip", a.qtip);
39455 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
39458 }else if(a.qtipCfg){
39459 a.qtipCfg.target = Roo.id(this.textNode);
39460 Roo.QuickTips.register(a.qtipCfg);
39463 if(!this.node.expanded){
39464 this.updateExpandIcon();
39467 if(bulkRender === true) {
39468 targetNode.appendChild(this.wrap);
39473 renderElements : function(n, a, targetNode, bulkRender)
39475 // add some indent caching, this helps performance when rendering a large tree
39476 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
39477 var t = n.getOwnerTree();
39478 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
39479 if (typeof(n.attributes.html) != 'undefined') {
39480 txt = n.attributes.html;
39482 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
39483 var cb = typeof a.checked == 'boolean';
39484 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
39485 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
39486 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
39487 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
39488 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
39489 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
39490 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
39491 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
39492 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
39493 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
39496 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
39497 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
39498 n.nextSibling.ui.getEl(), buf.join(""));
39500 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
39503 this.elNode = this.wrap.childNodes[0];
39504 this.ctNode = this.wrap.childNodes[1];
39505 var cs = this.elNode.childNodes;
39506 this.indentNode = cs[0];
39507 this.ecNode = cs[1];
39508 this.iconNode = cs[2];
39511 this.checkbox = cs[3];
39514 this.anchor = cs[index];
39515 this.textNode = cs[index].firstChild;
39518 getAnchor : function(){
39519 return this.anchor;
39522 getTextEl : function(){
39523 return this.textNode;
39526 getIconEl : function(){
39527 return this.iconNode;
39530 isChecked : function(){
39531 return this.checkbox ? this.checkbox.checked : false;
39534 updateExpandIcon : function(){
39536 var n = this.node, c1, c2;
39537 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
39538 var hasChild = n.hasChildNodes();
39542 c1 = "x-tree-node-collapsed";
39543 c2 = "x-tree-node-expanded";
39546 c1 = "x-tree-node-expanded";
39547 c2 = "x-tree-node-collapsed";
39550 this.removeClass("x-tree-node-leaf");
39551 this.wasLeaf = false;
39553 if(this.c1 != c1 || this.c2 != c2){
39554 Roo.fly(this.elNode).replaceClass(c1, c2);
39555 this.c1 = c1; this.c2 = c2;
39558 // this changes non-leafs into leafs if they have no children.
39559 // it's not very rational behaviour..
39561 if(!this.wasLeaf && this.node.leaf){
39562 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
39565 this.wasLeaf = true;
39568 var ecc = "x-tree-ec-icon "+cls;
39569 if(this.ecc != ecc){
39570 this.ecNode.className = ecc;
39576 getChildIndent : function(){
39577 if(!this.childIndent){
39581 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
39583 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
39585 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
39590 this.childIndent = buf.join("");
39592 return this.childIndent;
39595 renderIndent : function(){
39598 var p = this.node.parentNode;
39600 indent = p.ui.getChildIndent();
39602 if(this.indentMarkup != indent){ // don't rerender if not required
39603 this.indentNode.innerHTML = indent;
39604 this.indentMarkup = indent;
39606 this.updateExpandIcon();
39611 Roo.tree.RootTreeNodeUI = function(){
39612 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
39614 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
39615 render : function(){
39616 if(!this.rendered){
39617 var targetNode = this.node.ownerTree.innerCt.dom;
39618 this.node.expanded = true;
39619 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
39620 this.wrap = this.ctNode = targetNode.firstChild;
39623 collapse : function(){
39625 expand : function(){
39629 * Ext JS Library 1.1.1
39630 * Copyright(c) 2006-2007, Ext JS, LLC.
39632 * Originally Released Under LGPL - original licence link has changed is not relivant.
39635 * <script type="text/javascript">
39638 * @class Roo.tree.TreeLoader
39639 * @extends Roo.util.Observable
39640 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
39641 * nodes from a specified URL. The response must be a javascript Array definition
39642 * who's elements are node definition objects. eg:
39647 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
39648 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
39655 * The old style respose with just an array is still supported, but not recommended.
39658 * A server request is sent, and child nodes are loaded only when a node is expanded.
39659 * The loading node's id is passed to the server under the parameter name "node" to
39660 * enable the server to produce the correct child nodes.
39662 * To pass extra parameters, an event handler may be attached to the "beforeload"
39663 * event, and the parameters specified in the TreeLoader's baseParams property:
39665 myTreeLoader.on("beforeload", function(treeLoader, node) {
39666 this.baseParams.category = node.attributes.category;
39671 * This would pass an HTTP parameter called "category" to the server containing
39672 * the value of the Node's "category" attribute.
39674 * Creates a new Treeloader.
39675 * @param {Object} config A config object containing config properties.
39677 Roo.tree.TreeLoader = function(config){
39678 this.baseParams = {};
39679 this.requestMethod = "POST";
39680 Roo.apply(this, config);
39685 * @event beforeload
39686 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
39687 * @param {Object} This TreeLoader object.
39688 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39689 * @param {Object} callback The callback function specified in the {@link #load} call.
39694 * Fires when the node has been successfuly loaded.
39695 * @param {Object} This TreeLoader object.
39696 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39697 * @param {Object} response The response object containing the data from the server.
39701 * @event loadexception
39702 * Fires if the network request failed.
39703 * @param {Object} This TreeLoader object.
39704 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
39705 * @param {Object} response The response object containing the data from the server.
39707 loadexception : true,
39710 * Fires before a node is created, enabling you to return custom Node types
39711 * @param {Object} This TreeLoader object.
39712 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
39717 Roo.tree.TreeLoader.superclass.constructor.call(this);
39720 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
39722 * @cfg {String} dataUrl The URL from which to request a Json string which
39723 * specifies an array of node definition object representing the child nodes
39727 * @cfg {String} requestMethod either GET or POST
39728 * defaults to POST (due to BC)
39732 * @cfg {Object} baseParams (optional) An object containing properties which
39733 * specify HTTP parameters to be passed to each request for child nodes.
39736 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
39737 * created by this loader. If the attributes sent by the server have an attribute in this object,
39738 * they take priority.
39741 * @cfg {Object} uiProviders (optional) An object containing properties which
39743 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
39744 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
39745 * <i>uiProvider</i> attribute of a returned child node is a string rather
39746 * than a reference to a TreeNodeUI implementation, this that string value
39747 * is used as a property name in the uiProviders object. You can define the provider named
39748 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
39753 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
39754 * child nodes before loading.
39756 clearOnLoad : true,
39759 * @cfg {String} root (optional) Default to false. Use this to read data from an object
39760 * property on loading, rather than expecting an array. (eg. more compatible to a standard
39761 * Grid query { data : [ .....] }
39766 * @cfg {String} queryParam (optional)
39767 * Name of the query as it will be passed on the querystring (defaults to 'node')
39768 * eg. the request will be ?node=[id]
39775 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
39776 * This is called automatically when a node is expanded, but may be used to reload
39777 * a node (or append new children if the {@link #clearOnLoad} option is false.)
39778 * @param {Roo.tree.TreeNode} node
39779 * @param {Function} callback
39781 load : function(node, callback){
39782 if(this.clearOnLoad){
39783 while(node.firstChild){
39784 node.removeChild(node.firstChild);
39787 if(node.attributes.children){ // preloaded json children
39788 var cs = node.attributes.children;
39789 for(var i = 0, len = cs.length; i < len; i++){
39790 node.appendChild(this.createNode(cs[i]));
39792 if(typeof callback == "function"){
39795 }else if(this.dataUrl){
39796 this.requestData(node, callback);
39800 getParams: function(node){
39801 var buf = [], bp = this.baseParams;
39802 for(var key in bp){
39803 if(typeof bp[key] != "function"){
39804 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
39807 var n = this.queryParam === false ? 'node' : this.queryParam;
39808 buf.push(n + "=", encodeURIComponent(node.id));
39809 return buf.join("");
39812 requestData : function(node, callback){
39813 if(this.fireEvent("beforeload", this, node, callback) !== false){
39814 this.transId = Roo.Ajax.request({
39815 method:this.requestMethod,
39816 url: this.dataUrl||this.url,
39817 success: this.handleResponse,
39818 failure: this.handleFailure,
39820 argument: {callback: callback, node: node},
39821 params: this.getParams(node)
39824 // if the load is cancelled, make sure we notify
39825 // the node that we are done
39826 if(typeof callback == "function"){
39832 isLoading : function(){
39833 return this.transId ? true : false;
39836 abort : function(){
39837 if(this.isLoading()){
39838 Roo.Ajax.abort(this.transId);
39843 createNode : function(attr)
39845 // apply baseAttrs, nice idea Corey!
39846 if(this.baseAttrs){
39847 Roo.applyIf(attr, this.baseAttrs);
39849 if(this.applyLoader !== false){
39850 attr.loader = this;
39852 // uiProvider = depreciated..
39854 if(typeof(attr.uiProvider) == 'string'){
39855 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
39856 /** eval:var:attr */ eval(attr.uiProvider);
39858 if(typeof(this.uiProviders['default']) != 'undefined') {
39859 attr.uiProvider = this.uiProviders['default'];
39862 this.fireEvent('create', this, attr);
39864 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
39866 new Roo.tree.TreeNode(attr) :
39867 new Roo.tree.AsyncTreeNode(attr));
39870 processResponse : function(response, node, callback)
39872 var json = response.responseText;
39875 var o = Roo.decode(json);
39877 if (this.root === false && typeof(o.success) != undefined) {
39878 this.root = 'data'; // the default behaviour for list like data..
39881 if (this.root !== false && !o.success) {
39882 // it's a failure condition.
39883 var a = response.argument;
39884 this.fireEvent("loadexception", this, a.node, response);
39885 Roo.log("Load failed - should have a handler really");
39891 if (this.root !== false) {
39895 for(var i = 0, len = o.length; i < len; i++){
39896 var n = this.createNode(o[i]);
39898 node.appendChild(n);
39901 if(typeof callback == "function"){
39902 callback(this, node);
39905 this.handleFailure(response);
39909 handleResponse : function(response){
39910 this.transId = false;
39911 var a = response.argument;
39912 this.processResponse(response, a.node, a.callback);
39913 this.fireEvent("load", this, a.node, response);
39916 handleFailure : function(response)
39918 // should handle failure better..
39919 this.transId = false;
39920 var a = response.argument;
39921 this.fireEvent("loadexception", this, a.node, response);
39922 if(typeof a.callback == "function"){
39923 a.callback(this, a.node);
39928 * Ext JS Library 1.1.1
39929 * Copyright(c) 2006-2007, Ext JS, LLC.
39931 * Originally Released Under LGPL - original licence link has changed is not relivant.
39934 * <script type="text/javascript">
39938 * @class Roo.tree.TreeFilter
39939 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
39940 * @param {TreePanel} tree
39941 * @param {Object} config (optional)
39943 Roo.tree.TreeFilter = function(tree, config){
39945 this.filtered = {};
39946 Roo.apply(this, config);
39949 Roo.tree.TreeFilter.prototype = {
39956 * Filter the data by a specific attribute.
39957 * @param {String/RegExp} value Either string that the attribute value
39958 * should start with or a RegExp to test against the attribute
39959 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
39960 * @param {TreeNode} startNode (optional) The node to start the filter at.
39962 filter : function(value, attr, startNode){
39963 attr = attr || "text";
39965 if(typeof value == "string"){
39966 var vlen = value.length;
39967 // auto clear empty filter
39968 if(vlen == 0 && this.clearBlank){
39972 value = value.toLowerCase();
39974 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
39976 }else if(value.exec){ // regex?
39978 return value.test(n.attributes[attr]);
39981 throw 'Illegal filter type, must be string or regex';
39983 this.filterBy(f, null, startNode);
39987 * Filter by a function. The passed function will be called with each
39988 * node in the tree (or from the startNode). If the function returns true, the node is kept
39989 * otherwise it is filtered. If a node is filtered, its children are also filtered.
39990 * @param {Function} fn The filter function
39991 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
39993 filterBy : function(fn, scope, startNode){
39994 startNode = startNode || this.tree.root;
39995 if(this.autoClear){
39998 var af = this.filtered, rv = this.reverse;
39999 var f = function(n){
40000 if(n == startNode){
40006 var m = fn.call(scope || n, n);
40014 startNode.cascade(f);
40017 if(typeof id != "function"){
40019 if(n && n.parentNode){
40020 n.parentNode.removeChild(n);
40028 * Clears the current filter. Note: with the "remove" option
40029 * set a filter cannot be cleared.
40031 clear : function(){
40033 var af = this.filtered;
40035 if(typeof id != "function"){
40042 this.filtered = {};
40047 * Ext JS Library 1.1.1
40048 * Copyright(c) 2006-2007, Ext JS, LLC.
40050 * Originally Released Under LGPL - original licence link has changed is not relivant.
40053 * <script type="text/javascript">
40058 * @class Roo.tree.TreeSorter
40059 * Provides sorting of nodes in a TreePanel
40061 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
40062 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
40063 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
40064 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
40065 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
40066 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
40068 * @param {TreePanel} tree
40069 * @param {Object} config
40071 Roo.tree.TreeSorter = function(tree, config){
40072 Roo.apply(this, config);
40073 tree.on("beforechildrenrendered", this.doSort, this);
40074 tree.on("append", this.updateSort, this);
40075 tree.on("insert", this.updateSort, this);
40077 var dsc = this.dir && this.dir.toLowerCase() == "desc";
40078 var p = this.property || "text";
40079 var sortType = this.sortType;
40080 var fs = this.folderSort;
40081 var cs = this.caseSensitive === true;
40082 var leafAttr = this.leafAttr || 'leaf';
40084 this.sortFn = function(n1, n2){
40086 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
40089 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
40093 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
40094 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
40096 return dsc ? +1 : -1;
40098 return dsc ? -1 : +1;
40105 Roo.tree.TreeSorter.prototype = {
40106 doSort : function(node){
40107 node.sort(this.sortFn);
40110 compareNodes : function(n1, n2){
40111 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
40114 updateSort : function(tree, node){
40115 if(node.childrenRendered){
40116 this.doSort.defer(1, this, [node]);
40121 * Ext JS Library 1.1.1
40122 * Copyright(c) 2006-2007, Ext JS, LLC.
40124 * Originally Released Under LGPL - original licence link has changed is not relivant.
40127 * <script type="text/javascript">
40130 if(Roo.dd.DropZone){
40132 Roo.tree.TreeDropZone = function(tree, config){
40133 this.allowParentInsert = false;
40134 this.allowContainerDrop = false;
40135 this.appendOnly = false;
40136 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
40138 this.lastInsertClass = "x-tree-no-status";
40139 this.dragOverData = {};
40142 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
40143 ddGroup : "TreeDD",
40146 expandDelay : 1000,
40148 expandNode : function(node){
40149 if(node.hasChildNodes() && !node.isExpanded()){
40150 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
40154 queueExpand : function(node){
40155 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
40158 cancelExpand : function(){
40159 if(this.expandProcId){
40160 clearTimeout(this.expandProcId);
40161 this.expandProcId = false;
40165 isValidDropPoint : function(n, pt, dd, e, data){
40166 if(!n || !data){ return false; }
40167 var targetNode = n.node;
40168 var dropNode = data.node;
40169 // default drop rules
40170 if(!(targetNode && targetNode.isTarget && pt)){
40173 if(pt == "append" && targetNode.allowChildren === false){
40176 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
40179 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
40182 // reuse the object
40183 var overEvent = this.dragOverData;
40184 overEvent.tree = this.tree;
40185 overEvent.target = targetNode;
40186 overEvent.data = data;
40187 overEvent.point = pt;
40188 overEvent.source = dd;
40189 overEvent.rawEvent = e;
40190 overEvent.dropNode = dropNode;
40191 overEvent.cancel = false;
40192 var result = this.tree.fireEvent("nodedragover", overEvent);
40193 return overEvent.cancel === false && result !== false;
40196 getDropPoint : function(e, n, dd)
40200 return tn.allowChildren !== false ? "append" : false; // always append for root
40202 var dragEl = n.ddel;
40203 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
40204 var y = Roo.lib.Event.getPageY(e);
40205 //var noAppend = tn.allowChildren === false || tn.isLeaf();
40207 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
40208 var noAppend = tn.allowChildren === false;
40209 if(this.appendOnly || tn.parentNode.allowChildren === false){
40210 return noAppend ? false : "append";
40212 var noBelow = false;
40213 if(!this.allowParentInsert){
40214 noBelow = tn.hasChildNodes() && tn.isExpanded();
40216 var q = (b - t) / (noAppend ? 2 : 3);
40217 if(y >= t && y < (t + q)){
40219 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
40226 onNodeEnter : function(n, dd, e, data)
40228 this.cancelExpand();
40231 onNodeOver : function(n, dd, e, data)
40234 var pt = this.getDropPoint(e, n, dd);
40237 // auto node expand check
40238 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
40239 this.queueExpand(node);
40240 }else if(pt != "append"){
40241 this.cancelExpand();
40244 // set the insert point style on the target node
40245 var returnCls = this.dropNotAllowed;
40246 if(this.isValidDropPoint(n, pt, dd, e, data)){
40251 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
40252 cls = "x-tree-drag-insert-above";
40253 }else if(pt == "below"){
40254 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
40255 cls = "x-tree-drag-insert-below";
40257 returnCls = "x-tree-drop-ok-append";
40258 cls = "x-tree-drag-append";
40260 if(this.lastInsertClass != cls){
40261 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
40262 this.lastInsertClass = cls;
40269 onNodeOut : function(n, dd, e, data){
40271 this.cancelExpand();
40272 this.removeDropIndicators(n);
40275 onNodeDrop : function(n, dd, e, data){
40276 var point = this.getDropPoint(e, n, dd);
40277 var targetNode = n.node;
40278 targetNode.ui.startDrop();
40279 if(!this.isValidDropPoint(n, point, dd, e, data)){
40280 targetNode.ui.endDrop();
40283 // first try to find the drop node
40284 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
40287 target: targetNode,
40292 dropNode: dropNode,
40295 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
40296 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
40297 targetNode.ui.endDrop();
40300 // allow target changing
40301 targetNode = dropEvent.target;
40302 if(point == "append" && !targetNode.isExpanded()){
40303 targetNode.expand(false, null, function(){
40304 this.completeDrop(dropEvent);
40305 }.createDelegate(this));
40307 this.completeDrop(dropEvent);
40312 completeDrop : function(de){
40313 var ns = de.dropNode, p = de.point, t = de.target;
40314 if(!(ns instanceof Array)){
40318 for(var i = 0, len = ns.length; i < len; i++){
40321 t.parentNode.insertBefore(n, t);
40322 }else if(p == "below"){
40323 t.parentNode.insertBefore(n, t.nextSibling);
40329 if(this.tree.hlDrop){
40333 this.tree.fireEvent("nodedrop", de);
40336 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
40337 if(this.tree.hlDrop){
40338 dropNode.ui.focus();
40339 dropNode.ui.highlight();
40341 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
40344 getTree : function(){
40348 removeDropIndicators : function(n){
40351 Roo.fly(el).removeClass([
40352 "x-tree-drag-insert-above",
40353 "x-tree-drag-insert-below",
40354 "x-tree-drag-append"]);
40355 this.lastInsertClass = "_noclass";
40359 beforeDragDrop : function(target, e, id){
40360 this.cancelExpand();
40364 afterRepair : function(data){
40365 if(data && Roo.enableFx){
40366 data.node.ui.highlight();
40376 * Ext JS Library 1.1.1
40377 * Copyright(c) 2006-2007, Ext JS, LLC.
40379 * Originally Released Under LGPL - original licence link has changed is not relivant.
40382 * <script type="text/javascript">
40386 if(Roo.dd.DragZone){
40387 Roo.tree.TreeDragZone = function(tree, config){
40388 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
40392 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
40393 ddGroup : "TreeDD",
40395 onBeforeDrag : function(data, e){
40397 return n && n.draggable && !n.disabled;
40401 onInitDrag : function(e){
40402 var data = this.dragData;
40403 this.tree.getSelectionModel().select(data.node);
40404 this.proxy.update("");
40405 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
40406 this.tree.fireEvent("startdrag", this.tree, data.node, e);
40409 getRepairXY : function(e, data){
40410 return data.node.ui.getDDRepairXY();
40413 onEndDrag : function(data, e){
40414 this.tree.fireEvent("enddrag", this.tree, data.node, e);
40419 onValidDrop : function(dd, e, id){
40420 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
40424 beforeInvalidDrop : function(e, id){
40425 // this scrolls the original position back into view
40426 var sm = this.tree.getSelectionModel();
40427 sm.clearSelections();
40428 sm.select(this.dragData.node);
40433 * Ext JS Library 1.1.1
40434 * Copyright(c) 2006-2007, Ext JS, LLC.
40436 * Originally Released Under LGPL - original licence link has changed is not relivant.
40439 * <script type="text/javascript">
40442 * @class Roo.tree.TreeEditor
40443 * @extends Roo.Editor
40444 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
40445 * as the editor field.
40447 * @param {Object} config (used to be the tree panel.)
40448 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
40450 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
40451 * @cfg {Roo.form.TextField} field [required] The field configuration
40455 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
40458 if (oldconfig) { // old style..
40459 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
40462 tree = config.tree;
40463 config.field = config.field || {};
40464 config.field.xtype = 'TextField';
40465 field = Roo.factory(config.field, Roo.form);
40467 config = config || {};
40472 * @event beforenodeedit
40473 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
40474 * false from the handler of this event.
40475 * @param {Editor} this
40476 * @param {Roo.tree.Node} node
40478 "beforenodeedit" : true
40482 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
40486 tree.on('beforeclick', this.beforeNodeClick, this);
40487 tree.getTreeEl().on('mousedown', this.hide, this);
40488 this.on('complete', this.updateNode, this);
40489 this.on('beforestartedit', this.fitToTree, this);
40490 this.on('startedit', this.bindScroll, this, {delay:10});
40491 this.on('specialkey', this.onSpecialKey, this);
40494 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
40496 * @cfg {String} alignment
40497 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
40503 * @cfg {Boolean} hideEl
40504 * True to hide the bound element while the editor is displayed (defaults to false)
40508 * @cfg {String} cls
40509 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
40511 cls: "x-small-editor x-tree-editor",
40513 * @cfg {Boolean} shim
40514 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
40520 * @cfg {Number} maxWidth
40521 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
40522 * the containing tree element's size, it will be automatically limited for you to the container width, taking
40523 * scroll and client offsets into account prior to each edit.
40530 fitToTree : function(ed, el){
40531 var td = this.tree.getTreeEl().dom, nd = el.dom;
40532 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
40533 td.scrollLeft = nd.offsetLeft;
40537 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
40538 this.setSize(w, '');
40540 return this.fireEvent('beforenodeedit', this, this.editNode);
40545 triggerEdit : function(node){
40546 this.completeEdit();
40547 this.editNode = node;
40548 this.startEdit(node.ui.textNode, node.text);
40552 bindScroll : function(){
40553 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
40557 beforeNodeClick : function(node, e){
40558 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
40559 this.lastClick = new Date();
40560 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
40562 this.triggerEdit(node);
40569 updateNode : function(ed, value){
40570 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
40571 this.editNode.setText(value);
40575 onHide : function(){
40576 Roo.tree.TreeEditor.superclass.onHide.call(this);
40578 this.editNode.ui.focus();
40583 onSpecialKey : function(field, e){
40584 var k = e.getKey();
40588 }else if(k == e.ENTER && !e.hasModifier()){
40590 this.completeEdit();
40593 });//<Script type="text/javascript">
40596 * Ext JS Library 1.1.1
40597 * Copyright(c) 2006-2007, Ext JS, LLC.
40599 * Originally Released Under LGPL - original licence link has changed is not relivant.
40602 * <script type="text/javascript">
40606 * Not documented??? - probably should be...
40609 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
40610 //focus: Roo.emptyFn, // prevent odd scrolling behavior
40612 renderElements : function(n, a, targetNode, bulkRender){
40613 //consel.log("renderElements?");
40614 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
40616 var t = n.getOwnerTree();
40617 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
40619 var cols = t.columns;
40620 var bw = t.borderWidth;
40622 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
40623 var cb = typeof a.checked == "boolean";
40624 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
40625 var colcls = 'x-t-' + tid + '-c0';
40627 '<li class="x-tree-node">',
40630 '<div class="x-tree-node-el ', a.cls,'">',
40632 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
40635 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
40636 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
40637 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
40638 (a.icon ? ' x-tree-node-inline-icon' : ''),
40639 (a.iconCls ? ' '+a.iconCls : ''),
40640 '" unselectable="on" />',
40641 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
40642 (a.checked ? 'checked="checked" />' : ' />')) : ''),
40644 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
40645 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
40646 '<span unselectable="on" qtip="' + tx + '">',
40650 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
40651 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
40653 for(var i = 1, len = cols.length; i < len; i++){
40655 colcls = 'x-t-' + tid + '-c' +i;
40656 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
40657 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
40658 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
40664 '<div class="x-clear"></div></div>',
40665 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
40668 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
40669 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
40670 n.nextSibling.ui.getEl(), buf.join(""));
40672 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
40674 var el = this.wrap.firstChild;
40676 this.elNode = el.firstChild;
40677 this.ranchor = el.childNodes[1];
40678 this.ctNode = this.wrap.childNodes[1];
40679 var cs = el.firstChild.childNodes;
40680 this.indentNode = cs[0];
40681 this.ecNode = cs[1];
40682 this.iconNode = cs[2];
40685 this.checkbox = cs[3];
40688 this.anchor = cs[index];
40690 this.textNode = cs[index].firstChild;
40692 //el.on("click", this.onClick, this);
40693 //el.on("dblclick", this.onDblClick, this);
40696 // console.log(this);
40698 initEvents : function(){
40699 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
40702 var a = this.ranchor;
40704 var el = Roo.get(a);
40706 if(Roo.isOpera){ // opera render bug ignores the CSS
40707 el.setStyle("text-decoration", "none");
40710 el.on("click", this.onClick, this);
40711 el.on("dblclick", this.onDblClick, this);
40712 el.on("contextmenu", this.onContextMenu, this);
40716 /*onSelectedChange : function(state){
40719 this.addClass("x-tree-selected");
40722 this.removeClass("x-tree-selected");
40725 addClass : function(cls){
40727 Roo.fly(this.elRow).addClass(cls);
40733 removeClass : function(cls){
40735 Roo.fly(this.elRow).removeClass(cls);
40741 });//<Script type="text/javascript">
40745 * Ext JS Library 1.1.1
40746 * Copyright(c) 2006-2007, Ext JS, LLC.
40748 * Originally Released Under LGPL - original licence link has changed is not relivant.
40751 * <script type="text/javascript">
40756 * @class Roo.tree.ColumnTree
40757 * @extends Roo.tree.TreePanel
40758 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
40759 * @cfg {int} borderWidth compined right/left border allowance
40761 * @param {String/HTMLElement/Element} el The container element
40762 * @param {Object} config
40764 Roo.tree.ColumnTree = function(el, config)
40766 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
40770 * Fire this event on a container when it resizes
40771 * @param {int} w Width
40772 * @param {int} h Height
40776 this.on('resize', this.onResize, this);
40779 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
40783 borderWidth: Roo.isBorderBox ? 0 : 2,
40786 render : function(){
40787 // add the header.....
40789 Roo.tree.ColumnTree.superclass.render.apply(this);
40791 this.el.addClass('x-column-tree');
40793 this.headers = this.el.createChild(
40794 {cls:'x-tree-headers'},this.innerCt.dom);
40796 var cols = this.columns, c;
40797 var totalWidth = 0;
40799 var len = cols.length;
40800 for(var i = 0; i < len; i++){
40802 totalWidth += c.width;
40803 this.headEls.push(this.headers.createChild({
40804 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
40806 cls:'x-tree-hd-text',
40809 style:'width:'+(c.width-this.borderWidth)+'px;'
40812 this.headers.createChild({cls:'x-clear'});
40813 // prevent floats from wrapping when clipped
40814 this.headers.setWidth(totalWidth);
40815 //this.innerCt.setWidth(totalWidth);
40816 this.innerCt.setStyle({ overflow: 'auto' });
40817 this.onResize(this.width, this.height);
40821 onResize : function(w,h)
40826 this.innerCt.setWidth(this.width);
40827 this.innerCt.setHeight(this.height-20);
40830 var cols = this.columns, c;
40831 var totalWidth = 0;
40833 var len = cols.length;
40834 for(var i = 0; i < len; i++){
40836 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
40837 // it's the expander..
40838 expEl = this.headEls[i];
40841 totalWidth += c.width;
40845 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
40847 this.headers.setWidth(w-20);
40856 * Ext JS Library 1.1.1
40857 * Copyright(c) 2006-2007, Ext JS, LLC.
40859 * Originally Released Under LGPL - original licence link has changed is not relivant.
40862 * <script type="text/javascript">
40866 * @class Roo.menu.Menu
40867 * @extends Roo.util.Observable
40868 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
40869 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
40870 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
40872 * Creates a new Menu
40873 * @param {Object} config Configuration options
40875 Roo.menu.Menu = function(config){
40877 Roo.menu.Menu.superclass.constructor.call(this, config);
40879 this.id = this.id || Roo.id();
40882 * @event beforeshow
40883 * Fires before this menu is displayed
40884 * @param {Roo.menu.Menu} this
40888 * @event beforehide
40889 * Fires before this menu is hidden
40890 * @param {Roo.menu.Menu} this
40895 * Fires after this menu is displayed
40896 * @param {Roo.menu.Menu} this
40901 * Fires after this menu is hidden
40902 * @param {Roo.menu.Menu} this
40907 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
40908 * @param {Roo.menu.Menu} this
40909 * @param {Roo.menu.Item} menuItem The menu item that was clicked
40910 * @param {Roo.EventObject} e
40915 * Fires when the mouse is hovering over this menu
40916 * @param {Roo.menu.Menu} this
40917 * @param {Roo.EventObject} e
40918 * @param {Roo.menu.Item} menuItem The menu item that was clicked
40923 * Fires when the mouse exits this menu
40924 * @param {Roo.menu.Menu} this
40925 * @param {Roo.EventObject} e
40926 * @param {Roo.menu.Item} menuItem The menu item that was clicked
40931 * Fires when a menu item contained in this menu is clicked
40932 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
40933 * @param {Roo.EventObject} e
40937 if (this.registerMenu) {
40938 Roo.menu.MenuMgr.register(this);
40941 var mis = this.items;
40942 this.items = new Roo.util.MixedCollection();
40944 this.add.apply(this, mis);
40948 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
40950 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
40954 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
40955 * for bottom-right shadow (defaults to "sides")
40959 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
40960 * this menu (defaults to "tl-tr?")
40962 subMenuAlign : "tl-tr?",
40964 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
40965 * relative to its element of origin (defaults to "tl-bl?")
40967 defaultAlign : "tl-bl?",
40969 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
40971 allowOtherMenus : false,
40973 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
40975 registerMenu : true,
40980 render : function(){
40984 var el = this.el = new Roo.Layer({
40986 shadow:this.shadow,
40988 parentEl: this.parentEl || document.body,
40992 this.keyNav = new Roo.menu.MenuNav(this);
40995 el.addClass("x-menu-plain");
40998 el.addClass(this.cls);
41000 // generic focus element
41001 this.focusEl = el.createChild({
41002 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
41004 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
41005 //disabling touch- as it's causing issues ..
41006 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
41007 ul.on('click' , this.onClick, this);
41010 ul.on("mouseover", this.onMouseOver, this);
41011 ul.on("mouseout", this.onMouseOut, this);
41012 this.items.each(function(item){
41017 var li = document.createElement("li");
41018 li.className = "x-menu-list-item";
41019 ul.dom.appendChild(li);
41020 item.render(li, this);
41027 autoWidth : function(){
41028 var el = this.el, ul = this.ul;
41032 var w = this.width;
41035 }else if(Roo.isIE){
41036 el.setWidth(this.minWidth);
41037 var t = el.dom.offsetWidth; // force recalc
41038 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
41043 delayAutoWidth : function(){
41046 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
41048 this.awTask.delay(20);
41053 findTargetItem : function(e){
41054 var t = e.getTarget(".x-menu-list-item", this.ul, true);
41055 if(t && t.menuItemId){
41056 return this.items.get(t.menuItemId);
41061 onClick : function(e){
41062 Roo.log("menu.onClick");
41063 var t = this.findTargetItem(e);
41068 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
41069 if(t == this.activeItem && t.shouldDeactivate(e)){
41070 this.activeItem.deactivate();
41071 delete this.activeItem;
41075 this.setActiveItem(t, true);
41083 this.fireEvent("click", this, t, e);
41087 setActiveItem : function(item, autoExpand){
41088 if(item != this.activeItem){
41089 if(this.activeItem){
41090 this.activeItem.deactivate();
41092 this.activeItem = item;
41093 item.activate(autoExpand);
41094 }else if(autoExpand){
41100 tryActivate : function(start, step){
41101 var items = this.items;
41102 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
41103 var item = items.get(i);
41104 if(!item.disabled && item.canActivate){
41105 this.setActiveItem(item, false);
41113 onMouseOver : function(e){
41115 if(t = this.findTargetItem(e)){
41116 if(t.canActivate && !t.disabled){
41117 this.setActiveItem(t, true);
41120 this.fireEvent("mouseover", this, e, t);
41124 onMouseOut : function(e){
41126 if(t = this.findTargetItem(e)){
41127 if(t == this.activeItem && t.shouldDeactivate(e)){
41128 this.activeItem.deactivate();
41129 delete this.activeItem;
41132 this.fireEvent("mouseout", this, e, t);
41136 * Read-only. Returns true if the menu is currently displayed, else false.
41139 isVisible : function(){
41140 return this.el && !this.hidden;
41144 * Displays this menu relative to another element
41145 * @param {String/HTMLElement/Roo.Element} element The element to align to
41146 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
41147 * the element (defaults to this.defaultAlign)
41148 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41150 show : function(el, pos, parentMenu){
41151 this.parentMenu = parentMenu;
41155 this.fireEvent("beforeshow", this);
41156 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
41160 * Displays this menu at a specific xy position
41161 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
41162 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41164 showAt : function(xy, parentMenu, /* private: */_e){
41165 this.parentMenu = parentMenu;
41170 this.fireEvent("beforeshow", this);
41171 xy = this.el.adjustForConstraints(xy);
41175 this.hidden = false;
41177 this.fireEvent("show", this);
41180 focus : function(){
41182 this.doFocus.defer(50, this);
41186 doFocus : function(){
41188 this.focusEl.focus();
41193 * Hides this menu and optionally all parent menus
41194 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
41196 hide : function(deep){
41197 if(this.el && this.isVisible()){
41198 this.fireEvent("beforehide", this);
41199 if(this.activeItem){
41200 this.activeItem.deactivate();
41201 this.activeItem = null;
41204 this.hidden = true;
41205 this.fireEvent("hide", this);
41207 if(deep === true && this.parentMenu){
41208 this.parentMenu.hide(true);
41213 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
41214 * Any of the following are valid:
41216 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
41217 * <li>An HTMLElement object which will be converted to a menu item</li>
41218 * <li>A menu item config object that will be created as a new menu item</li>
41219 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
41220 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
41225 var menu = new Roo.menu.Menu();
41227 // Create a menu item to add by reference
41228 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
41230 // Add a bunch of items at once using different methods.
41231 // Only the last item added will be returned.
41232 var item = menu.add(
41233 menuItem, // add existing item by ref
41234 'Dynamic Item', // new TextItem
41235 '-', // new separator
41236 { text: 'Config Item' } // new item by config
41239 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
41240 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
41243 var a = arguments, l = a.length, item;
41244 for(var i = 0; i < l; i++){
41246 if ((typeof(el) == "object") && el.xtype && el.xns) {
41247 el = Roo.factory(el, Roo.menu);
41250 if(el.render){ // some kind of Item
41251 item = this.addItem(el);
41252 }else if(typeof el == "string"){ // string
41253 if(el == "separator" || el == "-"){
41254 item = this.addSeparator();
41256 item = this.addText(el);
41258 }else if(el.tagName || el.el){ // element
41259 item = this.addElement(el);
41260 }else if(typeof el == "object"){ // must be menu item config?
41261 item = this.addMenuItem(el);
41268 * Returns this menu's underlying {@link Roo.Element} object
41269 * @return {Roo.Element} The element
41271 getEl : function(){
41279 * Adds a separator bar to the menu
41280 * @return {Roo.menu.Item} The menu item that was added
41282 addSeparator : function(){
41283 return this.addItem(new Roo.menu.Separator());
41287 * Adds an {@link Roo.Element} object to the menu
41288 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
41289 * @return {Roo.menu.Item} The menu item that was added
41291 addElement : function(el){
41292 return this.addItem(new Roo.menu.BaseItem(el));
41296 * Adds an existing object based on {@link Roo.menu.Item} to the menu
41297 * @param {Roo.menu.Item} item The menu item to add
41298 * @return {Roo.menu.Item} The menu item that was added
41300 addItem : function(item){
41301 this.items.add(item);
41303 var li = document.createElement("li");
41304 li.className = "x-menu-list-item";
41305 this.ul.dom.appendChild(li);
41306 item.render(li, this);
41307 this.delayAutoWidth();
41313 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
41314 * @param {Object} config A MenuItem config object
41315 * @return {Roo.menu.Item} The menu item that was added
41317 addMenuItem : function(config){
41318 if(!(config instanceof Roo.menu.Item)){
41319 if(typeof config.checked == "boolean"){ // must be check menu item config?
41320 config = new Roo.menu.CheckItem(config);
41322 config = new Roo.menu.Item(config);
41325 return this.addItem(config);
41329 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
41330 * @param {String} text The text to display in the menu item
41331 * @return {Roo.menu.Item} The menu item that was added
41333 addText : function(text){
41334 return this.addItem(new Roo.menu.TextItem({ text : text }));
41338 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
41339 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
41340 * @param {Roo.menu.Item} item The menu item to add
41341 * @return {Roo.menu.Item} The menu item that was added
41343 insert : function(index, item){
41344 this.items.insert(index, item);
41346 var li = document.createElement("li");
41347 li.className = "x-menu-list-item";
41348 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
41349 item.render(li, this);
41350 this.delayAutoWidth();
41356 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
41357 * @param {Roo.menu.Item} item The menu item to remove
41359 remove : function(item){
41360 this.items.removeKey(item.id);
41365 * Removes and destroys all items in the menu
41367 removeAll : function(){
41369 while(f = this.items.first()){
41375 // MenuNav is a private utility class used internally by the Menu
41376 Roo.menu.MenuNav = function(menu){
41377 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
41378 this.scope = this.menu = menu;
41381 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
41382 doRelay : function(e, h){
41383 var k = e.getKey();
41384 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
41385 this.menu.tryActivate(0, 1);
41388 return h.call(this.scope || this, e, this.menu);
41391 up : function(e, m){
41392 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
41393 m.tryActivate(m.items.length-1, -1);
41397 down : function(e, m){
41398 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
41399 m.tryActivate(0, 1);
41403 right : function(e, m){
41405 m.activeItem.expandMenu(true);
41409 left : function(e, m){
41411 if(m.parentMenu && m.parentMenu.activeItem){
41412 m.parentMenu.activeItem.activate();
41416 enter : function(e, m){
41418 e.stopPropagation();
41419 m.activeItem.onClick(e);
41420 m.fireEvent("click", this, m.activeItem);
41426 * Ext JS Library 1.1.1
41427 * Copyright(c) 2006-2007, Ext JS, LLC.
41429 * Originally Released Under LGPL - original licence link has changed is not relivant.
41432 * <script type="text/javascript">
41436 * @class Roo.menu.MenuMgr
41437 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
41440 Roo.menu.MenuMgr = function(){
41441 var menus, active, groups = {}, attached = false, lastShow = new Date();
41443 // private - called when first menu is created
41446 active = new Roo.util.MixedCollection();
41447 Roo.get(document).addKeyListener(27, function(){
41448 if(active.length > 0){
41455 function hideAll(){
41456 if(active && active.length > 0){
41457 var c = active.clone();
41458 c.each(function(m){
41465 function onHide(m){
41467 if(active.length < 1){
41468 Roo.get(document).un("mousedown", onMouseDown);
41474 function onShow(m){
41475 var last = active.last();
41476 lastShow = new Date();
41479 Roo.get(document).on("mousedown", onMouseDown);
41483 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
41484 m.parentMenu.activeChild = m;
41485 }else if(last && last.isVisible()){
41486 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
41491 function onBeforeHide(m){
41493 m.activeChild.hide();
41495 if(m.autoHideTimer){
41496 clearTimeout(m.autoHideTimer);
41497 delete m.autoHideTimer;
41502 function onBeforeShow(m){
41503 var pm = m.parentMenu;
41504 if(!pm && !m.allowOtherMenus){
41506 }else if(pm && pm.activeChild && active != m){
41507 pm.activeChild.hide();
41512 function onMouseDown(e){
41513 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
41519 function onBeforeCheck(mi, state){
41521 var g = groups[mi.group];
41522 for(var i = 0, l = g.length; i < l; i++){
41524 g[i].setChecked(false);
41533 * Hides all menus that are currently visible
41535 hideAll : function(){
41540 register : function(menu){
41544 menus[menu.id] = menu;
41545 menu.on("beforehide", onBeforeHide);
41546 menu.on("hide", onHide);
41547 menu.on("beforeshow", onBeforeShow);
41548 menu.on("show", onShow);
41549 var g = menu.group;
41550 if(g && menu.events["checkchange"]){
41554 groups[g].push(menu);
41555 menu.on("checkchange", onCheck);
41560 * Returns a {@link Roo.menu.Menu} object
41561 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
41562 * be used to generate and return a new Menu instance.
41564 get : function(menu){
41565 if(typeof menu == "string"){ // menu id
41566 return menus[menu];
41567 }else if(menu.events){ // menu instance
41569 }else if(typeof menu.length == 'number'){ // array of menu items?
41570 return new Roo.menu.Menu({items:menu});
41571 }else{ // otherwise, must be a config
41572 return new Roo.menu.Menu(menu);
41577 unregister : function(menu){
41578 delete menus[menu.id];
41579 menu.un("beforehide", onBeforeHide);
41580 menu.un("hide", onHide);
41581 menu.un("beforeshow", onBeforeShow);
41582 menu.un("show", onShow);
41583 var g = menu.group;
41584 if(g && menu.events["checkchange"]){
41585 groups[g].remove(menu);
41586 menu.un("checkchange", onCheck);
41591 registerCheckable : function(menuItem){
41592 var g = menuItem.group;
41597 groups[g].push(menuItem);
41598 menuItem.on("beforecheckchange", onBeforeCheck);
41603 unregisterCheckable : function(menuItem){
41604 var g = menuItem.group;
41606 groups[g].remove(menuItem);
41607 menuItem.un("beforecheckchange", onBeforeCheck);
41613 * Ext JS Library 1.1.1
41614 * Copyright(c) 2006-2007, Ext JS, LLC.
41616 * Originally Released Under LGPL - original licence link has changed is not relivant.
41619 * <script type="text/javascript">
41624 * @class Roo.menu.BaseItem
41625 * @extends Roo.Component
41627 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
41628 * management and base configuration options shared by all menu components.
41630 * Creates a new BaseItem
41631 * @param {Object} config Configuration options
41633 Roo.menu.BaseItem = function(config){
41634 Roo.menu.BaseItem.superclass.constructor.call(this, config);
41639 * Fires when this item is clicked
41640 * @param {Roo.menu.BaseItem} this
41641 * @param {Roo.EventObject} e
41646 * Fires when this item is activated
41647 * @param {Roo.menu.BaseItem} this
41651 * @event deactivate
41652 * Fires when this item is deactivated
41653 * @param {Roo.menu.BaseItem} this
41659 this.on("click", this.handler, this.scope, true);
41663 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
41665 * @cfg {Function} handler
41666 * A function that will handle the click event of this menu item (defaults to undefined)
41669 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
41671 canActivate : false,
41674 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
41679 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
41681 activeClass : "x-menu-item-active",
41683 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
41685 hideOnClick : true,
41687 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
41692 ctype: "Roo.menu.BaseItem",
41695 actionMode : "container",
41698 render : function(container, parentMenu){
41699 this.parentMenu = parentMenu;
41700 Roo.menu.BaseItem.superclass.render.call(this, container);
41701 this.container.menuItemId = this.id;
41705 onRender : function(container, position){
41706 this.el = Roo.get(this.el);
41707 container.dom.appendChild(this.el.dom);
41711 onClick : function(e){
41712 if(!this.disabled && this.fireEvent("click", this, e) !== false
41713 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
41714 this.handleClick(e);
41721 activate : function(){
41725 var li = this.container;
41726 li.addClass(this.activeClass);
41727 this.region = li.getRegion().adjust(2, 2, -2, -2);
41728 this.fireEvent("activate", this);
41733 deactivate : function(){
41734 this.container.removeClass(this.activeClass);
41735 this.fireEvent("deactivate", this);
41739 shouldDeactivate : function(e){
41740 return !this.region || !this.region.contains(e.getPoint());
41744 handleClick : function(e){
41745 if(this.hideOnClick){
41746 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
41751 expandMenu : function(autoActivate){
41756 hideMenu : function(){
41761 * Ext JS Library 1.1.1
41762 * Copyright(c) 2006-2007, Ext JS, LLC.
41764 * Originally Released Under LGPL - original licence link has changed is not relivant.
41767 * <script type="text/javascript">
41771 * @class Roo.menu.Adapter
41772 * @extends Roo.menu.BaseItem
41774 * 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.
41775 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
41777 * Creates a new Adapter
41778 * @param {Object} config Configuration options
41780 Roo.menu.Adapter = function(component, config){
41781 Roo.menu.Adapter.superclass.constructor.call(this, config);
41782 this.component = component;
41784 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
41786 canActivate : true,
41789 onRender : function(container, position){
41790 this.component.render(container);
41791 this.el = this.component.getEl();
41795 activate : function(){
41799 this.component.focus();
41800 this.fireEvent("activate", this);
41805 deactivate : function(){
41806 this.fireEvent("deactivate", this);
41810 disable : function(){
41811 this.component.disable();
41812 Roo.menu.Adapter.superclass.disable.call(this);
41816 enable : function(){
41817 this.component.enable();
41818 Roo.menu.Adapter.superclass.enable.call(this);
41822 * Ext JS Library 1.1.1
41823 * Copyright(c) 2006-2007, Ext JS, LLC.
41825 * Originally Released Under LGPL - original licence link has changed is not relivant.
41828 * <script type="text/javascript">
41832 * @class Roo.menu.TextItem
41833 * @extends Roo.menu.BaseItem
41834 * Adds a static text string to a menu, usually used as either a heading or group separator.
41835 * Note: old style constructor with text is still supported.
41838 * Creates a new TextItem
41839 * @param {Object} cfg Configuration
41841 Roo.menu.TextItem = function(cfg){
41842 if (typeof(cfg) == 'string') {
41845 Roo.apply(this,cfg);
41848 Roo.menu.TextItem.superclass.constructor.call(this);
41851 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
41853 * @cfg {String} text Text to show on item.
41858 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
41860 hideOnClick : false,
41862 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
41864 itemCls : "x-menu-text",
41867 onRender : function(){
41868 var s = document.createElement("span");
41869 s.className = this.itemCls;
41870 s.innerHTML = this.text;
41872 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
41876 * Ext JS Library 1.1.1
41877 * Copyright(c) 2006-2007, Ext JS, LLC.
41879 * Originally Released Under LGPL - original licence link has changed is not relivant.
41882 * <script type="text/javascript">
41886 * @class Roo.menu.Separator
41887 * @extends Roo.menu.BaseItem
41888 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
41889 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
41891 * @param {Object} config Configuration options
41893 Roo.menu.Separator = function(config){
41894 Roo.menu.Separator.superclass.constructor.call(this, config);
41897 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
41899 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
41901 itemCls : "x-menu-sep",
41903 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
41905 hideOnClick : false,
41908 onRender : function(li){
41909 var s = document.createElement("span");
41910 s.className = this.itemCls;
41911 s.innerHTML = " ";
41913 li.addClass("x-menu-sep-li");
41914 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
41918 * Ext JS Library 1.1.1
41919 * Copyright(c) 2006-2007, Ext JS, LLC.
41921 * Originally Released Under LGPL - original licence link has changed is not relivant.
41924 * <script type="text/javascript">
41927 * @class Roo.menu.Item
41928 * @extends Roo.menu.BaseItem
41929 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
41930 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
41931 * activation and click handling.
41933 * Creates a new Item
41934 * @param {Object} config Configuration options
41936 Roo.menu.Item = function(config){
41937 Roo.menu.Item.superclass.constructor.call(this, config);
41939 this.menu = Roo.menu.MenuMgr.get(this.menu);
41942 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
41944 * @cfg {Roo.menu.Menu} menu
41948 * @cfg {String} text
41949 * The text to show on the menu item.
41953 * @cfg {String} html to render in menu
41954 * The text to show on the menu item (HTML version).
41958 * @cfg {String} icon
41959 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
41963 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
41965 itemCls : "x-menu-item",
41967 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
41969 canActivate : true,
41971 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
41974 // doc'd in BaseItem
41978 ctype: "Roo.menu.Item",
41981 onRender : function(container, position){
41982 var el = document.createElement("a");
41983 el.hideFocus = true;
41984 el.unselectable = "on";
41985 el.href = this.href || "#";
41986 if(this.hrefTarget){
41987 el.target = this.hrefTarget;
41989 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
41991 var html = this.html.length ? this.html : String.format('{0}',this.text);
41993 el.innerHTML = String.format(
41994 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
41995 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
41997 Roo.menu.Item.superclass.onRender.call(this, container, position);
42001 * Sets the text to display in this menu item
42002 * @param {String} text The text to display
42003 * @param {Boolean} isHTML true to indicate text is pure html.
42005 setText : function(text, isHTML){
42013 var html = this.html.length ? this.html : String.format('{0}',this.text);
42015 this.el.update(String.format(
42016 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
42017 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
42018 this.parentMenu.autoWidth();
42023 handleClick : function(e){
42024 if(!this.href){ // if no link defined, stop the event automatically
42027 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
42031 activate : function(autoExpand){
42032 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
42042 shouldDeactivate : function(e){
42043 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
42044 if(this.menu && this.menu.isVisible()){
42045 return !this.menu.getEl().getRegion().contains(e.getPoint());
42053 deactivate : function(){
42054 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
42059 expandMenu : function(autoActivate){
42060 if(!this.disabled && this.menu){
42061 clearTimeout(this.hideTimer);
42062 delete this.hideTimer;
42063 if(!this.menu.isVisible() && !this.showTimer){
42064 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
42065 }else if (this.menu.isVisible() && autoActivate){
42066 this.menu.tryActivate(0, 1);
42072 deferExpand : function(autoActivate){
42073 delete this.showTimer;
42074 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
42076 this.menu.tryActivate(0, 1);
42081 hideMenu : function(){
42082 clearTimeout(this.showTimer);
42083 delete this.showTimer;
42084 if(!this.hideTimer && this.menu && this.menu.isVisible()){
42085 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
42090 deferHide : function(){
42091 delete this.hideTimer;
42096 * Ext JS Library 1.1.1
42097 * Copyright(c) 2006-2007, Ext JS, LLC.
42099 * Originally Released Under LGPL - original licence link has changed is not relivant.
42102 * <script type="text/javascript">
42106 * @class Roo.menu.CheckItem
42107 * @extends Roo.menu.Item
42108 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
42110 * Creates a new CheckItem
42111 * @param {Object} config Configuration options
42113 Roo.menu.CheckItem = function(config){
42114 Roo.menu.CheckItem.superclass.constructor.call(this, config);
42117 * @event beforecheckchange
42118 * Fires before the checked value is set, providing an opportunity to cancel if needed
42119 * @param {Roo.menu.CheckItem} this
42120 * @param {Boolean} checked The new checked value that will be set
42122 "beforecheckchange" : true,
42124 * @event checkchange
42125 * Fires after the checked value has been set
42126 * @param {Roo.menu.CheckItem} this
42127 * @param {Boolean} checked The checked value that was set
42129 "checkchange" : true
42131 if(this.checkHandler){
42132 this.on('checkchange', this.checkHandler, this.scope);
42135 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
42137 * @cfg {String} group
42138 * All check items with the same group name will automatically be grouped into a single-select
42139 * radio button group (defaults to '')
42142 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
42144 itemCls : "x-menu-item x-menu-check-item",
42146 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
42148 groupClass : "x-menu-group-item",
42151 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
42152 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
42153 * initialized with checked = true will be rendered as checked.
42158 ctype: "Roo.menu.CheckItem",
42161 onRender : function(c){
42162 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
42164 this.el.addClass(this.groupClass);
42166 Roo.menu.MenuMgr.registerCheckable(this);
42168 this.checked = false;
42169 this.setChecked(true, true);
42174 destroy : function(){
42176 Roo.menu.MenuMgr.unregisterCheckable(this);
42178 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
42182 * Set the checked state of this item
42183 * @param {Boolean} checked The new checked value
42184 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
42186 setChecked : function(state, suppressEvent){
42187 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
42188 if(this.container){
42189 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
42191 this.checked = state;
42192 if(suppressEvent !== true){
42193 this.fireEvent("checkchange", this, state);
42199 handleClick : function(e){
42200 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
42201 this.setChecked(!this.checked);
42203 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
42207 * Ext JS Library 1.1.1
42208 * Copyright(c) 2006-2007, Ext JS, LLC.
42210 * Originally Released Under LGPL - original licence link has changed is not relivant.
42213 * <script type="text/javascript">
42217 * @class Roo.menu.DateItem
42218 * @extends Roo.menu.Adapter
42219 * A menu item that wraps the {@link Roo.DatPicker} component.
42221 * Creates a new DateItem
42222 * @param {Object} config Configuration options
42224 Roo.menu.DateItem = function(config){
42225 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
42226 /** The Roo.DatePicker object @type Roo.DatePicker */
42227 this.picker = this.component;
42228 this.addEvents({select: true});
42230 this.picker.on("render", function(picker){
42231 picker.getEl().swallowEvent("click");
42232 picker.container.addClass("x-menu-date-item");
42235 this.picker.on("select", this.onSelect, this);
42238 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
42240 onSelect : function(picker, date){
42241 this.fireEvent("select", this, date, picker);
42242 Roo.menu.DateItem.superclass.handleClick.call(this);
42246 * Ext JS Library 1.1.1
42247 * Copyright(c) 2006-2007, Ext JS, LLC.
42249 * Originally Released Under LGPL - original licence link has changed is not relivant.
42252 * <script type="text/javascript">
42256 * @class Roo.menu.ColorItem
42257 * @extends Roo.menu.Adapter
42258 * A menu item that wraps the {@link Roo.ColorPalette} component.
42260 * Creates a new ColorItem
42261 * @param {Object} config Configuration options
42263 Roo.menu.ColorItem = function(config){
42264 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
42265 /** The Roo.ColorPalette object @type Roo.ColorPalette */
42266 this.palette = this.component;
42267 this.relayEvents(this.palette, ["select"]);
42268 if(this.selectHandler){
42269 this.on('select', this.selectHandler, this.scope);
42272 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
42274 * Ext JS Library 1.1.1
42275 * Copyright(c) 2006-2007, Ext JS, LLC.
42277 * Originally Released Under LGPL - original licence link has changed is not relivant.
42280 * <script type="text/javascript">
42285 * @class Roo.menu.DateMenu
42286 * @extends Roo.menu.Menu
42287 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
42289 * Creates a new DateMenu
42290 * @param {Object} config Configuration options
42292 Roo.menu.DateMenu = function(config){
42293 Roo.menu.DateMenu.superclass.constructor.call(this, config);
42295 var di = new Roo.menu.DateItem(config);
42298 * The {@link Roo.DatePicker} instance for this DateMenu
42301 this.picker = di.picker;
42304 * @param {DatePicker} picker
42305 * @param {Date} date
42307 this.relayEvents(di, ["select"]);
42308 this.on('beforeshow', function(){
42310 this.picker.hideMonthPicker(false);
42314 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
42318 * Ext JS Library 1.1.1
42319 * Copyright(c) 2006-2007, Ext JS, LLC.
42321 * Originally Released Under LGPL - original licence link has changed is not relivant.
42324 * <script type="text/javascript">
42329 * @class Roo.menu.ColorMenu
42330 * @extends Roo.menu.Menu
42331 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
42333 * Creates a new ColorMenu
42334 * @param {Object} config Configuration options
42336 Roo.menu.ColorMenu = function(config){
42337 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
42339 var ci = new Roo.menu.ColorItem(config);
42342 * The {@link Roo.ColorPalette} instance for this ColorMenu
42343 * @type ColorPalette
42345 this.palette = ci.palette;
42348 * @param {ColorPalette} palette
42349 * @param {String} color
42351 this.relayEvents(ci, ["select"]);
42353 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
42355 * Ext JS Library 1.1.1
42356 * Copyright(c) 2006-2007, Ext JS, LLC.
42358 * Originally Released Under LGPL - original licence link has changed is not relivant.
42361 * <script type="text/javascript">
42365 * @class Roo.form.TextItem
42366 * @extends Roo.BoxComponent
42367 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
42369 * Creates a new TextItem
42370 * @param {Object} config Configuration options
42372 Roo.form.TextItem = function(config){
42373 Roo.form.TextItem.superclass.constructor.call(this, config);
42376 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
42379 * @cfg {String} tag the tag for this item (default div)
42383 * @cfg {String} html the content for this item
42387 getAutoCreate : function()
42400 onRender : function(ct, position)
42402 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
42405 var cfg = this.getAutoCreate();
42407 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
42409 if (!cfg.name.length) {
42412 this.el = ct.createChild(cfg, position);
42417 * @param {String} html update the Contents of the element.
42419 setHTML : function(html)
42421 this.fieldEl.dom.innerHTML = html;
42426 * Ext JS Library 1.1.1
42427 * Copyright(c) 2006-2007, Ext JS, LLC.
42429 * Originally Released Under LGPL - original licence link has changed is not relivant.
42432 * <script type="text/javascript">
42436 * @class Roo.form.Field
42437 * @extends Roo.BoxComponent
42438 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
42440 * Creates a new Field
42441 * @param {Object} config Configuration options
42443 Roo.form.Field = function(config){
42444 Roo.form.Field.superclass.constructor.call(this, config);
42447 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
42449 * @cfg {String} fieldLabel Label to use when rendering a form.
42452 * @cfg {String} labelSeparator the ':' after a field label (default :) = set it to empty string to hide the field label.
42455 * @cfg {String} qtip Mouse over tip
42459 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
42461 invalidClass : "x-form-invalid",
42463 * @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")
42465 invalidText : "The value in this field is invalid",
42467 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
42469 focusClass : "x-form-focus",
42471 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
42472 automatic validation (defaults to "keyup").
42474 validationEvent : "keyup",
42476 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
42478 validateOnBlur : true,
42480 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
42482 validationDelay : 250,
42484 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42485 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
42487 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
42489 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
42491 fieldClass : "x-form-field",
42493 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
42496 ----------- ----------------------------------------------------------------------
42497 qtip Display a quick tip when the user hovers over the field
42498 title Display a default browser title attribute popup
42499 under Add a block div beneath the field containing the error text
42500 side Add an error icon to the right of the field with a popup on hover
42501 [element id] Add the error text directly to the innerHTML of the specified element
42504 msgTarget : 'qtip',
42506 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
42511 * @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.
42516 * @cfg {Boolean} disabled True to disable the field (defaults to false).
42521 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
42523 inputType : undefined,
42526 * @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).
42528 tabIndex : undefined,
42531 isFormField : true,
42536 * @property {Roo.Element} fieldEl
42537 * Element Containing the rendered Field (with label etc.)
42540 * @cfg {Mixed} value A value to initialize this field with.
42545 * @cfg {String} name The field's HTML name attribute.
42548 * @cfg {String} cls A CSS class to apply to the field's underlying element.
42551 loadedValue : false,
42555 initComponent : function(){
42556 Roo.form.Field.superclass.initComponent.call(this);
42560 * Fires when this field receives input focus.
42561 * @param {Roo.form.Field} this
42566 * Fires when this field loses input focus.
42567 * @param {Roo.form.Field} this
42571 * @event specialkey
42572 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
42573 * {@link Roo.EventObject#getKey} to determine which key was pressed.
42574 * @param {Roo.form.Field} this
42575 * @param {Roo.EventObject} e The event object
42580 * Fires just before the field blurs if the field value has changed.
42581 * @param {Roo.form.Field} this
42582 * @param {Mixed} newValue The new value
42583 * @param {Mixed} oldValue The original value
42588 * Fires after the field has been marked as invalid.
42589 * @param {Roo.form.Field} this
42590 * @param {String} msg The validation message
42595 * Fires after the field has been validated with no errors.
42596 * @param {Roo.form.Field} this
42601 * Fires after the key up
42602 * @param {Roo.form.Field} this
42603 * @param {Roo.EventObject} e The event Object
42610 * Returns the name attribute of the field if available
42611 * @return {String} name The field name
42613 getName: function(){
42614 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42618 onRender : function(ct, position){
42619 Roo.form.Field.superclass.onRender.call(this, ct, position);
42621 var cfg = this.getAutoCreate();
42623 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
42625 if (!cfg.name.length) {
42628 if(this.inputType){
42629 cfg.type = this.inputType;
42631 this.el = ct.createChild(cfg, position);
42633 var type = this.el.dom.type;
42635 if(type == 'password'){
42638 this.el.addClass('x-form-'+type);
42641 this.el.dom.readOnly = true;
42643 if(this.tabIndex !== undefined){
42644 this.el.dom.setAttribute('tabIndex', this.tabIndex);
42647 this.el.addClass([this.fieldClass, this.cls]);
42652 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
42653 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
42654 * @return {Roo.form.Field} this
42656 applyTo : function(target){
42657 this.allowDomMove = false;
42658 this.el = Roo.get(target);
42659 this.render(this.el.dom.parentNode);
42664 initValue : function(){
42665 if(this.value !== undefined){
42666 this.setValue(this.value);
42667 }else if(this.el.dom.value.length > 0){
42668 this.setValue(this.el.dom.value);
42673 * Returns true if this field has been changed since it was originally loaded and is not disabled.
42674 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
42676 isDirty : function() {
42677 if(this.disabled) {
42680 return String(this.getValue()) !== String(this.originalValue);
42684 * stores the current value in loadedValue
42686 resetHasChanged : function()
42688 this.loadedValue = String(this.getValue());
42691 * checks the current value against the 'loaded' value.
42692 * Note - will return false if 'resetHasChanged' has not been called first.
42694 hasChanged : function()
42696 if(this.disabled || this.readOnly) {
42699 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
42705 afterRender : function(){
42706 Roo.form.Field.superclass.afterRender.call(this);
42711 fireKey : function(e){
42712 //Roo.log('field ' + e.getKey());
42713 if(e.isNavKeyPress()){
42714 this.fireEvent("specialkey", this, e);
42719 * Resets the current field value to the originally loaded value and clears any validation messages
42721 reset : function(){
42722 this.setValue(this.resetValue);
42723 this.originalValue = this.getValue();
42724 this.clearInvalid();
42728 initEvents : function(){
42729 // safari killled keypress - so keydown is now used..
42730 this.el.on("keydown" , this.fireKey, this);
42731 this.el.on("focus", this.onFocus, this);
42732 this.el.on("blur", this.onBlur, this);
42733 this.el.relayEvent('keyup', this);
42735 // reference to original value for reset
42736 this.originalValue = this.getValue();
42737 this.resetValue = this.getValue();
42741 onFocus : function(){
42742 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42743 this.el.addClass(this.focusClass);
42745 if(!this.hasFocus){
42746 this.hasFocus = true;
42747 this.startValue = this.getValue();
42748 this.fireEvent("focus", this);
42752 beforeBlur : Roo.emptyFn,
42755 onBlur : function(){
42757 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42758 this.el.removeClass(this.focusClass);
42760 this.hasFocus = false;
42761 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42764 var v = this.getValue();
42765 if(String(v) !== String(this.startValue)){
42766 this.fireEvent('change', this, v, this.startValue);
42768 this.fireEvent("blur", this);
42772 * Returns whether or not the field value is currently valid
42773 * @param {Boolean} preventMark True to disable marking the field invalid
42774 * @return {Boolean} True if the value is valid, else false
42776 isValid : function(preventMark){
42780 var restore = this.preventMark;
42781 this.preventMark = preventMark === true;
42782 var v = this.validateValue(this.processValue(this.getRawValue()));
42783 this.preventMark = restore;
42788 * Validates the field value
42789 * @return {Boolean} True if the value is valid, else false
42791 validate : function(){
42792 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
42793 this.clearInvalid();
42799 processValue : function(value){
42804 // Subclasses should provide the validation implementation by overriding this
42805 validateValue : function(value){
42810 * Mark this field as invalid
42811 * @param {String} msg The validation message
42813 markInvalid : function(msg){
42814 if(!this.rendered || this.preventMark){ // not rendered
42818 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
42820 obj.el.addClass(this.invalidClass);
42821 msg = msg || this.invalidText;
42822 switch(this.msgTarget){
42824 obj.el.dom.qtip = msg;
42825 obj.el.dom.qclass = 'x-form-invalid-tip';
42826 if(Roo.QuickTips){ // fix for floating editors interacting with DND
42827 Roo.QuickTips.enable();
42831 this.el.dom.title = msg;
42835 var elp = this.el.findParent('.x-form-element', 5, true);
42836 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
42837 this.errorEl.setWidth(elp.getWidth(true)-20);
42839 this.errorEl.update(msg);
42840 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
42843 if(!this.errorIcon){
42844 var elp = this.el.findParent('.x-form-element', 5, true);
42845 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
42847 this.alignErrorIcon();
42848 this.errorIcon.dom.qtip = msg;
42849 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
42850 this.errorIcon.show();
42851 this.on('resize', this.alignErrorIcon, this);
42854 var t = Roo.getDom(this.msgTarget);
42856 t.style.display = this.msgDisplay;
42859 this.fireEvent('invalid', this, msg);
42863 alignErrorIcon : function(){
42864 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
42868 * Clear any invalid styles/messages for this field
42870 clearInvalid : function(){
42871 if(!this.rendered || this.preventMark){ // not rendered
42874 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
42876 obj.el.removeClass(this.invalidClass);
42877 switch(this.msgTarget){
42879 obj.el.dom.qtip = '';
42882 this.el.dom.title = '';
42886 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
42890 if(this.errorIcon){
42891 this.errorIcon.dom.qtip = '';
42892 this.errorIcon.hide();
42893 this.un('resize', this.alignErrorIcon, this);
42897 var t = Roo.getDom(this.msgTarget);
42899 t.style.display = 'none';
42902 this.fireEvent('valid', this);
42906 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
42907 * @return {Mixed} value The field value
42909 getRawValue : function(){
42910 var v = this.el.getValue();
42916 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
42917 * @return {Mixed} value The field value
42919 getValue : function(){
42920 var v = this.el.getValue();
42926 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
42927 * @param {Mixed} value The value to set
42929 setRawValue : function(v){
42930 return this.el.dom.value = (v === null || v === undefined ? '' : v);
42934 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
42935 * @param {Mixed} value The value to set
42937 setValue : function(v){
42940 this.el.dom.value = (v === null || v === undefined ? '' : v);
42945 adjustSize : function(w, h){
42946 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
42947 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
42951 adjustWidth : function(tag, w){
42952 tag = tag.toLowerCase();
42953 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
42954 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
42955 if(tag == 'input'){
42958 if(tag == 'textarea'){
42961 }else if(Roo.isOpera){
42962 if(tag == 'input'){
42965 if(tag == 'textarea'){
42975 // anything other than normal should be considered experimental
42976 Roo.form.Field.msgFx = {
42978 show: function(msgEl, f){
42979 msgEl.setDisplayed('block');
42982 hide : function(msgEl, f){
42983 msgEl.setDisplayed(false).update('');
42988 show: function(msgEl, f){
42989 msgEl.slideIn('t', {stopFx:true});
42992 hide : function(msgEl, f){
42993 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
42998 show: function(msgEl, f){
42999 msgEl.fixDisplay();
43000 msgEl.alignTo(f.el, 'tl-tr');
43001 msgEl.slideIn('l', {stopFx:true});
43004 hide : function(msgEl, f){
43005 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
43010 * Ext JS Library 1.1.1
43011 * Copyright(c) 2006-2007, Ext JS, LLC.
43013 * Originally Released Under LGPL - original licence link has changed is not relivant.
43016 * <script type="text/javascript">
43021 * @class Roo.form.TextField
43022 * @extends Roo.form.Field
43023 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
43024 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
43026 * Creates a new TextField
43027 * @param {Object} config Configuration options
43029 Roo.form.TextField = function(config){
43030 Roo.form.TextField.superclass.constructor.call(this, config);
43034 * Fires when the autosize function is triggered. The field may or may not have actually changed size
43035 * according to the default logic, but this event provides a hook for the developer to apply additional
43036 * logic at runtime to resize the field if needed.
43037 * @param {Roo.form.Field} this This text field
43038 * @param {Number} width The new field width
43044 Roo.extend(Roo.form.TextField, Roo.form.Field, {
43046 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
43050 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
43054 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
43058 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
43062 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
43066 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
43068 disableKeyFilter : false,
43070 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
43074 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
43078 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
43080 maxLength : Number.MAX_VALUE,
43082 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
43084 minLengthText : "The minimum length for this field is {0}",
43086 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
43088 maxLengthText : "The maximum length for this field is {0}",
43090 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
43092 selectOnFocus : false,
43094 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
43096 allowLeadingSpace : false,
43098 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
43100 blankText : "This field is required",
43102 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
43103 * If available, this function will be called only after the basic validators all return true, and will be passed the
43104 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
43108 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
43109 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
43110 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
43114 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
43118 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
43124 initEvents : function()
43126 if (this.emptyText) {
43127 this.el.attr('placeholder', this.emptyText);
43130 Roo.form.TextField.superclass.initEvents.call(this);
43131 if(this.validationEvent == 'keyup'){
43132 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43133 this.el.on('keyup', this.filterValidation, this);
43135 else if(this.validationEvent !== false){
43136 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43139 if(this.selectOnFocus){
43140 this.on("focus", this.preFocus, this);
43142 if (!this.allowLeadingSpace) {
43143 this.on('blur', this.cleanLeadingSpace, this);
43146 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43147 this.el.on("keypress", this.filterKeys, this);
43150 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
43151 this.el.on("click", this.autoSize, this);
43153 if(this.el.is('input[type=password]') && Roo.isSafari){
43154 this.el.on('keydown', this.SafariOnKeyDown, this);
43158 processValue : function(value){
43159 if(this.stripCharsRe){
43160 var newValue = value.replace(this.stripCharsRe, '');
43161 if(newValue !== value){
43162 this.setRawValue(newValue);
43169 filterValidation : function(e){
43170 if(!e.isNavKeyPress()){
43171 this.validationTask.delay(this.validationDelay);
43176 onKeyUp : function(e){
43177 if(!e.isNavKeyPress()){
43181 // private - clean the leading white space
43182 cleanLeadingSpace : function(e)
43184 if ( this.inputType == 'file') {
43188 this.setValue((this.getValue() + '').replace(/^\s+/,''));
43191 * Resets the current field value to the originally-loaded value and clears any validation messages.
43194 reset : function(){
43195 Roo.form.TextField.superclass.reset.call(this);
43199 preFocus : function(){
43201 if(this.selectOnFocus){
43202 this.el.dom.select();
43208 filterKeys : function(e){
43209 var k = e.getKey();
43210 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
43213 var c = e.getCharCode(), cc = String.fromCharCode(c);
43214 if(Roo.isIE && (e.isSpecialKey() || !cc)){
43217 if(!this.maskRe.test(cc)){
43222 setValue : function(v){
43224 Roo.form.TextField.superclass.setValue.apply(this, arguments);
43230 * Validates a value according to the field's validation rules and marks the field as invalid
43231 * if the validation fails
43232 * @param {Mixed} value The value to validate
43233 * @return {Boolean} True if the value is valid, else false
43235 validateValue : function(value){
43236 if(value.length < 1) { // if it's blank
43237 if(this.allowBlank){
43238 this.clearInvalid();
43241 this.markInvalid(this.blankText);
43245 if(value.length < this.minLength){
43246 this.markInvalid(String.format(this.minLengthText, this.minLength));
43249 if(value.length > this.maxLength){
43250 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
43254 var vt = Roo.form.VTypes;
43255 if (value.trim() != value) { // trim before checking email (and other stuf??)
43256 value = value.trim();
43257 this.el.dom.value = value;
43260 if(!vt[this.vtype](value, this)){
43261 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
43265 if(typeof this.validator == "function"){
43266 var msg = this.validator(value);
43268 this.markInvalid(msg);
43272 if(this.regex && !this.regex.test(value)){
43273 this.markInvalid(this.regexText);
43280 * Selects text in this field
43281 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
43282 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
43284 selectText : function(start, end){
43285 var v = this.getRawValue();
43287 start = start === undefined ? 0 : start;
43288 end = end === undefined ? v.length : end;
43289 var d = this.el.dom;
43290 if(d.setSelectionRange){
43291 d.setSelectionRange(start, end);
43292 }else if(d.createTextRange){
43293 var range = d.createTextRange();
43294 range.moveStart("character", start);
43295 range.moveEnd("character", v.length-end);
43302 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
43303 * This only takes effect if grow = true, and fires the autosize event.
43305 autoSize : function(){
43306 if(!this.grow || !this.rendered){
43310 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
43313 var v = el.dom.value;
43314 var d = document.createElement('div');
43315 d.appendChild(document.createTextNode(v));
43319 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
43320 this.el.setWidth(w);
43321 this.fireEvent("autosize", this, w);
43325 SafariOnKeyDown : function(event)
43327 // this is a workaround for a password hang bug on chrome/ webkit.
43329 var isSelectAll = false;
43331 if(this.el.dom.selectionEnd > 0){
43332 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
43334 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
43335 event.preventDefault();
43340 // skip handling paste
43341 if(isSelectAll && event.getCharCode() > 31 && !(event.ctrlKey && event.getCharCode() == 86)){ // backspace and delete key
43343 event.preventDefault();
43344 // this is very hacky as keydown always get's upper case.
43346 var cc = String.fromCharCode(event.getCharCode());
43349 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
43355 });Roo.form.Password = function(config){
43356 Roo.form.Password.superclass.constructor.call(this, config);
43358 this.inputType = 'password';
43361 Roo.extend(Roo.form.Password, Roo.form.TextField, {
43362 onRender : function(ct, position)
43364 Roo.form.Password.superclass.onRender.call(this, ct, position);
43366 this.parentEl().addClass('form-password');
43368 this.wrap = this.el.wrap({
43369 cls : 'password-wrap'
43372 this.toggle = this.wrap.createChild({
43374 cls : 'password-toggle'
43378 this.toggleEl().addClass('password-hidden');
43380 this.toggleEl().on('click', this.onToggleClick, this);;
43383 parentEl : function()
43385 return this.el.findParent('.x-form-element', 5, true);
43388 toggleEl: function()
43390 return this.parentEl().select('button.password-toggle',true).first();
43393 onToggleClick : function(e)
43395 var input = this.el;
43396 var toggle = this.toggleEl();
43398 toggle.removeClass(['password-visible', 'password-hidden']);
43400 if(input.attr('type') == 'password') {
43401 input.attr('type', 'text');
43402 toggle.addClass('password-visible');
43405 input.attr('type', 'password');
43406 toggle.addClass('password-hidden');
43411 * Ext JS Library 1.1.1
43412 * Copyright(c) 2006-2007, Ext JS, LLC.
43414 * Originally Released Under LGPL - original licence link has changed is not relivant.
43417 * <script type="text/javascript">
43421 * @class Roo.form.Hidden
43422 * @extends Roo.form.TextField
43423 * Simple Hidden element used on forms
43425 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
43428 * Creates a new Hidden form element.
43429 * @param {Object} config Configuration options
43434 // easy hidden field...
43435 Roo.form.Hidden = function(config){
43436 Roo.form.Hidden.superclass.constructor.call(this, config);
43439 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
43441 inputType: 'hidden',
43444 labelSeparator: '',
43446 itemCls : 'x-form-item-display-none'
43454 * Ext JS Library 1.1.1
43455 * Copyright(c) 2006-2007, Ext JS, LLC.
43457 * Originally Released Under LGPL - original licence link has changed is not relivant.
43460 * <script type="text/javascript">
43464 * @class Roo.form.TriggerField
43465 * @extends Roo.form.TextField
43466 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
43467 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
43468 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
43469 * for which you can provide a custom implementation. For example:
43471 var trigger = new Roo.form.TriggerField();
43472 trigger.onTriggerClick = myTriggerFn;
43473 trigger.applyTo('my-field');
43476 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
43477 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
43478 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
43479 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
43481 * Create a new TriggerField.
43482 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
43483 * to the base TextField)
43485 Roo.form.TriggerField = function(config){
43486 this.mimicing = false;
43487 Roo.form.TriggerField.superclass.constructor.call(this, config);
43490 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
43492 * @cfg {String} triggerClass A CSS class to apply to the trigger
43495 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43496 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
43498 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
43500 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
43504 /** @cfg {Boolean} grow @hide */
43505 /** @cfg {Number} growMin @hide */
43506 /** @cfg {Number} growMax @hide */
43512 autoSize: Roo.emptyFn,
43516 deferHeight : true,
43519 actionMode : 'wrap',
43521 onResize : function(w, h){
43522 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
43523 if(typeof w == 'number'){
43524 var x = w - this.trigger.getWidth();
43525 this.el.setWidth(this.adjustWidth('input', x));
43526 this.trigger.setStyle('left', x+'px');
43531 adjustSize : Roo.BoxComponent.prototype.adjustSize,
43534 getResizeEl : function(){
43539 getPositionEl : function(){
43544 alignErrorIcon : function(){
43545 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
43549 onRender : function(ct, position){
43550 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
43551 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
43552 this.trigger = this.wrap.createChild(this.triggerConfig ||
43553 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
43554 if(this.hideTrigger){
43555 this.trigger.setDisplayed(false);
43557 this.initTrigger();
43559 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
43564 initTrigger : function(){
43565 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43566 this.trigger.addClassOnOver('x-form-trigger-over');
43567 this.trigger.addClassOnClick('x-form-trigger-click');
43571 onDestroy : function(){
43573 this.trigger.removeAllListeners();
43574 this.trigger.remove();
43577 this.wrap.remove();
43579 Roo.form.TriggerField.superclass.onDestroy.call(this);
43583 onFocus : function(){
43584 Roo.form.TriggerField.superclass.onFocus.call(this);
43585 if(!this.mimicing){
43586 this.wrap.addClass('x-trigger-wrap-focus');
43587 this.mimicing = true;
43588 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
43589 if(this.monitorTab){
43590 this.el.on("keydown", this.checkTab, this);
43596 checkTab : function(e){
43597 if(e.getKey() == e.TAB){
43598 this.triggerBlur();
43603 onBlur : function(){
43608 mimicBlur : function(e, t){
43609 if(!this.wrap.contains(t) && this.validateBlur()){
43610 this.triggerBlur();
43615 triggerBlur : function(){
43616 this.mimicing = false;
43617 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
43618 if(this.monitorTab){
43619 this.el.un("keydown", this.checkTab, this);
43621 this.wrap.removeClass('x-trigger-wrap-focus');
43622 Roo.form.TriggerField.superclass.onBlur.call(this);
43626 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
43627 validateBlur : function(e, t){
43632 onDisable : function(){
43633 Roo.form.TriggerField.superclass.onDisable.call(this);
43635 this.wrap.addClass('x-item-disabled');
43640 onEnable : function(){
43641 Roo.form.TriggerField.superclass.onEnable.call(this);
43643 this.wrap.removeClass('x-item-disabled');
43648 onShow : function(){
43649 var ae = this.getActionEl();
43652 ae.dom.style.display = '';
43653 ae.dom.style.visibility = 'visible';
43659 onHide : function(){
43660 var ae = this.getActionEl();
43661 ae.dom.style.display = 'none';
43665 * The function that should handle the trigger's click event. This method does nothing by default until overridden
43666 * by an implementing function.
43668 * @param {EventObject} e
43670 onTriggerClick : Roo.emptyFn
43673 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
43674 // to be extended by an implementing class. For an example of implementing this class, see the custom
43675 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
43676 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
43677 initComponent : function(){
43678 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
43680 this.triggerConfig = {
43681 tag:'span', cls:'x-form-twin-triggers', cn:[
43682 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
43683 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
43687 getTrigger : function(index){
43688 return this.triggers[index];
43691 initTrigger : function(){
43692 var ts = this.trigger.select('.x-form-trigger', true);
43693 this.wrap.setStyle('overflow', 'hidden');
43694 var triggerField = this;
43695 ts.each(function(t, all, index){
43696 t.hide = function(){
43697 var w = triggerField.wrap.getWidth();
43698 this.dom.style.display = 'none';
43699 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
43701 t.show = function(){
43702 var w = triggerField.wrap.getWidth();
43703 this.dom.style.display = '';
43704 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
43706 var triggerIndex = 'Trigger'+(index+1);
43708 if(this['hide'+triggerIndex]){
43709 t.dom.style.display = 'none';
43711 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
43712 t.addClassOnOver('x-form-trigger-over');
43713 t.addClassOnClick('x-form-trigger-click');
43715 this.triggers = ts.elements;
43718 onTrigger1Click : Roo.emptyFn,
43719 onTrigger2Click : Roo.emptyFn
43722 * Ext JS Library 1.1.1
43723 * Copyright(c) 2006-2007, Ext JS, LLC.
43725 * Originally Released Under LGPL - original licence link has changed is not relivant.
43728 * <script type="text/javascript">
43732 * @class Roo.form.TextArea
43733 * @extends Roo.form.TextField
43734 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
43735 * support for auto-sizing.
43737 * Creates a new TextArea
43738 * @param {Object} config Configuration options
43740 Roo.form.TextArea = function(config){
43741 Roo.form.TextArea.superclass.constructor.call(this, config);
43742 // these are provided exchanges for backwards compat
43743 // minHeight/maxHeight were replaced by growMin/growMax to be
43744 // compatible with TextField growing config values
43745 if(this.minHeight !== undefined){
43746 this.growMin = this.minHeight;
43748 if(this.maxHeight !== undefined){
43749 this.growMax = this.maxHeight;
43753 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
43755 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
43759 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
43763 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
43764 * in the field (equivalent to setting overflow: hidden, defaults to false)
43766 preventScrollbars: false,
43768 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43769 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
43773 onRender : function(ct, position){
43775 this.defaultAutoCreate = {
43777 style:"width:300px;height:60px;",
43778 autocomplete: "new-password"
43781 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
43783 this.textSizeEl = Roo.DomHelper.append(document.body, {
43784 tag: "pre", cls: "x-form-grow-sizer"
43786 if(this.preventScrollbars){
43787 this.el.setStyle("overflow", "hidden");
43789 this.el.setHeight(this.growMin);
43793 onDestroy : function(){
43794 if(this.textSizeEl){
43795 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
43797 Roo.form.TextArea.superclass.onDestroy.call(this);
43801 onKeyUp : function(e){
43802 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
43808 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
43809 * This only takes effect if grow = true, and fires the autosize event if the height changes.
43811 autoSize : function(){
43812 if(!this.grow || !this.textSizeEl){
43816 var v = el.dom.value;
43817 var ts = this.textSizeEl;
43820 ts.appendChild(document.createTextNode(v));
43823 Roo.fly(ts).setWidth(this.el.getWidth());
43825 v = "  ";
43828 v = v.replace(/\n/g, '<p> </p>');
43830 v += " \n ";
43833 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
43834 if(h != this.lastHeight){
43835 this.lastHeight = h;
43836 this.el.setHeight(h);
43837 this.fireEvent("autosize", this, h);
43842 * Ext JS Library 1.1.1
43843 * Copyright(c) 2006-2007, Ext JS, LLC.
43845 * Originally Released Under LGPL - original licence link has changed is not relivant.
43848 * <script type="text/javascript">
43853 * @class Roo.form.NumberField
43854 * @extends Roo.form.TextField
43855 * Numeric text field that provides automatic keystroke filtering and numeric validation.
43857 * Creates a new NumberField
43858 * @param {Object} config Configuration options
43860 Roo.form.NumberField = function(config){
43861 Roo.form.NumberField.superclass.constructor.call(this, config);
43864 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
43866 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
43868 fieldClass: "x-form-field x-form-num-field",
43870 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43872 allowDecimals : true,
43874 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43876 decimalSeparator : ".",
43878 * @cfg {String} thousandSeparator Character(s) to allow as the thousand separator (defaults to '') - set to ',' for example
43880 thousandSeparator : "",
43882 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43884 decimalPrecision : 2,
43886 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43888 allowNegative : true,
43890 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43892 minValue : Number.NEGATIVE_INFINITY,
43894 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43896 maxValue : Number.MAX_VALUE,
43898 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43900 minText : "The minimum value for this field is {0}",
43902 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43904 maxText : "The maximum value for this field is {0}",
43906 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43907 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43909 nanText : "{0} is not a valid number",
43911 hiddenField : false,
43913 onRender : function(ct, position)
43915 Roo.form.TextField.superclass.onRender.call(this, ct, position);
43917 //this.el.dom.removeAttribute('name');
43918 Roo.log("Changing name?");
43919 if (this.thousandSeparator != '') {
43920 this.el.dom.setAttribute('name', this.name + '____hidden___' );
43921 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
43923 this.hiddenField.value = this.value ? this.parseValue(this.value) : '';
43924 this.el.on('blur', this.onBlur, this);
43927 // prevent input submission
43932 onBlur : function(){
43934 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43935 this.el.removeClass(this.focusClass);
43937 this.hasFocus = false;
43938 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43941 var v = this.getValue();
43942 if(String(v) !== String(this.startValue)){
43943 this.setValue( this.parseValue(v));
43944 this.fireEvent('change', this, v, this.startValue);
43946 this.fireEvent("blur", this);
43949 // override name, so that it works with hidden field.
43950 getName: function(){
43951 if (this.thousandSeparator != '') {
43954 return Roo.form.TextField.superclass.getName.call(this);
43957 initEvents : function(){
43959 var allowed = "0123456789";
43960 if(this.allowDecimals){
43961 allowed += this.decimalSeparator;
43963 allowed += this.thousandSeparator;
43964 if(this.allowNegative){
43967 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43968 var keyPress = function(e){
43969 var k = e.getKey();
43970 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43973 var c = e.getCharCode();
43974 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43978 this.el.on("keypress", keyPress, this);
43982 validateValue : function(value){
43983 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
43986 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
43989 var num = this.parseValue(value);
43991 this.markInvalid(String.format(this.nanText, value));
43994 if(num < this.minValue){
43995 this.markInvalid(String.format(this.minText, this.minValue));
43998 if(num > this.maxValue){
43999 this.markInvalid(String.format(this.maxText, this.maxValue));
44005 getValue : function(){
44006 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
44010 parseValue : function(value){
44011 value = parseFloat(String(value).replace(this.decimalSeparator, ".").split(this.thousandSeparator).join(''));
44012 return isNaN(value) ? '' : value;
44016 fixPrecision : function(value){
44017 var nan = isNaN(value);
44018 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44019 return nan ? '' : value;
44021 return parseFloat(value).toFixed(this.decimalPrecision);
44024 setValue : function(v){
44025 v = this.fixPrecision(v);
44026 if(this.thousandSeparator != ''){
44027 v = Roo.util.Format.number(v, this.decimalPrecision, this.thousandSeparator);
44029 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
44030 if (this.hiddenField !== false) {
44031 this.hiddenField.value = v ? this.parseValue(v) : '';
44038 decimalPrecisionFcn : function(v){
44039 return Math.floor(v);
44042 beforeBlur : function(){
44043 var v = this.parseValue(this.getRawValue());
44050 * Ext JS Library 1.1.1
44051 * Copyright(c) 2006-2007, Ext JS, LLC.
44053 * Originally Released Under LGPL - original licence link has changed is not relivant.
44056 * <script type="text/javascript">
44060 * @class Roo.form.DateField
44061 * @extends Roo.form.TriggerField
44062 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44064 * Create a new DateField
44065 * @param {Object} config
44067 Roo.form.DateField = function(config)
44069 Roo.form.DateField.superclass.constructor.call(this, config);
44075 * Fires when a date is selected
44076 * @param {Roo.form.DateField} combo This combo box
44077 * @param {Date} date The date selected
44084 if(typeof this.minValue == "string") {
44085 this.minValue = this.parseDate(this.minValue);
44087 if(typeof this.maxValue == "string") {
44088 this.maxValue = this.parseDate(this.maxValue);
44090 this.ddMatch = null;
44091 if(this.disabledDates){
44092 var dd = this.disabledDates;
44094 for(var i = 0; i < dd.length; i++){
44096 if(i != dd.length-1) {
44100 this.ddMatch = new RegExp(re + ")");
44104 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
44106 * @cfg {String} format
44107 * The default date format string which can be overriden for localization support. The format must be
44108 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44112 * @cfg {String} altFormats
44113 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44114 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44116 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
44118 * @cfg {Array} disabledDays
44119 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44121 disabledDays : null,
44123 * @cfg {String} disabledDaysText
44124 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44126 disabledDaysText : "Disabled",
44128 * @cfg {Array} disabledDates
44129 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44130 * expression so they are very powerful. Some examples:
44132 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44133 * <li>["03/08", "09/16"] would disable those days for every year</li>
44134 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44135 * <li>["03/../2006"] would disable every day in March 2006</li>
44136 * <li>["^03"] would disable every day in every March</li>
44138 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44139 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44141 disabledDates : null,
44143 * @cfg {String} disabledDatesText
44144 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44146 disabledDatesText : "Disabled",
44150 * @cfg {Date/String} zeroValue
44151 * if the date is less that this number, then the field is rendered as empty
44154 zeroValue : '1800-01-01',
44158 * @cfg {Date/String} minValue
44159 * The minimum allowed date. Can be either a Javascript date object or a string date in a
44160 * valid format (defaults to null).
44164 * @cfg {Date/String} maxValue
44165 * The maximum allowed date. Can be either a Javascript date object or a string date in a
44166 * valid format (defaults to null).
44170 * @cfg {String} minText
44171 * The error text to display when the date in the cell is before minValue (defaults to
44172 * 'The date in this field must be after {minValue}').
44174 minText : "The date in this field must be equal to or after {0}",
44176 * @cfg {String} maxText
44177 * The error text to display when the date in the cell is after maxValue (defaults to
44178 * 'The date in this field must be before {maxValue}').
44180 maxText : "The date in this field must be equal to or before {0}",
44182 * @cfg {String} invalidText
44183 * The error text to display when the date in the field is invalid (defaults to
44184 * '{value} is not a valid date - it must be in the format {format}').
44186 invalidText : "{0} is not a valid date - it must be in the format {1}",
44188 * @cfg {String} triggerClass
44189 * An additional CSS class used to style the trigger button. The trigger will always get the
44190 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44191 * which displays a calendar icon).
44193 triggerClass : 'x-form-date-trigger',
44197 * @cfg {Boolean} useIso
44198 * if enabled, then the date field will use a hidden field to store the
44199 * real value as iso formated date. default (false)
44203 * @cfg {String/Object} autoCreate
44204 * A DomHelper element spec, or true for a default element spec (defaults to
44205 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44208 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
44211 hiddenField: false,
44213 onRender : function(ct, position)
44215 Roo.form.DateField.superclass.onRender.call(this, ct, position);
44217 //this.el.dom.removeAttribute('name');
44218 Roo.log("Changing name?");
44219 this.el.dom.setAttribute('name', this.name + '____hidden___' );
44220 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44222 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44223 // prevent input submission
44224 this.hiddenName = this.name;
44231 validateValue : function(value)
44233 value = this.formatDate(value);
44234 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
44235 Roo.log('super failed');
44238 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44241 var svalue = value;
44242 value = this.parseDate(value);
44244 Roo.log('parse date failed' + svalue);
44245 this.markInvalid(String.format(this.invalidText, svalue, this.format));
44248 var time = value.getTime();
44249 if(this.minValue && time < this.minValue.getTime()){
44250 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44253 if(this.maxValue && time > this.maxValue.getTime()){
44254 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44257 if(this.disabledDays){
44258 var day = value.getDay();
44259 for(var i = 0; i < this.disabledDays.length; i++) {
44260 if(day === this.disabledDays[i]){
44261 this.markInvalid(this.disabledDaysText);
44266 var fvalue = this.formatDate(value);
44267 if(this.ddMatch && this.ddMatch.test(fvalue)){
44268 this.markInvalid(String.format(this.disabledDatesText, fvalue));
44275 // Provides logic to override the default TriggerField.validateBlur which just returns true
44276 validateBlur : function(){
44277 return !this.menu || !this.menu.isVisible();
44280 getName: function()
44282 // returns hidden if it's set..
44283 if (!this.rendered) {return ''};
44284 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
44289 * Returns the current date value of the date field.
44290 * @return {Date} The date value
44292 getValue : function(){
44294 return this.hiddenField ?
44295 this.hiddenField.value :
44296 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
44300 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
44301 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
44302 * (the default format used is "m/d/y").
44305 //All of these calls set the same date value (May 4, 2006)
44307 //Pass a date object:
44308 var dt = new Date('5/4/06');
44309 dateField.setValue(dt);
44311 //Pass a date string (default format):
44312 dateField.setValue('5/4/06');
44314 //Pass a date string (custom format):
44315 dateField.format = 'Y-m-d';
44316 dateField.setValue('2006-5-4');
44318 * @param {String/Date} date The date or valid date string
44320 setValue : function(date){
44321 if (this.hiddenField) {
44322 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
44324 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
44325 // make sure the value field is always stored as a date..
44326 this.value = this.parseDate(date);
44332 parseDate : function(value){
44334 if (value instanceof Date) {
44335 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
44342 if(!value || value instanceof Date){
44345 var v = Date.parseDate(value, this.format);
44346 if (!v && this.useIso) {
44347 v = Date.parseDate(value, 'Y-m-d');
44349 if(!v && this.altFormats){
44350 if(!this.altFormatsArray){
44351 this.altFormatsArray = this.altFormats.split("|");
44353 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
44354 v = Date.parseDate(value, this.altFormatsArray[i]);
44357 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
44364 formatDate : function(date, fmt){
44365 return (!date || !(date instanceof Date)) ?
44366 date : date.dateFormat(fmt || this.format);
44371 select: function(m, d){
44374 this.fireEvent('select', this, d);
44376 show : function(){ // retain focus styling
44380 this.focus.defer(10, this);
44381 var ml = this.menuListeners;
44382 this.menu.un("select", ml.select, this);
44383 this.menu.un("show", ml.show, this);
44384 this.menu.un("hide", ml.hide, this);
44389 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
44390 onTriggerClick : function(){
44391 if(this.disabled || this.readOnly){
44394 if(this.menu == null){
44395 this.menu = new Roo.menu.DateMenu();
44397 Roo.apply(this.menu.picker, {
44398 showClear: this.allowBlank,
44399 minDate : this.minValue,
44400 maxDate : this.maxValue,
44401 disabledDatesRE : this.ddMatch,
44402 disabledDatesText : this.disabledDatesText,
44403 disabledDays : this.disabledDays,
44404 disabledDaysText : this.disabledDaysText,
44405 format : this.useIso ? 'Y-m-d' : this.format,
44406 minText : String.format(this.minText, this.formatDate(this.minValue)),
44407 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
44409 this.menu.on(Roo.apply({}, this.menuListeners, {
44412 this.menu.picker.setValue(this.getValue() || new Date());
44413 this.menu.show(this.el, "tl-bl?");
44416 beforeBlur : function(){
44417 var v = this.parseDate(this.getRawValue());
44427 isDirty : function() {
44428 if(this.disabled) {
44432 if(typeof(this.startValue) === 'undefined'){
44436 return String(this.getValue()) !== String(this.startValue);
44440 cleanLeadingSpace : function(e)
44447 * Ext JS Library 1.1.1
44448 * Copyright(c) 2006-2007, Ext JS, LLC.
44450 * Originally Released Under LGPL - original licence link has changed is not relivant.
44453 * <script type="text/javascript">
44457 * @class Roo.form.MonthField
44458 * @extends Roo.form.TriggerField
44459 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44461 * Create a new MonthField
44462 * @param {Object} config
44464 Roo.form.MonthField = function(config){
44466 Roo.form.MonthField.superclass.constructor.call(this, config);
44472 * Fires when a date is selected
44473 * @param {Roo.form.MonthFieeld} combo This combo box
44474 * @param {Date} date The date selected
44481 if(typeof this.minValue == "string") {
44482 this.minValue = this.parseDate(this.minValue);
44484 if(typeof this.maxValue == "string") {
44485 this.maxValue = this.parseDate(this.maxValue);
44487 this.ddMatch = null;
44488 if(this.disabledDates){
44489 var dd = this.disabledDates;
44491 for(var i = 0; i < dd.length; i++){
44493 if(i != dd.length-1) {
44497 this.ddMatch = new RegExp(re + ")");
44501 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
44503 * @cfg {String} format
44504 * The default date format string which can be overriden for localization support. The format must be
44505 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44509 * @cfg {String} altFormats
44510 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44511 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44513 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
44515 * @cfg {Array} disabledDays
44516 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44518 disabledDays : [0,1,2,3,4,5,6],
44520 * @cfg {String} disabledDaysText
44521 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44523 disabledDaysText : "Disabled",
44525 * @cfg {Array} disabledDates
44526 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44527 * expression so they are very powerful. Some examples:
44529 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44530 * <li>["03/08", "09/16"] would disable those days for every year</li>
44531 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44532 * <li>["03/../2006"] would disable every day in March 2006</li>
44533 * <li>["^03"] would disable every day in every March</li>
44535 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44536 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44538 disabledDates : null,
44540 * @cfg {String} disabledDatesText
44541 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44543 disabledDatesText : "Disabled",
44545 * @cfg {Date/String} minValue
44546 * The minimum allowed date. Can be either a Javascript date object or a string date in a
44547 * valid format (defaults to null).
44551 * @cfg {Date/String} maxValue
44552 * The maximum allowed date. Can be either a Javascript date object or a string date in a
44553 * valid format (defaults to null).
44557 * @cfg {String} minText
44558 * The error text to display when the date in the cell is before minValue (defaults to
44559 * 'The date in this field must be after {minValue}').
44561 minText : "The date in this field must be equal to or after {0}",
44563 * @cfg {String} maxTextf
44564 * The error text to display when the date in the cell is after maxValue (defaults to
44565 * 'The date in this field must be before {maxValue}').
44567 maxText : "The date in this field must be equal to or before {0}",
44569 * @cfg {String} invalidText
44570 * The error text to display when the date in the field is invalid (defaults to
44571 * '{value} is not a valid date - it must be in the format {format}').
44573 invalidText : "{0} is not a valid date - it must be in the format {1}",
44575 * @cfg {String} triggerClass
44576 * An additional CSS class used to style the trigger button. The trigger will always get the
44577 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44578 * which displays a calendar icon).
44580 triggerClass : 'x-form-date-trigger',
44584 * @cfg {Boolean} useIso
44585 * if enabled, then the date field will use a hidden field to store the
44586 * real value as iso formated date. default (true)
44590 * @cfg {String/Object} autoCreate
44591 * A DomHelper element spec, or true for a default element spec (defaults to
44592 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44595 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
44598 hiddenField: false,
44600 hideMonthPicker : false,
44602 onRender : function(ct, position)
44604 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
44606 this.el.dom.removeAttribute('name');
44607 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44609 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44610 // prevent input submission
44611 this.hiddenName = this.name;
44618 validateValue : function(value)
44620 value = this.formatDate(value);
44621 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
44624 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44627 var svalue = value;
44628 value = this.parseDate(value);
44630 this.markInvalid(String.format(this.invalidText, svalue, this.format));
44633 var time = value.getTime();
44634 if(this.minValue && time < this.minValue.getTime()){
44635 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44638 if(this.maxValue && time > this.maxValue.getTime()){
44639 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44642 /*if(this.disabledDays){
44643 var day = value.getDay();
44644 for(var i = 0; i < this.disabledDays.length; i++) {
44645 if(day === this.disabledDays[i]){
44646 this.markInvalid(this.disabledDaysText);
44652 var fvalue = this.formatDate(value);
44653 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
44654 this.markInvalid(String.format(this.disabledDatesText, fvalue));
44662 // Provides logic to override the default TriggerField.validateBlur which just returns true
44663 validateBlur : function(){
44664 return !this.menu || !this.menu.isVisible();
44668 * Returns the current date value of the date field.
44669 * @return {Date} The date value
44671 getValue : function(){
44675 return this.hiddenField ?
44676 this.hiddenField.value :
44677 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
44681 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
44682 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
44683 * (the default format used is "m/d/y").
44686 //All of these calls set the same date value (May 4, 2006)
44688 //Pass a date object:
44689 var dt = new Date('5/4/06');
44690 monthField.setValue(dt);
44692 //Pass a date string (default format):
44693 monthField.setValue('5/4/06');
44695 //Pass a date string (custom format):
44696 monthField.format = 'Y-m-d';
44697 monthField.setValue('2006-5-4');
44699 * @param {String/Date} date The date or valid date string
44701 setValue : function(date){
44702 Roo.log('month setValue' + date);
44703 // can only be first of month..
44705 var val = this.parseDate(date);
44707 if (this.hiddenField) {
44708 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
44710 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
44711 this.value = this.parseDate(date);
44715 parseDate : function(value){
44716 if(!value || value instanceof Date){
44717 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
44720 var v = Date.parseDate(value, this.format);
44721 if (!v && this.useIso) {
44722 v = Date.parseDate(value, 'Y-m-d');
44726 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
44730 if(!v && this.altFormats){
44731 if(!this.altFormatsArray){
44732 this.altFormatsArray = this.altFormats.split("|");
44734 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
44735 v = Date.parseDate(value, this.altFormatsArray[i]);
44742 formatDate : function(date, fmt){
44743 return (!date || !(date instanceof Date)) ?
44744 date : date.dateFormat(fmt || this.format);
44749 select: function(m, d){
44751 this.fireEvent('select', this, d);
44753 show : function(){ // retain focus styling
44757 this.focus.defer(10, this);
44758 var ml = this.menuListeners;
44759 this.menu.un("select", ml.select, this);
44760 this.menu.un("show", ml.show, this);
44761 this.menu.un("hide", ml.hide, this);
44765 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
44766 onTriggerClick : function(){
44770 if(this.menu == null){
44771 this.menu = new Roo.menu.DateMenu();
44775 Roo.apply(this.menu.picker, {
44777 showClear: this.allowBlank,
44778 minDate : this.minValue,
44779 maxDate : this.maxValue,
44780 disabledDatesRE : this.ddMatch,
44781 disabledDatesText : this.disabledDatesText,
44783 format : this.useIso ? 'Y-m-d' : this.format,
44784 minText : String.format(this.minText, this.formatDate(this.minValue)),
44785 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
44788 this.menu.on(Roo.apply({}, this.menuListeners, {
44796 // hide month picker get's called when we called by 'before hide';
44798 var ignorehide = true;
44799 p.hideMonthPicker = function(disableAnim){
44803 if(this.monthPicker){
44804 Roo.log("hideMonthPicker called");
44805 if(disableAnim === true){
44806 this.monthPicker.hide();
44808 this.monthPicker.slideOut('t', {duration:.2});
44809 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
44810 p.fireEvent("select", this, this.value);
44816 Roo.log('picker set value');
44817 Roo.log(this.getValue());
44818 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
44819 m.show(this.el, 'tl-bl?');
44820 ignorehide = false;
44821 // this will trigger hideMonthPicker..
44824 // hidden the day picker
44825 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
44831 p.showMonthPicker.defer(100, p);
44837 beforeBlur : function(){
44838 var v = this.parseDate(this.getRawValue());
44844 /** @cfg {Boolean} grow @hide */
44845 /** @cfg {Number} growMin @hide */
44846 /** @cfg {Number} growMax @hide */
44853 * Ext JS Library 1.1.1
44854 * Copyright(c) 2006-2007, Ext JS, LLC.
44856 * Originally Released Under LGPL - original licence link has changed is not relivant.
44859 * <script type="text/javascript">
44864 * @class Roo.form.ComboBox
44865 * @extends Roo.form.TriggerField
44866 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
44868 * Create a new ComboBox.
44869 * @param {Object} config Configuration options
44871 Roo.form.ComboBox = function(config){
44872 Roo.form.ComboBox.superclass.constructor.call(this, config);
44876 * Fires when the dropdown list is expanded
44877 * @param {Roo.form.ComboBox} combo This combo box
44882 * Fires when the dropdown list is collapsed
44883 * @param {Roo.form.ComboBox} combo This combo box
44887 * @event beforeselect
44888 * Fires before a list item is selected. Return false to cancel the selection.
44889 * @param {Roo.form.ComboBox} combo This combo box
44890 * @param {Roo.data.Record} record The data record returned from the underlying store
44891 * @param {Number} index The index of the selected item in the dropdown list
44893 'beforeselect' : true,
44896 * Fires when a list item is selected
44897 * @param {Roo.form.ComboBox} combo This combo box
44898 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
44899 * @param {Number} index The index of the selected item in the dropdown list
44903 * @event beforequery
44904 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
44905 * The event object passed has these properties:
44906 * @param {Roo.form.ComboBox} combo This combo box
44907 * @param {String} query The query
44908 * @param {Boolean} forceAll true to force "all" query
44909 * @param {Boolean} cancel true to cancel the query
44910 * @param {Object} e The query event object
44912 'beforequery': true,
44915 * Fires when the 'add' icon is pressed (add a listener to enable add button)
44916 * @param {Roo.form.ComboBox} combo This combo box
44921 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
44922 * @param {Roo.form.ComboBox} combo This combo box
44923 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
44929 if(this.transform){
44930 this.allowDomMove = false;
44931 var s = Roo.getDom(this.transform);
44932 if(!this.hiddenName){
44933 this.hiddenName = s.name;
44936 this.mode = 'local';
44937 var d = [], opts = s.options;
44938 for(var i = 0, len = opts.length;i < len; i++){
44940 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
44942 this.value = value;
44944 d.push([value, o.text]);
44946 this.store = new Roo.data.SimpleStore({
44948 fields: ['value', 'text'],
44951 this.valueField = 'value';
44952 this.displayField = 'text';
44954 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
44955 if(!this.lazyRender){
44956 this.target = true;
44957 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
44958 s.parentNode.removeChild(s); // remove it
44959 this.render(this.el.parentNode);
44961 s.parentNode.removeChild(s); // remove it
44966 this.store = Roo.factory(this.store, Roo.data);
44969 this.selectedIndex = -1;
44970 if(this.mode == 'local'){
44971 if(config.queryDelay === undefined){
44972 this.queryDelay = 10;
44974 if(config.minChars === undefined){
44980 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
44982 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
44985 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
44986 * rendering into an Roo.Editor, defaults to false)
44989 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
44990 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
44993 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
44996 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
44997 * the dropdown list (defaults to undefined, with no header element)
45001 * @cfg {String/Roo.Template} tpl The template to use to render the output
45005 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
45007 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
45009 listWidth: undefined,
45011 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
45012 * mode = 'remote' or 'text' if mode = 'local')
45014 displayField: undefined,
45016 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
45017 * mode = 'remote' or 'value' if mode = 'local').
45018 * Note: use of a valueField requires the user make a selection
45019 * in order for a value to be mapped.
45021 valueField: undefined,
45025 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
45026 * field's data value (defaults to the underlying DOM element's name)
45028 hiddenName: undefined,
45030 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
45034 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
45036 selectedClass: 'x-combo-selected',
45038 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
45039 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
45040 * which displays a downward arrow icon).
45042 triggerClass : 'x-form-arrow-trigger',
45044 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
45048 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
45049 * anchor positions (defaults to 'tl-bl')
45051 listAlign: 'tl-bl?',
45053 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
45057 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
45058 * query specified by the allQuery config option (defaults to 'query')
45060 triggerAction: 'query',
45062 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
45063 * (defaults to 4, does not apply if editable = false)
45067 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
45068 * delay (typeAheadDelay) if it matches a known value (defaults to false)
45072 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
45073 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
45077 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
45078 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
45082 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
45083 * when editable = true (defaults to false)
45085 selectOnFocus:false,
45087 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
45089 queryParam: 'query',
45091 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
45092 * when mode = 'remote' (defaults to 'Loading...')
45094 loadingText: 'Loading...',
45096 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
45100 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
45104 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
45105 * traditional select (defaults to true)
45109 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
45113 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
45117 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
45118 * listWidth has a higher value)
45122 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
45123 * allow the user to set arbitrary text into the field (defaults to false)
45125 forceSelection:false,
45127 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
45128 * if typeAhead = true (defaults to 250)
45130 typeAheadDelay : 250,
45132 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
45133 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
45135 valueNotFoundText : undefined,
45137 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
45139 blockFocus : false,
45142 * @cfg {Boolean} disableClear Disable showing of clear button.
45144 disableClear : false,
45146 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
45148 alwaysQuery : false,
45154 // element that contains real text value.. (when hidden is used..)
45157 onRender : function(ct, position)
45159 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
45161 if(this.hiddenName){
45162 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
45164 this.hiddenField.value =
45165 this.hiddenValue !== undefined ? this.hiddenValue :
45166 this.value !== undefined ? this.value : '';
45168 // prevent input submission
45169 this.el.dom.removeAttribute('name');
45175 this.el.dom.setAttribute('autocomplete', 'off');
45178 var cls = 'x-combo-list';
45180 this.list = new Roo.Layer({
45181 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
45184 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
45185 this.list.setWidth(lw);
45186 this.list.swallowEvent('mousewheel');
45187 this.assetHeight = 0;
45190 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
45191 this.assetHeight += this.header.getHeight();
45194 this.innerList = this.list.createChild({cls:cls+'-inner'});
45195 this.innerList.on('mouseover', this.onViewOver, this);
45196 this.innerList.on('mousemove', this.onViewMove, this);
45197 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45199 if(this.allowBlank && !this.pageSize && !this.disableClear){
45200 this.footer = this.list.createChild({cls:cls+'-ft'});
45201 this.pageTb = new Roo.Toolbar(this.footer);
45205 this.footer = this.list.createChild({cls:cls+'-ft'});
45206 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
45207 {pageSize: this.pageSize});
45211 if (this.pageTb && this.allowBlank && !this.disableClear) {
45213 this.pageTb.add(new Roo.Toolbar.Fill(), {
45214 cls: 'x-btn-icon x-btn-clear',
45216 handler: function()
45219 _this.clearValue();
45220 _this.onSelect(false, -1);
45225 this.assetHeight += this.footer.getHeight();
45230 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
45233 this.view = new Roo.View(this.innerList, this.tpl, {
45236 selectedClass: this.selectedClass
45239 this.view.on('click', this.onViewClick, this);
45241 this.store.on('beforeload', this.onBeforeLoad, this);
45242 this.store.on('load', this.onLoad, this);
45243 this.store.on('loadexception', this.onLoadException, this);
45245 if(this.resizable){
45246 this.resizer = new Roo.Resizable(this.list, {
45247 pinned:true, handles:'se'
45249 this.resizer.on('resize', function(r, w, h){
45250 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
45251 this.listWidth = w;
45252 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
45253 this.restrictHeight();
45255 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
45257 if(!this.editable){
45258 this.editable = true;
45259 this.setEditable(false);
45263 if (typeof(this.events.add.listeners) != 'undefined') {
45265 this.addicon = this.wrap.createChild(
45266 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
45268 this.addicon.on('click', function(e) {
45269 this.fireEvent('add', this);
45272 if (typeof(this.events.edit.listeners) != 'undefined') {
45274 this.editicon = this.wrap.createChild(
45275 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
45276 if (this.addicon) {
45277 this.editicon.setStyle('margin-left', '40px');
45279 this.editicon.on('click', function(e) {
45281 // we fire even if inothing is selected..
45282 this.fireEvent('edit', this, this.lastData );
45292 initEvents : function(){
45293 Roo.form.ComboBox.superclass.initEvents.call(this);
45295 this.keyNav = new Roo.KeyNav(this.el, {
45296 "up" : function(e){
45297 this.inKeyMode = true;
45301 "down" : function(e){
45302 if(!this.isExpanded()){
45303 this.onTriggerClick();
45305 this.inKeyMode = true;
45310 "enter" : function(e){
45311 this.onViewClick();
45315 "esc" : function(e){
45319 "tab" : function(e){
45320 this.onViewClick(false);
45321 this.fireEvent("specialkey", this, e);
45327 doRelay : function(foo, bar, hname){
45328 if(hname == 'down' || this.scope.isExpanded()){
45329 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45336 this.queryDelay = Math.max(this.queryDelay || 10,
45337 this.mode == 'local' ? 10 : 250);
45338 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
45339 if(this.typeAhead){
45340 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
45342 if(this.editable !== false){
45343 this.el.on("keyup", this.onKeyUp, this);
45345 if(this.forceSelection){
45346 this.on('blur', this.doForce, this);
45350 onDestroy : function(){
45352 this.view.setStore(null);
45353 this.view.el.removeAllListeners();
45354 this.view.el.remove();
45355 this.view.purgeListeners();
45358 this.list.destroy();
45361 this.store.un('beforeload', this.onBeforeLoad, this);
45362 this.store.un('load', this.onLoad, this);
45363 this.store.un('loadexception', this.onLoadException, this);
45365 Roo.form.ComboBox.superclass.onDestroy.call(this);
45369 fireKey : function(e){
45370 if(e.isNavKeyPress() && !this.list.isVisible()){
45371 this.fireEvent("specialkey", this, e);
45376 onResize: function(w, h){
45377 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
45379 if(typeof w != 'number'){
45380 // we do not handle it!?!?
45383 var tw = this.trigger.getWidth();
45384 tw += this.addicon ? this.addicon.getWidth() : 0;
45385 tw += this.editicon ? this.editicon.getWidth() : 0;
45387 this.el.setWidth( this.adjustWidth('input', x));
45389 this.trigger.setStyle('left', x+'px');
45391 if(this.list && this.listWidth === undefined){
45392 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
45393 this.list.setWidth(lw);
45394 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45402 * Allow or prevent the user from directly editing the field text. If false is passed,
45403 * the user will only be able to select from the items defined in the dropdown list. This method
45404 * is the runtime equivalent of setting the 'editable' config option at config time.
45405 * @param {Boolean} value True to allow the user to directly edit the field text
45407 setEditable : function(value){
45408 if(value == this.editable){
45411 this.editable = value;
45413 this.el.dom.setAttribute('readOnly', true);
45414 this.el.on('mousedown', this.onTriggerClick, this);
45415 this.el.addClass('x-combo-noedit');
45417 this.el.dom.setAttribute('readOnly', false);
45418 this.el.un('mousedown', this.onTriggerClick, this);
45419 this.el.removeClass('x-combo-noedit');
45424 onBeforeLoad : function(){
45425 if(!this.hasFocus){
45428 this.innerList.update(this.loadingText ?
45429 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
45430 this.restrictHeight();
45431 this.selectedIndex = -1;
45435 onLoad : function(){
45436 if(!this.hasFocus){
45439 if(this.store.getCount() > 0){
45441 this.restrictHeight();
45442 if(this.lastQuery == this.allQuery){
45444 this.el.dom.select();
45446 if(!this.selectByValue(this.value, true)){
45447 this.select(0, true);
45451 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
45452 this.taTask.delay(this.typeAheadDelay);
45456 this.onEmptyResults();
45461 onLoadException : function()
45464 Roo.log(this.store.reader.jsonData);
45465 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
45466 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
45472 onTypeAhead : function(){
45473 if(this.store.getCount() > 0){
45474 var r = this.store.getAt(0);
45475 var newValue = r.data[this.displayField];
45476 var len = newValue.length;
45477 var selStart = this.getRawValue().length;
45478 if(selStart != len){
45479 this.setRawValue(newValue);
45480 this.selectText(selStart, newValue.length);
45486 onSelect : function(record, index){
45487 if(this.fireEvent('beforeselect', this, record, index) !== false){
45488 this.setFromData(index > -1 ? record.data : false);
45490 this.fireEvent('select', this, record, index);
45495 * Returns the currently selected field value or empty string if no value is set.
45496 * @return {String} value The selected value
45498 getValue : function(){
45499 if(this.valueField){
45500 return typeof this.value != 'undefined' ? this.value : '';
45502 return Roo.form.ComboBox.superclass.getValue.call(this);
45506 * Clears any text/value currently set in the field
45508 clearValue : function(){
45509 if(this.hiddenField){
45510 this.hiddenField.value = '';
45513 this.setRawValue('');
45514 this.lastSelectionText = '';
45519 * Sets the specified value into the field. If the value finds a match, the corresponding record text
45520 * will be displayed in the field. If the value does not match the data value of an existing item,
45521 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
45522 * Otherwise the field will be blank (although the value will still be set).
45523 * @param {String} value The value to match
45525 setValue : function(v){
45527 if(this.valueField){
45528 var r = this.findRecord(this.valueField, v);
45530 text = r.data[this.displayField];
45531 }else if(this.valueNotFoundText !== undefined){
45532 text = this.valueNotFoundText;
45535 this.lastSelectionText = text;
45536 if(this.hiddenField){
45537 this.hiddenField.value = v;
45539 Roo.form.ComboBox.superclass.setValue.call(this, text);
45543 * @property {Object} the last set data for the element
45548 * Sets the value of the field based on a object which is related to the record format for the store.
45549 * @param {Object} value the value to set as. or false on reset?
45551 setFromData : function(o){
45552 var dv = ''; // display value
45553 var vv = ''; // value value..
45555 if (this.displayField) {
45556 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
45558 // this is an error condition!!!
45559 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
45562 if(this.valueField){
45563 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
45565 if(this.hiddenField){
45566 this.hiddenField.value = vv;
45568 this.lastSelectionText = dv;
45569 Roo.form.ComboBox.superclass.setValue.call(this, dv);
45573 // no hidden field.. - we store the value in 'value', but still display
45574 // display field!!!!
45575 this.lastSelectionText = dv;
45576 Roo.form.ComboBox.superclass.setValue.call(this, dv);
45582 reset : function(){
45583 // overridden so that last data is reset..
45584 this.setValue(this.resetValue);
45585 this.originalValue = this.getValue();
45586 this.clearInvalid();
45587 this.lastData = false;
45589 this.view.clearSelections();
45593 findRecord : function(prop, value){
45595 if(this.store.getCount() > 0){
45596 this.store.each(function(r){
45597 if(r.data[prop] == value){
45607 getName: function()
45609 // returns hidden if it's set..
45610 if (!this.rendered) {return ''};
45611 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
45615 onViewMove : function(e, t){
45616 this.inKeyMode = false;
45620 onViewOver : function(e, t){
45621 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
45624 var item = this.view.findItemFromChild(t);
45626 var index = this.view.indexOf(item);
45627 this.select(index, false);
45632 onViewClick : function(doFocus)
45634 var index = this.view.getSelectedIndexes()[0];
45635 var r = this.store.getAt(index);
45637 this.onSelect(r, index);
45639 if(doFocus !== false && !this.blockFocus){
45645 restrictHeight : function(){
45646 this.innerList.dom.style.height = '';
45647 var inner = this.innerList.dom;
45648 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
45649 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
45650 this.list.beginUpdate();
45651 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
45652 this.list.alignTo(this.el, this.listAlign);
45653 this.list.endUpdate();
45657 onEmptyResults : function(){
45662 * Returns true if the dropdown list is expanded, else false.
45664 isExpanded : function(){
45665 return this.list.isVisible();
45669 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
45670 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45671 * @param {String} value The data value of the item to select
45672 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45673 * selected item if it is not currently in view (defaults to true)
45674 * @return {Boolean} True if the value matched an item in the list, else false
45676 selectByValue : function(v, scrollIntoView){
45677 if(v !== undefined && v !== null){
45678 var r = this.findRecord(this.valueField || this.displayField, v);
45680 this.select(this.store.indexOf(r), scrollIntoView);
45688 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
45689 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45690 * @param {Number} index The zero-based index of the list item to select
45691 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45692 * selected item if it is not currently in view (defaults to true)
45694 select : function(index, scrollIntoView){
45695 this.selectedIndex = index;
45696 this.view.select(index);
45697 if(scrollIntoView !== false){
45698 var el = this.view.getNode(index);
45700 this.innerList.scrollChildIntoView(el, false);
45706 selectNext : function(){
45707 var ct = this.store.getCount();
45709 if(this.selectedIndex == -1){
45711 }else if(this.selectedIndex < ct-1){
45712 this.select(this.selectedIndex+1);
45718 selectPrev : function(){
45719 var ct = this.store.getCount();
45721 if(this.selectedIndex == -1){
45723 }else if(this.selectedIndex != 0){
45724 this.select(this.selectedIndex-1);
45730 onKeyUp : function(e){
45731 if(this.editable !== false && !e.isSpecialKey()){
45732 this.lastKey = e.getKey();
45733 this.dqTask.delay(this.queryDelay);
45738 validateBlur : function(){
45739 return !this.list || !this.list.isVisible();
45743 initQuery : function(){
45744 this.doQuery(this.getRawValue());
45748 doForce : function(){
45749 if(this.el.dom.value.length > 0){
45750 this.el.dom.value =
45751 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
45757 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
45758 * query allowing the query action to be canceled if needed.
45759 * @param {String} query The SQL query to execute
45760 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
45761 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
45762 * saved in the current store (defaults to false)
45764 doQuery : function(q, forceAll){
45765 if(q === undefined || q === null){
45770 forceAll: forceAll,
45774 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
45778 forceAll = qe.forceAll;
45779 if(forceAll === true || (q.length >= this.minChars)){
45780 if(this.lastQuery != q || this.alwaysQuery){
45781 this.lastQuery = q;
45782 if(this.mode == 'local'){
45783 this.selectedIndex = -1;
45785 this.store.clearFilter();
45787 this.store.filter(this.displayField, q);
45791 this.store.baseParams[this.queryParam] = q;
45793 params: this.getParams(q)
45798 this.selectedIndex = -1;
45805 getParams : function(q){
45807 //p[this.queryParam] = q;
45810 p.limit = this.pageSize;
45816 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
45818 collapse : function(){
45819 if(!this.isExpanded()){
45823 Roo.get(document).un('mousedown', this.collapseIf, this);
45824 Roo.get(document).un('mousewheel', this.collapseIf, this);
45825 if (!this.editable) {
45826 Roo.get(document).un('keydown', this.listKeyPress, this);
45828 this.fireEvent('collapse', this);
45832 collapseIf : function(e){
45833 if(!e.within(this.wrap) && !e.within(this.list)){
45839 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
45841 expand : function(){
45842 if(this.isExpanded() || !this.hasFocus){
45845 this.list.alignTo(this.el, this.listAlign);
45847 Roo.get(document).on('mousedown', this.collapseIf, this);
45848 Roo.get(document).on('mousewheel', this.collapseIf, this);
45849 if (!this.editable) {
45850 Roo.get(document).on('keydown', this.listKeyPress, this);
45853 this.fireEvent('expand', this);
45857 // Implements the default empty TriggerField.onTriggerClick function
45858 onTriggerClick : function(){
45862 if(this.isExpanded()){
45864 if (!this.blockFocus) {
45869 this.hasFocus = true;
45870 if(this.triggerAction == 'all') {
45871 this.doQuery(this.allQuery, true);
45873 this.doQuery(this.getRawValue());
45875 if (!this.blockFocus) {
45880 listKeyPress : function(e)
45882 //Roo.log('listkeypress');
45883 // scroll to first matching element based on key pres..
45884 if (e.isSpecialKey()) {
45887 var k = String.fromCharCode(e.getKey()).toUpperCase();
45890 var csel = this.view.getSelectedNodes();
45891 var cselitem = false;
45893 var ix = this.view.indexOf(csel[0]);
45894 cselitem = this.store.getAt(ix);
45895 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
45901 this.store.each(function(v) {
45903 // start at existing selection.
45904 if (cselitem.id == v.id) {
45910 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
45911 match = this.store.indexOf(v);
45916 if (match === false) {
45917 return true; // no more action?
45920 this.view.select(match);
45921 var sn = Roo.get(this.view.getSelectedNodes()[0]);
45922 sn.scrollIntoView(sn.dom.parentNode, false);
45924 cleanLeadingSpace : function()
45926 // override textfield strip white space (trigers set on blur)
45930 * @cfg {Boolean} grow
45934 * @cfg {Number} growMin
45938 * @cfg {Number} growMax
45946 * Copyright(c) 2010-2012, Roo J Solutions Limited
45953 * @class Roo.form.ComboBoxArray
45954 * @extends Roo.form.TextField
45955 * A facebook style adder... for lists of email / people / countries etc...
45956 * pick multiple items from a combo box, and shows each one.
45958 * Fred [x] Brian [x] [Pick another |v]
45961 * For this to work: it needs various extra information
45962 * - normal combo problay has
45964 * + displayField, valueField
45966 * For our purpose...
45969 * If we change from 'extends' to wrapping...
45976 * Create a new ComboBoxArray.
45977 * @param {Object} config Configuration options
45981 Roo.form.ComboBoxArray = function(config)
45985 * @event beforeremove
45986 * Fires before remove the value from the list
45987 * @param {Roo.form.ComboBoxArray} _self This combo box array
45988 * @param {Roo.form.ComboBoxArray.Item} item removed item
45990 'beforeremove' : true,
45993 * Fires when remove the value from the list
45994 * @param {Roo.form.ComboBoxArray} _self This combo box array
45995 * @param {Roo.form.ComboBoxArray.Item} item removed item
46002 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
46004 this.items = new Roo.util.MixedCollection(false);
46006 // construct the child combo...
46016 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
46019 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
46024 // behavies liek a hiddne field
46025 inputType: 'hidden',
46027 * @cfg {Number} width The width of the box that displays the selected element
46034 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
46038 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
46040 hiddenName : false,
46042 * @cfg {String} seperator The value seperator normally ','
46047 // private the array of items that are displayed..
46049 // private - the hidden field el.
46051 // private - the filed el..
46054 //validateValue : function() { return true; }, // all values are ok!
46055 //onAddClick: function() { },
46057 onRender : function(ct, position)
46060 // create the standard hidden element
46061 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
46064 // give fake names to child combo;
46065 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
46066 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
46068 this.combo = Roo.factory(this.combo, Roo.form);
46069 this.combo.onRender(ct, position);
46070 if (typeof(this.combo.width) != 'undefined') {
46071 this.combo.onResize(this.combo.width,0);
46074 this.combo.initEvents();
46076 // assigned so form know we need to do this..
46077 this.store = this.combo.store;
46078 this.valueField = this.combo.valueField;
46079 this.displayField = this.combo.displayField ;
46082 this.combo.wrap.addClass('x-cbarray-grp');
46084 var cbwrap = this.combo.wrap.createChild(
46085 {tag: 'div', cls: 'x-cbarray-cb'},
46090 this.hiddenEl = this.combo.wrap.createChild({
46091 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
46093 this.el = this.combo.wrap.createChild({
46094 tag: 'input', type:'hidden' , name: this.name, value : ''
46096 // this.el.dom.removeAttribute("name");
46099 this.outerWrap = this.combo.wrap;
46100 this.wrap = cbwrap;
46102 this.outerWrap.setWidth(this.width);
46103 this.outerWrap.dom.removeChild(this.el.dom);
46105 this.wrap.dom.appendChild(this.el.dom);
46106 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
46107 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
46109 this.combo.trigger.setStyle('position','relative');
46110 this.combo.trigger.setStyle('left', '0px');
46111 this.combo.trigger.setStyle('top', '2px');
46113 this.combo.el.setStyle('vertical-align', 'text-bottom');
46115 //this.trigger.setStyle('vertical-align', 'top');
46117 // this should use the code from combo really... on('add' ....)
46121 this.adder = this.outerWrap.createChild(
46122 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
46124 this.adder.on('click', function(e) {
46125 _t.fireEvent('adderclick', this, e);
46129 //this.adder.on('click', this.onAddClick, _t);
46132 this.combo.on('select', function(cb, rec, ix) {
46133 this.addItem(rec.data);
46136 cb.el.dom.value = '';
46137 //cb.lastData = rec.data;
46148 getName: function()
46150 // returns hidden if it's set..
46151 if (!this.rendered) {return ''};
46152 return this.hiddenName ? this.hiddenName : this.name;
46157 onResize: function(w, h){
46160 // not sure if this is needed..
46161 //this.combo.onResize(w,h);
46163 if(typeof w != 'number'){
46164 // we do not handle it!?!?
46167 var tw = this.combo.trigger.getWidth();
46168 tw += this.addicon ? this.addicon.getWidth() : 0;
46169 tw += this.editicon ? this.editicon.getWidth() : 0;
46171 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
46173 this.combo.trigger.setStyle('left', '0px');
46175 if(this.list && this.listWidth === undefined){
46176 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
46177 this.list.setWidth(lw);
46178 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
46185 addItem: function(rec)
46187 var valueField = this.combo.valueField;
46188 var displayField = this.combo.displayField;
46190 if (this.items.indexOfKey(rec[valueField]) > -1) {
46191 //console.log("GOT " + rec.data.id);
46195 var x = new Roo.form.ComboBoxArray.Item({
46196 //id : rec[this.idField],
46198 displayField : displayField ,
46199 tipField : displayField ,
46203 this.items.add(rec[valueField],x);
46204 // add it before the element..
46205 this.updateHiddenEl();
46206 x.render(this.outerWrap, this.wrap.dom);
46207 // add the image handler..
46210 updateHiddenEl : function()
46213 if (!this.hiddenEl) {
46217 var idField = this.combo.valueField;
46219 this.items.each(function(f) {
46220 ar.push(f.data[idField]);
46222 this.hiddenEl.dom.value = ar.join(this.seperator);
46228 this.items.clear();
46230 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
46234 this.el.dom.value = '';
46235 if (this.hiddenEl) {
46236 this.hiddenEl.dom.value = '';
46240 getValue: function()
46242 return this.hiddenEl ? this.hiddenEl.dom.value : '';
46244 setValue: function(v) // not a valid action - must use addItems..
46249 if (this.store.isLocal && (typeof(v) == 'string')) {
46250 // then we can use the store to find the values..
46251 // comma seperated at present.. this needs to allow JSON based encoding..
46252 this.hiddenEl.value = v;
46254 Roo.each(v.split(this.seperator), function(k) {
46255 Roo.log("CHECK " + this.valueField + ',' + k);
46256 var li = this.store.query(this.valueField, k);
46261 add[this.valueField] = k;
46262 add[this.displayField] = li.item(0).data[this.displayField];
46268 if (typeof(v) == 'object' ) {
46269 // then let's assume it's an array of objects..
46270 Roo.each(v, function(l) {
46272 if (typeof(l) == 'string') {
46274 add[this.valueField] = l;
46275 add[this.displayField] = l
46284 setFromData: function(v)
46286 // this recieves an object, if setValues is called.
46288 this.el.dom.value = v[this.displayField];
46289 this.hiddenEl.dom.value = v[this.valueField];
46290 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
46293 var kv = v[this.valueField];
46294 var dv = v[this.displayField];
46295 kv = typeof(kv) != 'string' ? '' : kv;
46296 dv = typeof(dv) != 'string' ? '' : dv;
46299 var keys = kv.split(this.seperator);
46300 var display = dv.split(this.seperator);
46301 for (var i = 0 ; i < keys.length; i++) {
46303 add[this.valueField] = keys[i];
46304 add[this.displayField] = display[i];
46312 * Validates the combox array value
46313 * @return {Boolean} True if the value is valid, else false
46315 validate : function(){
46316 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
46317 this.clearInvalid();
46323 validateValue : function(value){
46324 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
46332 isDirty : function() {
46333 if(this.disabled) {
46338 var d = Roo.decode(String(this.originalValue));
46340 return String(this.getValue()) !== String(this.originalValue);
46343 var originalValue = [];
46345 for (var i = 0; i < d.length; i++){
46346 originalValue.push(d[i][this.valueField]);
46349 return String(this.getValue()) !== String(originalValue.join(this.seperator));
46358 * @class Roo.form.ComboBoxArray.Item
46359 * @extends Roo.BoxComponent
46360 * A selected item in the list
46361 * Fred [x] Brian [x] [Pick another |v]
46364 * Create a new item.
46365 * @param {Object} config Configuration options
46368 Roo.form.ComboBoxArray.Item = function(config) {
46369 config.id = Roo.id();
46370 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
46373 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
46376 displayField : false,
46380 defaultAutoCreate : {
46382 cls: 'x-cbarray-item',
46389 src : Roo.BLANK_IMAGE_URL ,
46397 onRender : function(ct, position)
46399 Roo.form.Field.superclass.onRender.call(this, ct, position);
46402 var cfg = this.getAutoCreate();
46403 this.el = ct.createChild(cfg, position);
46406 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
46408 this.el.child('div').dom.innerHTML = this.cb.renderer ?
46409 this.cb.renderer(this.data) :
46410 String.format('{0}',this.data[this.displayField]);
46413 this.el.child('div').dom.setAttribute('qtip',
46414 String.format('{0}',this.data[this.tipField])
46417 this.el.child('img').on('click', this.remove, this);
46421 remove : function()
46423 if(this.cb.disabled){
46427 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
46428 this.cb.items.remove(this);
46429 this.el.child('img').un('click', this.remove, this);
46431 this.cb.updateHiddenEl();
46433 this.cb.fireEvent('remove', this.cb, this);
46438 * RooJS Library 1.1.1
46439 * Copyright(c) 2008-2011 Alan Knowles
46446 * @class Roo.form.ComboNested
46447 * @extends Roo.form.ComboBox
46448 * A combobox for that allows selection of nested items in a list,
46463 * Create a new ComboNested
46464 * @param {Object} config Configuration options
46466 Roo.form.ComboNested = function(config){
46467 Roo.form.ComboCheck.superclass.constructor.call(this, config);
46468 // should verify some data...
46470 // hiddenName = required..
46471 // displayField = required
46472 // valudField == required
46473 var req= [ 'hiddenName', 'displayField', 'valueField' ];
46475 Roo.each(req, function(e) {
46476 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46477 throw "Roo.form.ComboNested : missing value for: " + e;
46484 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
46487 * @config {Number} max Number of columns to show
46492 list : null, // the outermost div..
46493 innerLists : null, // the
46497 loadingChildren : false,
46499 onRender : function(ct, position)
46501 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
46503 if(this.hiddenName){
46504 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
46506 this.hiddenField.value =
46507 this.hiddenValue !== undefined ? this.hiddenValue :
46508 this.value !== undefined ? this.value : '';
46510 // prevent input submission
46511 this.el.dom.removeAttribute('name');
46517 this.el.dom.setAttribute('autocomplete', 'off');
46520 var cls = 'x-combo-list';
46522 this.list = new Roo.Layer({
46523 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
46526 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
46527 this.list.setWidth(lw);
46528 this.list.swallowEvent('mousewheel');
46529 this.assetHeight = 0;
46532 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
46533 this.assetHeight += this.header.getHeight();
46535 this.innerLists = [];
46538 for (var i =0 ; i < this.maxColumns; i++) {
46539 this.onRenderList( cls, i);
46542 // always needs footer, as we are going to have an 'OK' button.
46543 this.footer = this.list.createChild({cls:cls+'-ft'});
46544 this.pageTb = new Roo.Toolbar(this.footer);
46549 handler: function()
46555 if ( this.allowBlank && !this.disableClear) {
46557 this.pageTb.add(new Roo.Toolbar.Fill(), {
46558 cls: 'x-btn-icon x-btn-clear',
46560 handler: function()
46563 _this.clearValue();
46564 _this.onSelect(false, -1);
46569 this.assetHeight += this.footer.getHeight();
46573 onRenderList : function ( cls, i)
46576 var lw = Math.floor(
46577 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
46580 this.list.setWidth(lw); // default to '1'
46582 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
46583 //il.on('mouseover', this.onViewOver, this, { list: i });
46584 //il.on('mousemove', this.onViewMove, this, { list: i });
46586 il.setStyle({ 'overflow-x' : 'hidden'});
46589 this.tpl = new Roo.Template({
46590 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
46591 isEmpty: function (value, allValues) {
46593 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
46594 return dl ? 'has-children' : 'no-children'
46599 var store = this.store;
46601 store = new Roo.data.SimpleStore({
46602 //fields : this.store.reader.meta.fields,
46603 reader : this.store.reader,
46607 this.stores[i] = store;
46609 var view = this.views[i] = new Roo.View(
46615 selectedClass: this.selectedClass
46618 view.getEl().setWidth(lw);
46619 view.getEl().setStyle({
46620 position: i < 1 ? 'relative' : 'absolute',
46622 left: (i * lw ) + 'px',
46623 display : i > 0 ? 'none' : 'block'
46625 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
46626 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
46627 //view.on('click', this.onViewClick, this, { list : i });
46629 store.on('beforeload', this.onBeforeLoad, this);
46630 store.on('load', this.onLoad, this, { list : i});
46631 store.on('loadexception', this.onLoadException, this);
46633 // hide the other vies..
46639 restrictHeight : function()
46642 Roo.each(this.innerLists, function(il,i) {
46643 var el = this.views[i].getEl();
46644 el.dom.style.height = '';
46645 var inner = el.dom;
46646 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
46647 // only adjust heights on other ones..
46648 mh = Math.max(h, mh);
46651 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
46652 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
46659 this.list.beginUpdate();
46660 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
46661 this.list.alignTo(this.el, this.listAlign);
46662 this.list.endUpdate();
46667 // -- store handlers..
46669 onBeforeLoad : function()
46671 if(!this.hasFocus){
46674 this.innerLists[0].update(this.loadingText ?
46675 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46676 this.restrictHeight();
46677 this.selectedIndex = -1;
46680 onLoad : function(a,b,c,d)
46682 if (!this.loadingChildren) {
46683 // then we are loading the top level. - hide the children
46684 for (var i = 1;i < this.views.length; i++) {
46685 this.views[i].getEl().setStyle({ display : 'none' });
46687 var lw = Math.floor(
46688 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
46691 this.list.setWidth(lw); // default to '1'
46695 if(!this.hasFocus){
46699 if(this.store.getCount() > 0) {
46701 this.restrictHeight();
46703 this.onEmptyResults();
46706 if (!this.loadingChildren) {
46707 this.selectActive();
46710 this.stores[1].loadData([]);
46711 this.stores[2].loadData([]);
46720 onLoadException : function()
46723 Roo.log(this.store.reader.jsonData);
46724 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
46725 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
46730 // no cleaning of leading spaces on blur here.
46731 cleanLeadingSpace : function(e) { },
46734 onSelectChange : function (view, sels, opts )
46736 var ix = view.getSelectedIndexes();
46738 if (opts.list > this.maxColumns - 2) {
46739 if (view.store.getCount()< 1) {
46740 this.views[opts.list ].getEl().setStyle({ display : 'none' });
46744 // used to clear ?? but if we are loading unselected
46745 this.setFromData(view.store.getAt(ix[0]).data);
46754 // this get's fired when trigger opens..
46755 // this.setFromData({});
46756 var str = this.stores[opts.list+1];
46757 str.data.clear(); // removeall wihtout the fire events..
46761 var rec = view.store.getAt(ix[0]);
46763 this.setFromData(rec.data);
46764 this.fireEvent('select', this, rec, ix[0]);
46766 var lw = Math.floor(
46768 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
46769 ) / this.maxColumns
46771 this.loadingChildren = true;
46772 this.stores[opts.list+1].loadDataFromChildren( rec );
46773 this.loadingChildren = false;
46774 var dl = this.stores[opts.list+1]. getTotalCount();
46776 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
46778 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
46779 for (var i = opts.list+2; i < this.views.length;i++) {
46780 this.views[i].getEl().setStyle({ display : 'none' });
46783 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
46784 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
46786 if (this.isLoading) {
46787 // this.selectActive(opts.list);
46795 onDoubleClick : function()
46797 this.collapse(); //??
46805 recordToStack : function(store, prop, value, stack)
46807 var cstore = new Roo.data.SimpleStore({
46808 //fields : this.store.reader.meta.fields, // we need array reader.. for
46809 reader : this.store.reader,
46813 var record = false;
46815 if(store.getCount() < 1){
46818 store.each(function(r){
46819 if(r.data[prop] == value){
46824 if (r.data.cn && r.data.cn.length) {
46825 cstore.loadDataFromChildren( r);
46826 var cret = _this.recordToStack(cstore, prop, value, stack);
46827 if (cret !== false) {
46836 if (record == false) {
46839 stack.unshift(srec);
46844 * find the stack of stores that match our value.
46849 selectActive : function ()
46851 // if store is not loaded, then we will need to wait for that to happen first.
46853 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
46854 for (var i = 0; i < stack.length; i++ ) {
46855 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
46867 * Ext JS Library 1.1.1
46868 * Copyright(c) 2006-2007, Ext JS, LLC.
46870 * Originally Released Under LGPL - original licence link has changed is not relivant.
46873 * <script type="text/javascript">
46876 * @class Roo.form.Checkbox
46877 * @extends Roo.form.Field
46878 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
46880 * Creates a new Checkbox
46881 * @param {Object} config Configuration options
46883 Roo.form.Checkbox = function(config){
46884 Roo.form.Checkbox.superclass.constructor.call(this, config);
46888 * Fires when the checkbox is checked or unchecked.
46889 * @param {Roo.form.Checkbox} this This checkbox
46890 * @param {Boolean} checked The new checked value
46896 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
46898 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46900 focusClass : undefined,
46902 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46904 fieldClass: "x-form-field",
46906 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
46910 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46911 * {tag: "input", type: "checkbox", autocomplete: "off"})
46913 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46915 * @cfg {String} boxLabel The text that appears beside the checkbox
46919 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
46923 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
46925 valueOff: '0', // value when not checked..
46927 actionMode : 'viewEl',
46930 itemCls : 'x-menu-check-item x-form-item',
46931 groupClass : 'x-menu-group-item',
46932 inputType : 'hidden',
46935 inSetChecked: false, // check that we are not calling self...
46937 inputElement: false, // real input element?
46938 basedOn: false, // ????
46940 isFormField: true, // not sure where this is needed!!!!
46942 onResize : function(){
46943 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46944 if(!this.boxLabel){
46945 this.el.alignTo(this.wrap, 'c-c');
46949 initEvents : function(){
46950 Roo.form.Checkbox.superclass.initEvents.call(this);
46951 this.el.on("click", this.onClick, this);
46952 this.el.on("change", this.onClick, this);
46956 getResizeEl : function(){
46960 getPositionEl : function(){
46965 onRender : function(ct, position){
46966 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46968 if(this.inputValue !== undefined){
46969 this.el.dom.value = this.inputValue;
46972 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
46973 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
46974 var viewEl = this.wrap.createChild({
46975 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
46976 this.viewEl = viewEl;
46977 this.wrap.on('click', this.onClick, this);
46979 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
46980 this.el.on('propertychange', this.setFromHidden, this); //ie
46985 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
46986 // viewEl.on('click', this.onClick, this);
46988 //if(this.checked){
46989 this.setChecked(this.checked);
46991 //this.checked = this.el.dom;
46997 initValue : Roo.emptyFn,
47000 * Returns the checked state of the checkbox.
47001 * @return {Boolean} True if checked, else false
47003 getValue : function(){
47005 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
47007 return this.valueOff;
47012 onClick : function(){
47013 if (this.disabled) {
47016 this.setChecked(!this.checked);
47018 //if(this.el.dom.checked != this.checked){
47019 // this.setValue(this.el.dom.checked);
47024 * Sets the checked state of the checkbox.
47025 * On is always based on a string comparison between inputValue and the param.
47026 * @param {Boolean/String} value - the value to set
47027 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47029 setValue : function(v,suppressEvent){
47032 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
47033 //if(this.el && this.el.dom){
47034 // this.el.dom.checked = this.checked;
47035 // this.el.dom.defaultChecked = this.checked;
47037 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
47038 //this.fireEvent("check", this, this.checked);
47041 setChecked : function(state,suppressEvent)
47043 if (this.inSetChecked) {
47044 this.checked = state;
47050 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
47052 this.checked = state;
47053 if(suppressEvent !== true){
47054 this.fireEvent('check', this, state);
47056 this.inSetChecked = true;
47058 this.el.dom.value = state ? this.inputValue : this.valueOff;
47060 this.inSetChecked = false;
47063 // handle setting of hidden value by some other method!!?!?
47064 setFromHidden: function()
47069 //console.log("SET FROM HIDDEN");
47070 //alert('setFrom hidden');
47071 this.setValue(this.el.dom.value);
47074 onDestroy : function()
47077 Roo.get(this.viewEl).remove();
47080 Roo.form.Checkbox.superclass.onDestroy.call(this);
47083 setBoxLabel : function(str)
47085 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
47090 * Ext JS Library 1.1.1
47091 * Copyright(c) 2006-2007, Ext JS, LLC.
47093 * Originally Released Under LGPL - original licence link has changed is not relivant.
47096 * <script type="text/javascript">
47100 * @class Roo.form.Radio
47101 * @extends Roo.form.Checkbox
47102 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
47103 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
47105 * Creates a new Radio
47106 * @param {Object} config Configuration options
47108 Roo.form.Radio = function(){
47109 Roo.form.Radio.superclass.constructor.apply(this, arguments);
47111 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
47112 inputType: 'radio',
47115 * If this radio is part of a group, it will return the selected value
47118 getGroupValue : function(){
47119 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
47123 onRender : function(ct, position){
47124 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47126 if(this.inputValue !== undefined){
47127 this.el.dom.value = this.inputValue;
47130 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
47131 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
47132 //var viewEl = this.wrap.createChild({
47133 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
47134 //this.viewEl = viewEl;
47135 //this.wrap.on('click', this.onClick, this);
47137 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
47138 //this.el.on('propertychange', this.setFromHidden, this); //ie
47143 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
47144 // viewEl.on('click', this.onClick, this);
47147 this.el.dom.checked = 'checked' ;
47152 * Sets the checked state of the checkbox.
47153 * On is always based on a string comparison between inputValue and the param.
47154 * @param {Boolean/String} value - the value to set
47155 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47157 setValue : function(v,suppressEvent){
47160 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
47161 //if(this.el && this.el.dom){
47162 // this.el.dom.checked = this.checked;
47163 // this.el.dom.defaultChecked = this.checked;
47165 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
47167 this.el.dom.form[this.name].value = v;
47169 //this.fireEvent("check", this, this.checked);
47172 setChecked : function(state,suppressEvent)
47176 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
47178 this.checked = state;
47179 if(suppressEvent !== true){
47180 this.fireEvent('check', this, state);
47187 reset : function(){
47188 // this.setValue(this.resetValue);
47189 //this.originalValue = this.getValue();
47190 this.clearInvalid();
47193 });Roo.rtf = {}; // namespace
47194 Roo.rtf.Hex = function(hex)
47198 Roo.rtf.Paragraph = function(opts)
47200 this.content = []; ///??? is that used?
47201 };Roo.rtf.Span = function(opts)
47203 this.value = opts.value;
47206 Roo.rtf.Group = function(parent)
47208 // we dont want to acutally store parent - it will make debug a nightmare..
47216 Roo.rtf.Group.prototype = {
47220 addContent : function(node) {
47221 // could set styles...
47222 this.content.push(node);
47224 addChild : function(cn)
47228 // only for images really...
47229 toDataURL : function()
47231 var mimetype = false;
47233 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
47234 mimetype = "image/png";
47236 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
47237 mimetype = "image/jpeg";
47240 return 'about:blank'; // ?? error?
47244 var hexstring = this.content[this.content.length-1].value;
47246 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
47247 return String.fromCharCode(parseInt(a, 16));
47252 // this looks like it's normally the {rtf{ .... }}
47253 Roo.rtf.Document = function()
47255 // we dont want to acutally store parent - it will make debug a nightmare..
47261 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
47262 addChild : function(cn)
47266 case 'rtlch': // most content seems to be inside this??
47269 this.rtlch.push(cn);
47272 this[cn.type] = cn;
47277 getElementsByType : function(type)
47280 this._getElementsByType(type, ret, this.cn, 'rtf');
47283 _getElementsByType : function (type, ret, search_array, path)
47285 search_array.forEach(function(n,i) {
47286 if (n.type == type) {
47287 n.path = path + '/' + n.type + ':' + i;
47290 if (n.cn.length > 0) {
47291 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
47298 Roo.rtf.Ctrl = function(opts)
47300 this.value = opts.value;
47301 this.param = opts.param;
47306 * based on this https://github.com/iarna/rtf-parser
47307 * it's really only designed to extract pict from pasted RTF
47311 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
47320 Roo.rtf.Parser = function(text) {
47321 //super({objectMode: true})
47323 this.parserState = this.parseText;
47325 // these are for interpeter...
47327 ///this.parserState = this.parseTop
47328 this.groupStack = [];
47329 this.hexStore = [];
47332 this.groups = []; // where we put the return.
47334 for (var ii = 0; ii < text.length; ++ii) {
47337 if (text[ii] === '\n') {
47343 this.parserState(text[ii]);
47349 Roo.rtf.Parser.prototype = {
47350 text : '', // string being parsed..
47352 controlWordParam : '',
47356 groupStack : false,
47361 row : 1, // reportin?
47365 push : function (el)
47367 var m = 'cmd'+ el.type;
47368 if (typeof(this[m]) == 'undefined') {
47369 Roo.log('invalid cmd:' + el.type);
47375 flushHexStore : function()
47377 if (this.hexStore.length < 1) {
47380 var hexstr = this.hexStore.map(
47385 this.group.addContent( new Roo.rtf.Hex( hexstr ));
47388 this.hexStore.splice(0)
47392 cmdgroupstart : function()
47394 this.flushHexStore();
47396 this.groupStack.push(this.group);
47399 if (this.doc === false) {
47400 this.group = this.doc = new Roo.rtf.Document();
47404 this.group = new Roo.rtf.Group(this.group);
47406 cmdignorable : function()
47408 this.flushHexStore();
47409 this.group.ignorable = true;
47411 cmdendparagraph : function()
47413 this.flushHexStore();
47414 this.group.addContent(new Roo.rtf.Paragraph());
47416 cmdgroupend : function ()
47418 this.flushHexStore();
47419 var endingGroup = this.group;
47422 this.group = this.groupStack.pop();
47424 this.group.addChild(endingGroup);
47429 var doc = this.group || this.doc;
47430 //if (endingGroup instanceof FontTable) {
47431 // doc.fonts = endingGroup.table
47432 //} else if (endingGroup instanceof ColorTable) {
47433 // doc.colors = endingGroup.table
47434 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
47435 if (endingGroup.ignorable === false) {
47437 this.groups.push(endingGroup);
47438 // Roo.log( endingGroup );
47440 //Roo.each(endingGroup.content, function(item)) {
47441 // doc.addContent(item);
47443 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
47446 cmdtext : function (cmd)
47448 this.flushHexStore();
47449 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
47450 //this.group = this.doc
47451 return; // we really don't care about stray text...
47453 this.group.addContent(new Roo.rtf.Span(cmd));
47455 cmdcontrolword : function (cmd)
47457 this.flushHexStore();
47458 if (!this.group.type) {
47459 this.group.type = cmd.value;
47462 this.group.addContent(new Roo.rtf.Ctrl(cmd));
47463 // we actually don't care about ctrl words...
47466 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
47467 if (this[method]) {
47468 this[method](cmd.param)
47470 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
47474 cmdhexchar : function(cmd) {
47475 this.hexStore.push(cmd);
47477 cmderror : function(cmd) {
47483 if (this.text !== '\u0000') this.emitText()
47489 parseText : function(c)
47492 this.parserState = this.parseEscapes;
47493 } else if (c === '{') {
47494 this.emitStartGroup();
47495 } else if (c === '}') {
47496 this.emitEndGroup();
47497 } else if (c === '\x0A' || c === '\x0D') {
47498 // cr/lf are noise chars
47504 parseEscapes: function (c)
47506 if (c === '\\' || c === '{' || c === '}') {
47508 this.parserState = this.parseText;
47510 this.parserState = this.parseControlSymbol;
47511 this.parseControlSymbol(c);
47514 parseControlSymbol: function(c)
47517 this.text += '\u00a0'; // nbsp
47518 this.parserState = this.parseText
47519 } else if (c === '-') {
47520 this.text += '\u00ad'; // soft hyphen
47521 } else if (c === '_') {
47522 this.text += '\u2011'; // non-breaking hyphen
47523 } else if (c === '*') {
47524 this.emitIgnorable();
47525 this.parserState = this.parseText;
47526 } else if (c === "'") {
47527 this.parserState = this.parseHexChar;
47528 } else if (c === '|') { // formula cacter
47529 this.emitFormula();
47530 this.parserState = this.parseText;
47531 } else if (c === ':') { // subentry in an index entry
47532 this.emitIndexSubEntry();
47533 this.parserState = this.parseText;
47534 } else if (c === '\x0a') {
47535 this.emitEndParagraph();
47536 this.parserState = this.parseText;
47537 } else if (c === '\x0d') {
47538 this.emitEndParagraph();
47539 this.parserState = this.parseText;
47541 this.parserState = this.parseControlWord;
47542 this.parseControlWord(c);
47545 parseHexChar: function (c)
47547 if (/^[A-Fa-f0-9]$/.test(c)) {
47549 if (this.hexChar.length >= 2) {
47550 this.emitHexChar();
47551 this.parserState = this.parseText;
47555 this.emitError("Invalid character \"" + c + "\" in hex literal.");
47556 this.parserState = this.parseText;
47559 parseControlWord : function(c)
47562 this.emitControlWord();
47563 this.parserState = this.parseText;
47564 } else if (/^[-\d]$/.test(c)) {
47565 this.parserState = this.parseControlWordParam;
47566 this.controlWordParam += c;
47567 } else if (/^[A-Za-z]$/.test(c)) {
47568 this.controlWord += c;
47570 this.emitControlWord();
47571 this.parserState = this.parseText;
47575 parseControlWordParam : function (c) {
47576 if (/^\d$/.test(c)) {
47577 this.controlWordParam += c;
47578 } else if (c === ' ') {
47579 this.emitControlWord();
47580 this.parserState = this.parseText;
47582 this.emitControlWord();
47583 this.parserState = this.parseText;
47591 emitText : function () {
47592 if (this.text === '') {
47604 emitControlWord : function ()
47607 if (this.controlWord === '') {
47608 // do we want to track this - it seems just to cause problems.
47609 //this.emitError('empty control word');
47612 type: 'controlword',
47613 value: this.controlWord,
47614 param: this.controlWordParam !== '' && Number(this.controlWordParam),
47620 this.controlWord = '';
47621 this.controlWordParam = '';
47623 emitStartGroup : function ()
47627 type: 'groupstart',
47633 emitEndGroup : function ()
47643 emitIgnorable : function ()
47653 emitHexChar : function ()
47658 value: this.hexChar,
47665 emitError : function (message)
47673 char: this.cpos //,
47674 //stack: new Error().stack
47677 emitEndParagraph : function () {
47680 type: 'endparagraph',
47688 Roo.htmleditor = {};
47691 * @class Roo.htmleditor.Filter
47692 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
47693 * @cfg {DomElement} node The node to iterate and filter
47694 * @cfg {boolean|String|Array} tag Tags to replace
47696 * Create a new Filter.
47697 * @param {Object} config Configuration options
47702 Roo.htmleditor.Filter = function(cfg) {
47703 Roo.apply(this.cfg);
47704 // this does not actually call walk as it's really just a abstract class
47708 Roo.htmleditor.Filter.prototype = {
47714 // overrride to do replace comments.
47715 replaceComment : false,
47717 // overrride to do replace or do stuff with tags..
47718 replaceTag : false,
47720 walk : function(dom)
47722 Roo.each( Array.from(dom.childNodes), function( e ) {
47725 case e.nodeType == 8 && this.replaceComment !== false: // comment
47726 this.replaceComment(e);
47729 case e.nodeType != 1: //not a node.
47732 case this.tag === true: // everything
47733 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
47734 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
47735 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
47736 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
47737 if (this.replaceTag && false === this.replaceTag(e)) {
47740 if (e.hasChildNodes()) {
47745 default: // tags .. that do not match.
47746 if (e.hasChildNodes()) {
47756 removeNodeKeepChildren : function( node)
47759 ar = Array.from(node.childNodes);
47760 for (var i = 0; i < ar.length; i++) {
47762 node.removeChild(ar[i]);
47763 // what if we need to walk these???
47764 node.parentNode.insertBefore(ar[i], node);
47767 node.parentNode.removeChild(node);
47770 searchTag : function(dom)
47772 if(this.tag === false) {
47776 var els = dom.getElementsByTagName(this.tag);
47778 Roo.each(Array.from(els), function(e){
47779 if(e.parentNode == null) {
47782 if(this.replaceTag) {
47783 this.replaceTag(e);
47790 * @class Roo.htmleditor.FilterAttributes
47791 * clean attributes and styles including http:// etc.. in attribute
47793 * Run a new Attribute Filter
47794 * @param {Object} config Configuration options
47796 Roo.htmleditor.FilterAttributes = function(cfg)
47798 Roo.apply(this, cfg);
47799 this.attrib_black = this.attrib_black || [];
47800 this.attrib_white = this.attrib_white || [];
47802 this.attrib_clean = this.attrib_clean || [];
47803 this.style_white = this.style_white || [];
47804 this.style_black = this.style_black || [];
47805 this.walk(cfg.node);
47808 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
47810 tag: true, // all tags
47812 attrib_black : false, // array
47813 attrib_clean : false,
47814 attrib_white : false,
47816 style_white : false,
47817 style_black : false,
47820 replaceTag : function(node)
47822 if (!node.attributes || !node.attributes.length) {
47826 for (var i = node.attributes.length-1; i > -1 ; i--) {
47827 var a = node.attributes[i];
47829 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
47830 node.removeAttribute(a.name);
47836 if (a.name.toLowerCase().substr(0,2)=='on') {
47837 node.removeAttribute(a.name);
47842 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
47843 node.removeAttribute(a.name);
47846 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
47847 this.cleanAttr(node,a.name,a.value); // fixme..
47850 if (a.name == 'style') {
47851 this.cleanStyle(node,a.name,a.value);
47854 /// clean up MS crap..
47855 // tecnically this should be a list of valid class'es..
47858 if (a.name == 'class') {
47859 if (a.value.match(/^Mso/)) {
47860 node.removeAttribute('class');
47863 if (a.value.match(/^body$/)) {
47864 node.removeAttribute('class');
47874 return true; // clean children
47877 cleanAttr: function(node, n,v)
47880 if (v.match(/^\./) || v.match(/^\//)) {
47883 if (v.match(/^(http|https):\/\//)
47884 || v.match(/^mailto:/)
47885 || v.match(/^ftp:/)
47886 || v.match(/^data:/)
47890 if (v.match(/^#/)) {
47893 if (v.match(/^\{/)) { // allow template editing.
47896 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
47897 node.removeAttribute(n);
47900 cleanStyle : function(node, n,v)
47902 if (v.match(/expression/)) { //XSS?? should we even bother..
47903 node.removeAttribute(n);
47907 var parts = v.split(/;/);
47910 Roo.each(parts, function(p) {
47911 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
47915 var l = p.split(':').shift().replace(/\s+/g,'');
47916 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
47918 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
47922 // only allow 'c whitelisted system attributes'
47923 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
47931 if (clean.length) {
47932 node.setAttribute(n, clean.join(';'));
47934 node.removeAttribute(n);
47943 * @class Roo.htmleditor.FilterBlack
47944 * remove blacklisted elements.
47946 * Run a new Blacklisted Filter
47947 * @param {Object} config Configuration options
47950 Roo.htmleditor.FilterBlack = function(cfg)
47952 Roo.apply(this, cfg);
47953 this.walk(cfg.node);
47956 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
47958 tag : true, // all elements.
47960 replaceTag : function(n)
47962 n.parentNode.removeChild(n);
47966 * @class Roo.htmleditor.FilterComment
47969 * Run a new Comments Filter
47970 * @param {Object} config Configuration options
47972 Roo.htmleditor.FilterComment = function(cfg)
47974 this.walk(cfg.node);
47977 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
47980 replaceComment : function(n)
47982 n.parentNode.removeChild(n);
47985 * @class Roo.htmleditor.FilterEmpty
47986 * filter empty elements
47988 * Run a new Empty Filter
47989 * @param {Object} config Configuration options
47992 Roo.htmleditor.FilterEmpty = function(cfg)
47994 // no need to apply config.
47995 this.walk(cfg.node);
47998 Roo.extend(Roo.htmleditor.FilterEmpty, Roo.htmleditor.FilterBlack,
48004 replaceTag : function(node)
48006 // start from leaf node
48007 if(node.hasChildNodes()) {
48011 // only filter empty leaf element with certain tags
48013 ['B', 'I', 'U', 'S'].indexOf(node.tagName) < 0
48015 node.attributes && node.attributes.length > 0
48017 node.hasChildNodes()
48019 return false; // don't walk
48022 Roo.htmleditor.FilterBlack.prototype.replaceTag.call(this, node);
48023 return false; // don't walk
48028 * @class Roo.htmleditor.FilterKeepChildren
48029 * remove tags but keep children
48031 * Run a new Keep Children Filter
48032 * @param {Object} config Configuration options
48035 Roo.htmleditor.FilterKeepChildren = function(cfg)
48037 Roo.apply(this, cfg);
48038 if (this.tag === false) {
48039 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
48042 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
48043 this.cleanNamespace = true;
48046 this.walk(cfg.node);
48049 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
48051 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
48053 replaceTag : function(node)
48055 // walk children...
48056 //Roo.log(node.tagName);
48057 var ar = Array.from(node.childNodes);
48060 for (var i = 0; i < ar.length; i++) {
48062 if (e.nodeType == 1) {
48064 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
48065 || // array and it matches
48066 (typeof(this.tag) == 'string' && this.tag == e.tagName)
48068 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
48070 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
48072 this.replaceTag(ar[i]); // child is blacklisted as well...
48077 ar = Array.from(node.childNodes);
48078 for (var i = 0; i < ar.length; i++) {
48080 node.removeChild(ar[i]);
48081 // what if we need to walk these???
48082 node.parentNode.insertBefore(ar[i], node);
48083 if (this.tag !== false) {
48088 //Roo.log("REMOVE:" + node.tagName);
48089 node.parentNode.removeChild(node);
48090 return false; // don't walk children
48095 * @class Roo.htmleditor.FilterParagraph
48096 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
48097 * like on 'push' to remove the <p> tags and replace them with line breaks.
48099 * Run a new Paragraph Filter
48100 * @param {Object} config Configuration options
48103 Roo.htmleditor.FilterParagraph = function(cfg)
48105 // no need to apply config.
48106 this.searchTag(cfg.node);
48109 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
48116 replaceTag : function(node)
48119 if (node.childNodes.length == 1 &&
48120 node.childNodes[0].nodeType == 3 &&
48121 node.childNodes[0].textContent.trim().length < 1
48123 // remove and replace with '<BR>';
48124 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
48125 return false; // no need to walk..
48128 var ar = Array.from(node.childNodes);
48129 for (var i = 0; i < ar.length; i++) {
48130 node.removeChild(ar[i]);
48131 // what if we need to walk these???
48132 node.parentNode.insertBefore(ar[i], node);
48134 // now what about this?
48138 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
48139 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
48140 node.parentNode.removeChild(node);
48147 * @class Roo.htmleditor.FilterHashLink
48150 * Run a new Hash Link Filter
48151 * @param {Object} config Configuration options
48154 Roo.htmleditor.FilterHashLink = function(cfg)
48156 // no need to apply config.
48157 // this.walk(cfg.node);
48158 this.searchTag(cfg.node);
48161 Roo.extend(Roo.htmleditor.FilterHashLink, Roo.htmleditor.Filter,
48167 replaceTag : function(node)
48169 for(var i = 0; i < node.attributes.length; i ++) {
48170 var a = node.attributes[i];
48172 if(a.name.toLowerCase() == 'href' && a.value.startsWith('#')) {
48173 this.removeNodeKeepChildren(node);
48182 * @class Roo.htmleditor.FilterSpan
48183 * filter span's with no attributes out..
48185 * Run a new Span Filter
48186 * @param {Object} config Configuration options
48189 Roo.htmleditor.FilterSpan = function(cfg)
48191 // no need to apply config.
48192 this.searchTag(cfg.node);
48195 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
48201 replaceTag : function(node)
48203 if (node.attributes && node.attributes.length > 0) {
48204 return true; // walk if there are any.
48206 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
48212 * @class Roo.htmleditor.FilterTableWidth
48213 try and remove table width data - as that frequently messes up other stuff.
48215 * was cleanTableWidths.
48217 * Quite often pasting from word etc.. results in tables with column and widths.
48218 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
48221 * Run a new Table Filter
48222 * @param {Object} config Configuration options
48225 Roo.htmleditor.FilterTableWidth = function(cfg)
48227 // no need to apply config.
48228 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
48229 this.walk(cfg.node);
48232 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
48237 replaceTag: function(node) {
48241 if (node.hasAttribute('width')) {
48242 node.removeAttribute('width');
48246 if (node.hasAttribute("style")) {
48249 var styles = node.getAttribute("style").split(";");
48251 Roo.each(styles, function(s) {
48252 if (!s.match(/:/)) {
48255 var kv = s.split(":");
48256 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
48259 // what ever is left... we allow.
48262 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48263 if (!nstyle.length) {
48264 node.removeAttribute('style');
48268 return true; // continue doing children..
48271 * @class Roo.htmleditor.FilterWord
48272 * try and clean up all the mess that Word generates.
48274 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
48277 * Run a new Span Filter
48278 * @param {Object} config Configuration options
48281 Roo.htmleditor.FilterWord = function(cfg)
48283 // no need to apply config.
48284 this.replaceDocBullets(cfg.node);
48286 this.replaceAname(cfg.node);
48287 // this is disabled as the removal is done by other filters;
48288 // this.walk(cfg.node);
48289 this.replaceImageTable(cfg.node);
48293 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
48299 * Clean up MS wordisms...
48301 replaceTag : function(node)
48304 // no idea what this does - span with text, replaceds with just text.
48306 node.nodeName == 'SPAN' &&
48307 !node.hasAttributes() &&
48308 node.childNodes.length == 1 &&
48309 node.firstChild.nodeName == "#text"
48311 var textNode = node.firstChild;
48312 node.removeChild(textNode);
48313 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
48314 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
48316 node.parentNode.insertBefore(textNode, node);
48317 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
48318 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
48321 node.parentNode.removeChild(node);
48322 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
48327 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
48328 node.parentNode.removeChild(node);
48329 return false; // dont do chidlren
48331 //Roo.log(node.tagName);
48332 // remove - but keep children..
48333 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
48334 //Roo.log('-- removed');
48335 while (node.childNodes.length) {
48336 var cn = node.childNodes[0];
48337 node.removeChild(cn);
48338 node.parentNode.insertBefore(cn, node);
48339 // move node to parent - and clean it..
48340 if (cn.nodeType == 1) {
48341 this.replaceTag(cn);
48345 node.parentNode.removeChild(node);
48346 /// no need to iterate chidlren = it's got none..
48347 //this.iterateChildren(node, this.cleanWord);
48348 return false; // no need to iterate children.
48351 if (node.className.length) {
48353 var cn = node.className.split(/\W+/);
48355 Roo.each(cn, function(cls) {
48356 if (cls.match(/Mso[a-zA-Z]+/)) {
48361 node.className = cna.length ? cna.join(' ') : '';
48363 node.removeAttribute("class");
48367 if (node.hasAttribute("lang")) {
48368 node.removeAttribute("lang");
48371 if (node.hasAttribute("style")) {
48373 var styles = node.getAttribute("style").split(";");
48375 Roo.each(styles, function(s) {
48376 if (!s.match(/:/)) {
48379 var kv = s.split(":");
48380 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
48383 // what ever is left... we allow.
48386 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48387 if (!nstyle.length) {
48388 node.removeAttribute('style');
48391 return true; // do children
48397 styleToObject: function(node)
48399 var styles = (node.getAttribute("style") || '').split(";");
48401 Roo.each(styles, function(s) {
48402 if (!s.match(/:/)) {
48405 var kv = s.split(":");
48407 // what ever is left... we allow.
48408 ret[kv[0].trim()] = kv[1];
48414 replaceAname : function (doc)
48416 // replace all the a/name without..
48417 var aa = Array.from(doc.getElementsByTagName('a'));
48418 for (var i = 0; i < aa.length; i++) {
48420 if (a.hasAttribute("name")) {
48421 a.removeAttribute("name");
48423 if (a.hasAttribute("href")) {
48426 // reparent children.
48427 this.removeNodeKeepChildren(a);
48437 replaceDocBullets : function(doc)
48439 // this is a bit odd - but it appears some indents use ql-indent-1
48440 //Roo.log(doc.innerHTML);
48442 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
48443 for( var i = 0; i < listpara.length; i ++) {
48444 listpara[i].className = "MsoListParagraph";
48447 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
48448 for( var i = 0; i < listpara.length; i ++) {
48449 listpara[i].className = "MsoListParagraph";
48451 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
48452 for( var i = 0; i < listpara.length; i ++) {
48453 listpara[i].className = "MsoListParagraph";
48455 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
48456 for( var i = 0; i < listpara.length; i ++) {
48457 listpara[i].className = "MsoListParagraph";
48460 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
48461 var htwo = Array.from(doc.getElementsByTagName('h2'));
48462 for( var i = 0; i < htwo.length; i ++) {
48463 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
48464 htwo[i].className = "MsoListParagraph";
48467 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
48468 for( var i = 0; i < listpara.length; i ++) {
48469 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
48470 listpara[i].className = "MsoListParagraph";
48472 listpara[i].className = "MsoNormalx";
48476 listpara = doc.getElementsByClassName('MsoListParagraph');
48477 // Roo.log(doc.innerHTML);
48481 while(listpara.length) {
48483 this.replaceDocBullet(listpara.item(0));
48490 replaceDocBullet : function(p)
48492 // gather all the siblings.
48494 parent = p.parentNode,
48495 doc = parent.ownerDocument,
48498 //Roo.log("Parsing: " + p.innerText) ;
48499 var listtype = 'ul';
48501 if (ns.nodeType != 1) {
48502 ns = ns.nextSibling;
48505 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
48506 //Roo.log("Missing para r q1indent - got:" + ns.className);
48509 var spans = ns.getElementsByTagName('span');
48511 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
48513 ns = ns.nextSibling;
48515 if (!spans.length) {
48520 for (var i = 0; i < spans.length;i++) {
48522 if (se.hasAttribute('style') && se.hasAttribute('style') && se.style.fontFamily != '') {
48523 ff = se.style.fontFamily;
48529 //Roo.log("got font family: " + ff);
48530 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
48536 //Roo.log("no mso-list?");
48538 var spans = ns.getElementsByTagName('span');
48539 if (!spans.length) {
48542 var has_list = false;
48543 for(var i = 0; i < spans.length; i++) {
48544 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
48553 ns = ns.nextSibling;
48557 if (!items.length) {
48562 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
48563 parent.insertBefore(ul, p);
48565 var stack = [ ul ];
48566 var last_li = false;
48568 var margin_to_depth = {};
48571 items.forEach(function(n, ipos) {
48572 //Roo.log("got innertHMLT=" + n.innerHTML);
48574 var spans = n.getElementsByTagName('span');
48575 if (!spans.length) {
48576 //Roo.log("No spans found");
48578 parent.removeChild(n);
48581 return; // skip it...
48587 for(var i = 0; i < spans.length; i++) {
48589 style = this.styleToObject(spans[i]);
48590 if (typeof(style['mso-list']) == 'undefined') {
48593 if (listtype == 'ol') {
48594 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
48596 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
48599 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
48600 style = this.styleToObject(n); // mo-list is from the parent node.
48601 if (typeof(style['mso-list']) == 'undefined') {
48602 //Roo.log("parent is missing level");
48604 parent.removeChild(n);
48609 var margin = style['margin-left'];
48610 if (typeof(margin_to_depth[margin]) == 'undefined') {
48612 margin_to_depth[margin] = max_margins;
48614 nlvl = margin_to_depth[margin] ;
48618 var nul = doc.createElement(listtype); // what about number lists...
48620 last_li = doc.createElement('li');
48621 stack[lvl].appendChild(last_li);
48623 last_li.appendChild(nul);
48629 // not starting at 1..
48630 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
48631 stack[nlvl].setAttribute("start", num);
48634 var nli = stack[nlvl].appendChild(doc.createElement('li'));
48636 nli.innerHTML = n.innerHTML;
48637 //Roo.log("innerHTML = " + n.innerHTML);
48638 parent.removeChild(n);
48650 replaceImageTable : function(doc)
48653 <table cellpadding=0 cellspacing=0 align=left>
48655 <td width=423 height=0></td>
48659 <td><img width=601 height=401
48660 src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
48661 v:shapes="Picture_x0020_2"></td>
48665 var imgs = Array.from(doc.getElementsByTagName('img'));
48666 Roo.each(imgs, function(img) {
48667 var td = img.parentNode;
48668 if (td.nodeName != 'TD') {
48671 var tr = td.parentNode;
48672 if (tr.nodeName != 'TR') {
48675 var tbody = tr.parentNode;
48676 if (tbody.nodeName != 'TBODY') {
48679 var table = tbody.parentNode;
48680 if (table.nodeName != 'TABLE') {
48685 if (table.getElementsByTagName('tr').length != 2) {
48688 if (table.getElementsByTagName('td').length != 3) {
48691 if (table.innerText.trim() != '') {
48694 var p = table.parentNode;
48695 img.parentNode.removeChild(img);
48696 p.insertBefore(img, table);
48697 p.removeChild(table);
48708 * @class Roo.htmleditor.FilterStyleToTag
48709 * part of the word stuff... - certain 'styles' should be converted to tags.
48711 * font-weight: bold -> bold
48712 * ?? super / subscrit etc..
48715 * Run a new style to tag filter.
48716 * @param {Object} config Configuration options
48718 Roo.htmleditor.FilterStyleToTag = function(cfg)
48722 B : [ 'fontWeight' , 'bold', 'font-weight'],
48723 I : [ 'fontStyle' , 'italic', 'font-style'],
48724 //pre : [ 'font-style' , 'italic'],
48725 // h1.. h6 ?? font-size?
48726 SUP : [ 'verticalAlign' , 'super', 'vertical-align'],
48727 SUB : [ 'verticalAlign' , 'sub', 'vertical-align']
48732 Roo.apply(this, cfg);
48735 this.walk(cfg.node);
48742 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
48744 tag: true, // all tags
48749 replaceTag : function(node)
48753 if (node.getAttribute("style") === null) {
48757 for (var k in this.tags) {
48758 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
48760 node.style.removeProperty(this.tags[k][2]);
48763 if (!inject.length) {
48766 var cn = Array.from(node.childNodes);
48768 Roo.each(inject, function(t) {
48769 var nc = node.ownerDocument.createElement(t);
48770 nn.appendChild(nc);
48773 for(var i = 0;i < cn.length;i++) {
48774 node.removeChild(cn[i]);
48775 nn.appendChild(cn[i]);
48777 return true /// iterate thru
48781 * @class Roo.htmleditor.FilterLongBr
48782 * BR/BR/BR - keep a maximum of 2...
48784 * Run a new Long BR Filter
48785 * @param {Object} config Configuration options
48788 Roo.htmleditor.FilterLongBr = function(cfg)
48790 // no need to apply config.
48791 this.searchTag(cfg.node);
48794 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
48801 replaceTag : function(node)
48804 var ps = node.nextSibling;
48805 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
48806 ps = ps.nextSibling;
48809 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
48810 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
48814 if (!ps || ps.nodeType != 1) {
48818 if (!ps || ps.tagName != 'BR') {
48825 if (!node.previousSibling) {
48828 var ps = node.previousSibling;
48830 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
48831 ps = ps.previousSibling;
48833 if (!ps || ps.nodeType != 1) {
48836 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
48837 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
48841 node.parentNode.removeChild(node); // remove me...
48843 return false; // no need to do children
48850 * @class Roo.htmleditor.FilterBlock
48851 * removes id / data-block and contenteditable that are associated with blocks
48852 * usage should be done on a cloned copy of the dom
48854 * Run a new Attribute Filter { node : xxxx }}
48855 * @param {Object} config Configuration options
48857 Roo.htmleditor.FilterBlock = function(cfg)
48859 Roo.apply(this, cfg);
48860 var qa = cfg.node.querySelectorAll;
48861 this.removeAttributes('data-block');
48862 this.removeAttributes('contenteditable');
48863 this.removeAttributes('id');
48867 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
48869 node: true, // all tags
48872 removeAttributes : function(attr)
48874 var ar = this.node.querySelectorAll('*[' + attr + ']');
48875 for (var i =0;i<ar.length;i++) {
48876 ar[i].removeAttribute(attr);
48885 * This is based loosely on tinymce
48886 * @class Roo.htmleditor.TidySerializer
48887 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
48889 * @method Serializer
48890 * @param {Object} settings Name/value settings object.
48894 Roo.htmleditor.TidySerializer = function(settings)
48896 Roo.apply(this, settings);
48898 this.writer = new Roo.htmleditor.TidyWriter(settings);
48903 Roo.htmleditor.TidySerializer.prototype = {
48906 * @param {boolean} inner do the inner of the node.
48913 * Serializes the specified node into a string.
48916 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
48917 * @method serialize
48918 * @param {DomElement} node Node instance to serialize.
48919 * @return {String} String with HTML based on DOM tree.
48921 serialize : function(node) {
48923 // = settings.validate;
48924 var writer = this.writer;
48928 3: function(node) {
48930 writer.text(node.nodeValue, node);
48933 8: function(node) {
48934 writer.comment(node.nodeValue);
48936 // Processing instruction
48937 7: function(node) {
48938 writer.pi(node.name, node.nodeValue);
48941 10: function(node) {
48942 writer.doctype(node.nodeValue);
48945 4: function(node) {
48946 writer.cdata(node.nodeValue);
48948 // Document fragment
48949 11: function(node) {
48950 node = node.firstChild;
48956 node = node.nextSibling
48961 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
48962 return writer.getContent();
48965 walk: function(node)
48967 var attrName, attrValue, sortedAttrs, i, l, elementRule,
48968 handler = this.handlers[node.nodeType];
48975 var name = node.nodeName;
48976 var isEmpty = node.childNodes.length < 1;
48978 var writer = this.writer;
48979 var attrs = node.attributes;
48982 writer.start(node.nodeName, attrs, isEmpty, node);
48986 node = node.firstChild;
48993 node = node.nextSibling;
48999 // Serialize element and treat all non elements as fragments
49004 * This is based loosely on tinymce
49005 * @class Roo.htmleditor.TidyWriter
49006 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
49009 * - not tested much with 'PRE' formated elements.
49015 Roo.htmleditor.TidyWriter = function(settings)
49018 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
49019 Roo.apply(this, settings);
49023 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
49026 Roo.htmleditor.TidyWriter.prototype = {
49033 // part of state...
49037 last_inline : false,
49042 * Writes the a start element such as <p id="a">.
49045 * @param {String} name Name of the element.
49046 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
49047 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
49049 start: function(name, attrs, empty, node)
49051 var i, l, attr, value;
49053 // there are some situations where adding line break && indentation will not work. will not work.
49054 // <span / b / i ... formating?
49056 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
49057 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
49059 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
49061 var add_lb = name == 'BR' ? false : in_inline;
49063 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
49067 var indentstr = this.indentstr;
49069 // e_inline = elements that can be inline, but still allow \n before and after?
49070 // only 'BR' ??? any others?
49072 // ADD LINE BEFORE tage
49073 if (!this.in_pre) {
49076 if (name == 'BR') {
49078 } else if (this.lastElementEndsWS()) {
49081 // otherwise - no new line. (and dont indent.)
49092 this.html.push(indentstr + '<', name.toLowerCase());
49095 for (i = 0, l = attrs.length; i < l; i++) {
49097 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
49103 this.html[this.html.length] = '/>';
49105 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
49107 var e_inline = name == 'BR' ? false : this.in_inline;
49109 if (!e_inline && !this.in_pre) {
49116 this.html[this.html.length] = '>';
49118 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
49120 if (!in_inline && !in_pre) {
49121 var cn = node.firstChild;
49123 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
49127 cn = cn.nextSibling;
49135 indentstr : in_pre ? '' : (this.indentstr + this.indent),
49137 in_inline : in_inline
49139 // add a line after if we are not in a
49141 if (!in_inline && !in_pre) {
49150 lastElementEndsWS : function()
49152 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
49153 if (value === false) {
49156 return value.match(/\s+$/);
49161 * Writes the a end element such as </p>.
49164 * @param {String} name Name of the element.
49166 end: function(name) {
49169 var indentstr = '';
49170 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
49172 if (!this.in_pre && !in_inline) {
49174 indentstr = this.indentstr;
49176 this.html.push(indentstr + '</', name.toLowerCase(), '>');
49177 this.last_inline = in_inline;
49179 // pop the indent state..
49182 * Writes a text node.
49184 * In pre - we should not mess with the contents.
49188 * @param {String} text String to write out.
49189 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
49191 text: function(in_text, node)
49193 // if not in whitespace critical
49194 if (in_text.length < 1) {
49197 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
49200 this.html[this.html.length] = text;
49204 if (this.in_inline) {
49205 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
49207 text = text.replace(/\s+/,' '); // all white space to single white space
49210 // if next tag is '<BR>', then we can trim right..
49211 if (node.nextSibling &&
49212 node.nextSibling.nodeType == 1 &&
49213 node.nextSibling.nodeName == 'BR' )
49215 text = text.replace(/\s+$/g,'');
49217 // if previous tag was a BR, we can also trim..
49218 if (node.previousSibling &&
49219 node.previousSibling.nodeType == 1 &&
49220 node.previousSibling.nodeName == 'BR' )
49222 text = this.indentstr + text.replace(/^\s+/g,'');
49224 if (text.match(/\n/)) {
49225 text = text.replace(
49226 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
49228 // remoeve the last whitespace / line break.
49229 text = text.replace(/\n\s+$/,'');
49231 // repace long lines
49235 this.html[this.html.length] = text;
49238 // see if previous element was a inline element.
49239 var indentstr = this.indentstr;
49241 text = text.replace(/\s+/g," "); // all whitespace into single white space.
49243 // should trim left?
49244 if (node.previousSibling &&
49245 node.previousSibling.nodeType == 1 &&
49246 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
49252 text = text.replace(/^\s+/,''); // trim left
49255 // should trim right?
49256 if (node.nextSibling &&
49257 node.nextSibling.nodeType == 1 &&
49258 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
49263 text = text.replace(/\s+$/,''); // trim right
49270 if (text.length < 1) {
49273 if (!text.match(/\n/)) {
49274 this.html.push(indentstr + text);
49278 text = this.indentstr + text.replace(
49279 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
49281 // remoeve the last whitespace / line break.
49282 text = text.replace(/\s+$/,'');
49284 this.html.push(text);
49286 // split and indent..
49291 * Writes a cdata node such as <![CDATA[data]]>.
49294 * @param {String} text String to write out inside the cdata.
49296 cdata: function(text) {
49297 this.html.push('<![CDATA[', text, ']]>');
49300 * Writes a comment node such as <!-- Comment -->.
49303 * @param {String} text String to write out inside the comment.
49305 comment: function(text) {
49306 this.html.push('<!--', text, '-->');
49309 * Writes a PI node such as <?xml attr="value" ?>.
49312 * @param {String} name Name of the pi.
49313 * @param {String} text String to write out inside the pi.
49315 pi: function(name, text) {
49316 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
49317 this.indent != '' && this.html.push('\n');
49320 * Writes a doctype node such as <!DOCTYPE data>.
49323 * @param {String} text String to write out inside the doctype.
49325 doctype: function(text) {
49326 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
49329 * Resets the internal buffer if one wants to reuse the writer.
49333 reset: function() {
49334 this.html.length = 0;
49343 * Returns the contents that got serialized.
49345 * @method getContent
49346 * @return {String} HTML contents that got written down.
49348 getContent: function() {
49349 return this.html.join('').replace(/\n$/, '');
49352 pushState : function(cfg)
49354 this.state.push(cfg);
49355 Roo.apply(this, cfg);
49358 popState : function()
49360 if (this.state.length < 1) {
49361 return; // nothing to push
49368 if (this.state.length > 0) {
49369 cfg = this.state[this.state.length-1];
49371 Roo.apply(this, cfg);
49374 addLine: function()
49376 if (this.html.length < 1) {
49381 var value = this.html[this.html.length - 1];
49382 if (value.length > 0 && '\n' !== value) {
49383 this.html.push('\n');
49388 //'pre script noscript style textarea video audio iframe object code'
49389 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
49393 Roo.htmleditor.TidyWriter.inline_elements = [
49394 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
49395 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
49397 Roo.htmleditor.TidyWriter.shortend_elements = [
49398 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
49399 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
49402 Roo.htmleditor.TidyWriter.whitespace_elements = [
49403 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
49405 * This is based loosely on tinymce
49406 * @class Roo.htmleditor.TidyEntities
49408 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
49410 * Not 100% sure this is actually used or needed.
49413 Roo.htmleditor.TidyEntities = {
49416 * initialize data..
49418 init : function (){
49420 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
49425 buildEntitiesLookup: function(items, radix) {
49426 var i, chr, entity, lookup = {};
49430 items = typeof(items) == 'string' ? items.split(',') : items;
49431 radix = radix || 10;
49432 // Build entities lookup table
49433 for (i = 0; i < items.length; i += 2) {
49434 chr = String.fromCharCode(parseInt(items[i], radix));
49435 // Only add non base entities
49436 if (!this.baseEntities[chr]) {
49437 entity = '&' + items[i + 1] + ';';
49438 lookup[chr] = entity;
49439 lookup[entity] = chr;
49478 // Needs to be escaped since the YUI compressor would otherwise break the code
49485 // Reverse lookup table for raw entities
49486 reverseEntities : {
49494 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
49495 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
49496 rawCharsRegExp : /[<>&\"\']/g,
49497 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
49498 namedEntities : false,
49499 namedEntitiesData : [
50000 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
50002 * @method encodeRaw
50003 * @param {String} text Text to encode.
50004 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50005 * @return {String} Entity encoded text.
50007 encodeRaw: function(text, attr)
50010 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50011 return t.baseEntities[chr] || chr;
50015 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
50016 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
50017 * and is exposed as the DOMUtils.encode function.
50019 * @method encodeAllRaw
50020 * @param {String} text Text to encode.
50021 * @return {String} Entity encoded text.
50023 encodeAllRaw: function(text) {
50025 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
50026 return t.baseEntities[chr] || chr;
50030 * Encodes the specified string using numeric entities. The core entities will be
50031 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
50033 * @method encodeNumeric
50034 * @param {String} text Text to encode.
50035 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50036 * @return {String} Entity encoded text.
50038 encodeNumeric: function(text, attr) {
50040 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50041 // Multi byte sequence convert it to a single entity
50042 if (chr.length > 1) {
50043 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
50045 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
50049 * Encodes the specified string using named entities. The core entities will be encoded
50050 * as named ones but all non lower ascii characters will be encoded into named entities.
50052 * @method encodeNamed
50053 * @param {String} text Text to encode.
50054 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
50055 * @param {Object} entities Optional parameter with entities to use.
50056 * @return {String} Entity encoded text.
50058 encodeNamed: function(text, attr, entities) {
50060 entities = entities || this.namedEntities;
50061 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
50062 return t.baseEntities[chr] || entities[chr] || chr;
50066 * Returns an encode function based on the name(s) and it's optional entities.
50068 * @method getEncodeFunc
50069 * @param {String} name Comma separated list of encoders for example named,numeric.
50070 * @param {String} entities Optional parameter with entities to use instead of the built in set.
50071 * @return {function} Encode function to be used.
50073 getEncodeFunc: function(name, entities) {
50074 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
50076 function encodeNamedAndNumeric(text, attr) {
50077 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
50078 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
50082 function encodeCustomNamed(text, attr) {
50083 return t.encodeNamed(text, attr, entities);
50085 // Replace + with , to be compatible with previous TinyMCE versions
50086 name = this.makeMap(name.replace(/\+/g, ','));
50087 // Named and numeric encoder
50088 if (name.named && name.numeric) {
50089 return this.encodeNamedAndNumeric;
50095 return encodeCustomNamed;
50097 return this.encodeNamed;
50100 if (name.numeric) {
50101 return this.encodeNumeric;
50104 return this.encodeRaw;
50107 * Decodes the specified string, this will replace entities with raw UTF characters.
50110 * @param {String} text Text to entity decode.
50111 * @return {String} Entity decoded string.
50113 decode: function(text)
50116 return text.replace(this.entityRegExp, function(all, numeric) {
50118 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
50119 // Support upper UTF
50120 if (numeric > 65535) {
50122 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
50124 return t.asciiMap[numeric] || String.fromCharCode(numeric);
50126 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
50129 nativeDecode : function (text) {
50132 makeMap : function (items, delim, map) {
50134 items = items || [];
50135 delim = delim || ',';
50136 if (typeof items == "string") {
50137 items = items.split(delim);
50142 map[items[i]] = {};
50150 Roo.htmleditor.TidyEntities.init();
50152 * @class Roo.htmleditor.KeyEnter
50153 * Handle Enter press..
50154 * @cfg {Roo.HtmlEditorCore} core the editor.
50156 * Create a new Filter.
50157 * @param {Object} config Configuration options
50164 Roo.htmleditor.KeyEnter = function(cfg) {
50165 Roo.apply(this, cfg);
50166 // this does not actually call walk as it's really just a abstract class
50168 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
50171 //Roo.htmleditor.KeyEnter.i = 0;
50174 Roo.htmleditor.KeyEnter.prototype = {
50178 keypress : function(e)
50180 if (e.charCode != 13 && e.charCode != 10) {
50181 Roo.log([e.charCode,e]);
50184 e.preventDefault();
50185 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
50186 var doc = this.core.doc;
50190 var sel = this.core.getSelection();
50191 var range = sel.getRangeAt(0);
50192 var n = range.commonAncestorContainer;
50193 var pc = range.closest([ 'ol', 'ul']);
50194 var pli = range.closest('li');
50195 if (!pc || e.ctrlKey) {
50196 // on it list, or ctrl pressed.
50198 sel.insertNode('br', 'after');
50200 // only do this if we have ctrl key..
50201 var br = doc.createElement('br');
50202 br.className = 'clear';
50203 br.setAttribute('style', 'clear: both');
50204 sel.insertNode(br, 'after');
50208 this.core.undoManager.addEvent();
50209 this.core.fireEditorEvent(e);
50213 // deal with <li> insetion
50214 if (pli.innerText.trim() == '' &&
50215 pli.previousSibling &&
50216 pli.previousSibling.nodeName == 'LI' &&
50217 pli.previousSibling.innerText.trim() == '') {
50218 pli.parentNode.removeChild(pli.previousSibling);
50219 sel.cursorAfter(pc);
50220 this.core.undoManager.addEvent();
50221 this.core.fireEditorEvent(e);
50225 var li = doc.createElement('LI');
50226 li.innerHTML = ' ';
50227 if (!pli || !pli.firstSibling) {
50228 pc.appendChild(li);
50230 pli.parentNode.insertBefore(li, pli.firstSibling);
50232 sel.cursorText (li.firstChild);
50234 this.core.undoManager.addEvent();
50235 this.core.fireEditorEvent(e);
50247 * @class Roo.htmleditor.Block
50248 * Base class for html editor blocks - do not use it directly .. extend it..
50249 * @cfg {DomElement} node The node to apply stuff to.
50250 * @cfg {String} friendly_name the name that appears in the context bar about this block
50251 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
50254 * Create a new Filter.
50255 * @param {Object} config Configuration options
50258 Roo.htmleditor.Block = function(cfg)
50260 // do nothing .. should not be called really.
50263 * factory method to get the block from an element (using cache if necessary)
50265 * @param {HtmlElement} the dom element
50267 Roo.htmleditor.Block.factory = function(node)
50269 var cc = Roo.htmleditor.Block.cache;
50270 var id = Roo.get(node).id;
50271 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
50272 Roo.htmleditor.Block.cache[id].readElement(node);
50273 return Roo.htmleditor.Block.cache[id];
50275 var db = node.getAttribute('data-block');
50277 db = node.nodeName.toLowerCase().toUpperCaseFirst();
50279 var cls = Roo.htmleditor['Block' + db];
50280 if (typeof(cls) == 'undefined') {
50281 //Roo.log(node.getAttribute('data-block'));
50282 Roo.log("OOps missing block : " + 'Block' + db);
50285 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
50286 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
50290 * initalize all Elements from content that are 'blockable'
50292 * @param the body element
50294 Roo.htmleditor.Block.initAll = function(body, type)
50296 if (typeof(type) == 'undefined') {
50297 var ia = Roo.htmleditor.Block.initAll;
50303 Roo.each(Roo.get(body).query(type), function(e) {
50304 Roo.htmleditor.Block.factory(e);
50307 // question goes here... do we need to clear out this cache sometimes?
50308 // or show we make it relivant to the htmleditor.
50309 Roo.htmleditor.Block.cache = {};
50311 Roo.htmleditor.Block.prototype = {
50315 // used by context menu
50316 friendly_name : 'Based Block',
50318 // text for button to delete this element
50319 deleteTitle : false,
50323 * Update a node with values from this object
50324 * @param {DomElement} node
50326 updateElement : function(node)
50328 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
50331 * convert to plain HTML for calling insertAtCursor..
50333 toHTML : function()
50335 return Roo.DomHelper.markup(this.toObject());
50338 * used by readEleemnt to extract data from a node
50339 * may need improving as it's pretty basic
50341 * @param {DomElement} node
50342 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
50343 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
50344 * @param {String} style the style property - eg. text-align
50346 getVal : function(node, tag, attr, style)
50349 if (tag !== true && n.tagName != tag.toUpperCase()) {
50350 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
50351 // but kiss for now.
50352 n = node.getElementsByTagName(tag).item(0);
50357 if (attr === false) {
50360 if (attr == 'html') {
50361 return n.innerHTML;
50363 if (attr == 'style') {
50364 return n.style[style];
50367 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
50371 * create a DomHelper friendly object - for use with
50372 * Roo.DomHelper.markup / overwrite / etc..
50375 toObject : function()
50380 * Read a node that has a 'data-block' property - and extract the values from it.
50381 * @param {DomElement} node - the node
50383 readElement : function(node)
50394 * @class Roo.htmleditor.BlockFigure
50395 * Block that has an image and a figcaption
50396 * @cfg {String} image_src the url for the image
50397 * @cfg {String} align (left|right) alignment for the block default left
50398 * @cfg {String} caption the text to appear below (and in the alt tag)
50399 * @cfg {String} caption_display (block|none) display or not the caption
50400 * @cfg {String|number} image_width the width of the image number or %?
50401 * @cfg {String|number} image_height the height of the image number or %?
50404 * Create a new Filter.
50405 * @param {Object} config Configuration options
50408 Roo.htmleditor.BlockFigure = function(cfg)
50411 this.readElement(cfg.node);
50412 this.updateElement(cfg.node);
50414 Roo.apply(this, cfg);
50416 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
50423 caption_display : 'block',
50429 // margin: '2%', not used
50431 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
50434 // used by context menu
50435 friendly_name : 'Image with caption',
50436 deleteTitle : "Delete Image and Caption",
50438 contextMenu : function(toolbar)
50441 var block = function() {
50442 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
50446 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
50448 var syncValue = toolbar.editorcore.syncValue;
50454 xtype : 'TextItem',
50456 xns : rooui.Toolbar //Boostrap?
50460 text: 'Change Image URL',
50463 click: function (btn, state)
50467 Roo.MessageBox.show({
50468 title : "Image Source URL",
50469 msg : "Enter the url for the image",
50470 buttons: Roo.MessageBox.OKCANCEL,
50471 fn: function(btn, val){
50478 toolbar.editorcore.onEditorEvent();
50482 //multiline: multiline,
50484 value : b.image_src
50488 xns : rooui.Toolbar
50493 text: 'Change Link URL',
50496 click: function (btn, state)
50500 Roo.MessageBox.show({
50501 title : "Link URL",
50502 msg : "Enter the url for the link - leave blank to have no link",
50503 buttons: Roo.MessageBox.OKCANCEL,
50504 fn: function(btn, val){
50511 toolbar.editorcore.onEditorEvent();
50515 //multiline: multiline,
50521 xns : rooui.Toolbar
50525 text: 'Show Video URL',
50528 click: function (btn, state)
50530 Roo.MessageBox.alert("Video URL",
50531 block().video_url == '' ? 'This image is not linked ot a video' :
50532 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
50535 xns : rooui.Toolbar
50540 xtype : 'TextItem',
50542 xns : rooui.Toolbar //Boostrap?
50545 xtype : 'ComboBox',
50546 allowBlank : false,
50547 displayField : 'val',
50550 triggerAction : 'all',
50552 valueField : 'val',
50556 select : function (combo, r, index)
50558 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50560 b.width = r.get('val');
50563 toolbar.editorcore.onEditorEvent();
50568 xtype : 'SimpleStore',
50581 xtype : 'TextItem',
50583 xns : rooui.Toolbar //Boostrap?
50586 xtype : 'ComboBox',
50587 allowBlank : false,
50588 displayField : 'val',
50591 triggerAction : 'all',
50593 valueField : 'val',
50597 select : function (combo, r, index)
50599 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50601 b.align = r.get('val');
50604 toolbar.editorcore.onEditorEvent();
50609 xtype : 'SimpleStore',
50623 text: 'Hide Caption',
50624 name : 'caption_display',
50626 enableToggle : true,
50627 setValue : function(v) {
50628 // this trigger toggle.
50630 this.setText(v ? "Hide Caption" : "Show Caption");
50631 this.setPressed(v != 'block');
50634 toggle: function (btn, state)
50637 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
50638 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
50641 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50642 toolbar.editorcore.onEditorEvent();
50645 xns : rooui.Toolbar
50651 * create a DomHelper friendly object - for use with
50652 * Roo.DomHelper.markup / overwrite / etc..
50654 toObject : function()
50656 var d = document.createElement('div');
50657 d.innerHTML = this.caption;
50659 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
50661 var iw = this.align == 'center' ? this.width : '100%';
50664 contenteditable : 'false',
50665 src : this.image_src,
50666 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
50669 maxWidth : iw + ' !important', // this is not getting rendered?
50673 width: this.align == 'center' ? this.width : '100%'
50678 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
50680 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
50685 if (this.href.length > 0) {
50689 contenteditable : 'true',
50697 if (this.video_url.length > 0) {
50702 allowfullscreen : true,
50703 width : 420, // these are for video tricks - that we replace the outer
50705 src : this.video_url,
50716 'data-block' : 'Figure',
50717 'data-width' : this.width,
50718 'data-caption' : this.caption,
50719 'data-caption-display' : this.caption_display,
50720 contenteditable : 'false',
50724 float : this.align ,
50725 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
50726 width : this.align == 'center' ? '100%' : this.width,
50728 padding: this.align == 'center' ? '0' : '0 10px' ,
50729 textAlign : this.align // seems to work for email..
50733 align : this.align,
50739 // show figcaption only if caption_display is 'block'
50740 if(this.caption_display == 'block') {
50744 textAlign : 'left',
50746 lineHeight : '24px',
50747 display : this.caption_display,
50748 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
50750 width: this.align == 'center' ? this.width : '100%'
50754 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
50759 marginTop : '16px',
50760 textAlign : 'start'
50765 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
50767 contenteditable : Roo.htmleditor.BlockFigure.caption_edit,
50768 html : this.caption.length ? this.caption : "Caption" // fake caption
50782 readElement : function(node)
50784 // this should not really come from the link...
50785 this.video_url = this.getVal(node, 'div', 'src');
50786 this.cls = this.getVal(node, 'div', 'class');
50787 this.href = this.getVal(node, 'a', 'href');
50790 this.image_src = this.getVal(node, 'img', 'src');
50792 this.align = this.getVal(node, 'figure', 'align');
50794 // caption display is stored in figure
50795 this.caption_display = this.getVal(node, true, 'data-caption-display');
50797 // backward compatible
50798 // it was stored in figcaption
50799 if(this.caption_display == '') {
50800 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
50803 // read caption from figcaption
50804 var figcaption = this.getVal(node, 'figcaption', false);
50806 if (figcaption !== '') {
50807 this.caption = this.getVal(figcaption, 'i', 'html');
50811 // read caption from data-caption in figure if no caption from figcaption
50812 var dc = this.getVal(node, true, 'data-caption');
50814 if(this.caption_display == 'none' && dc && dc.length){
50818 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
50819 this.width = this.getVal(node, true, 'data-width');
50820 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
50823 removeNode : function()
50837 Roo.apply(Roo.htmleditor.BlockFigure, {
50838 caption_edit : true
50844 * @class Roo.htmleditor.BlockTable
50845 * Block that manages a table
50848 * Create a new Filter.
50849 * @param {Object} config Configuration options
50852 Roo.htmleditor.BlockTable = function(cfg)
50855 this.readElement(cfg.node);
50856 this.updateElement(cfg.node);
50858 Roo.apply(this, cfg);
50861 for(var r = 0; r < this.no_row; r++) {
50863 for(var c = 0; c < this.no_col; c++) {
50864 this.rows[r][c] = this.emptyCell();
50871 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
50880 // used by context menu
50881 friendly_name : 'Table',
50882 deleteTitle : 'Delete Table',
50883 // context menu is drawn once..
50885 contextMenu : function(toolbar)
50888 var block = function() {
50889 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
50893 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
50895 var syncValue = toolbar.editorcore.syncValue;
50901 xtype : 'TextItem',
50903 xns : rooui.Toolbar //Boostrap?
50906 xtype : 'ComboBox',
50907 allowBlank : false,
50908 displayField : 'val',
50911 triggerAction : 'all',
50913 valueField : 'val',
50917 select : function (combo, r, index)
50919 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50921 b.width = r.get('val');
50924 toolbar.editorcore.onEditorEvent();
50929 xtype : 'SimpleStore',
50941 xtype : 'TextItem',
50942 text : "Columns: ",
50943 xns : rooui.Toolbar //Boostrap?
50950 click : function (_self, e)
50952 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50953 block().removeColumn();
50955 toolbar.editorcore.onEditorEvent();
50958 xns : rooui.Toolbar
50964 click : function (_self, e)
50966 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50967 block().addColumn();
50969 toolbar.editorcore.onEditorEvent();
50972 xns : rooui.Toolbar
50976 xtype : 'TextItem',
50978 xns : rooui.Toolbar //Boostrap?
50985 click : function (_self, e)
50987 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
50988 block().removeRow();
50990 toolbar.editorcore.onEditorEvent();
50993 xns : rooui.Toolbar
50999 click : function (_self, e)
51003 toolbar.editorcore.onEditorEvent();
51006 xns : rooui.Toolbar
51011 text: 'Reset Column Widths',
51014 click : function (_self, e)
51016 block().resetWidths();
51018 toolbar.editorcore.onEditorEvent();
51021 xns : rooui.Toolbar
51032 * create a DomHelper friendly object - for use with
51033 * Roo.DomHelper.markup / overwrite / etc..
51034 * ?? should it be called with option to hide all editing features?
51036 toObject : function()
51041 contenteditable : 'false', // this stops cell selection from picking the table.
51042 'data-block' : 'Table',
51045 border : 'solid 1px #000', // ??? hard coded?
51046 'border-collapse' : 'collapse'
51049 { tag : 'tbody' , cn : [] }
51053 // do we have a head = not really
51055 Roo.each(this.rows, function( row ) {
51060 border : 'solid 1px #000',
51066 ret.cn[0].cn.push(tr);
51067 // does the row have any properties? ?? height?
51069 Roo.each(row, function( cell ) {
51073 contenteditable : 'true',
51074 'data-block' : 'Td',
51078 if (cell.colspan > 1) {
51079 td.colspan = cell.colspan ;
51080 nc += cell.colspan;
51084 if (cell.rowspan > 1) {
51085 td.rowspan = cell.rowspan ;
51094 ncols = Math.max(nc, ncols);
51098 // add the header row..
51107 readElement : function(node)
51109 node = node ? node : this.node ;
51110 this.width = this.getVal(node, true, 'style', 'width') || '100%';
51114 var trs = Array.from(node.rows);
51115 trs.forEach(function(tr) {
51117 this.rows.push(row);
51121 Array.from(tr.cells).forEach(function(td) {
51124 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
51125 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
51126 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
51127 html : td.innerHTML
51129 no_column += add.colspan;
51136 this.no_col = Math.max(this.no_col, no_column);
51143 normalizeRows: function()
51147 this.rows.forEach(function(row) {
51150 row = this.normalizeRow(row);
51152 row.forEach(function(c) {
51153 while (typeof(ret[rid][cid]) != 'undefined') {
51156 if (typeof(ret[rid]) == 'undefined') {
51162 if (c.rowspan < 2) {
51166 for(var i = 1 ;i < c.rowspan; i++) {
51167 if (typeof(ret[rid+i]) == 'undefined') {
51170 ret[rid+i][cid] = c;
51178 normalizeRow: function(row)
51181 row.forEach(function(c) {
51182 if (c.colspan < 2) {
51186 for(var i =0 ;i < c.colspan; i++) {
51194 deleteColumn : function(sel)
51196 if (!sel || sel.type != 'col') {
51199 if (this.no_col < 2) {
51203 this.rows.forEach(function(row) {
51204 var cols = this.normalizeRow(row);
51205 var col = cols[sel.col];
51206 if (col.colspan > 1) {
51216 removeColumn : function()
51218 this.deleteColumn({
51220 col : this.no_col-1
51222 this.updateElement();
51226 addColumn : function()
51229 this.rows.forEach(function(row) {
51230 row.push(this.emptyCell());
51233 this.updateElement();
51236 deleteRow : function(sel)
51238 if (!sel || sel.type != 'row') {
51242 if (this.no_row < 2) {
51246 var rows = this.normalizeRows();
51249 rows[sel.row].forEach(function(col) {
51250 if (col.rowspan > 1) {
51253 col.remove = 1; // flage it as removed.
51258 this.rows.forEach(function(row) {
51260 row.forEach(function(c) {
51261 if (typeof(c.remove) == 'undefined') {
51266 if (newrow.length > 0) {
51270 this.rows = newrows;
51275 this.updateElement();
51278 removeRow : function()
51282 row : this.no_row-1
51288 addRow : function()
51292 for (var i = 0; i < this.no_col; i++ ) {
51294 row.push(this.emptyCell());
51297 this.rows.push(row);
51298 this.updateElement();
51302 // the default cell object... at present...
51303 emptyCell : function() {
51304 return (new Roo.htmleditor.BlockTd({})).toObject();
51309 removeNode : function()
51316 resetWidths : function()
51318 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
51319 var nn = Roo.htmleditor.Block.factory(n);
51321 nn.updateElement(n);
51334 * since selections really work on the table cell, then editing really should work from there
51336 * The original plan was to support merging etc... - but that may not be needed yet..
51338 * So this simple version will support:
51340 * adjust the width +/-
51341 * reset the width...
51350 * @class Roo.htmleditor.BlockTable
51351 * Block that manages a table
51354 * Create a new Filter.
51355 * @param {Object} config Configuration options
51358 Roo.htmleditor.BlockTd = function(cfg)
51361 this.readElement(cfg.node);
51362 this.updateElement(cfg.node);
51364 Roo.apply(this, cfg);
51369 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
51374 textAlign : 'left',
51381 // used by context menu
51382 friendly_name : 'Table Cell',
51383 deleteTitle : false, // use our customer delete
51385 // context menu is drawn once..
51387 contextMenu : function(toolbar)
51390 var cell = function() {
51391 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
51394 var table = function() {
51395 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
51399 var saveSel = function()
51401 lr = toolbar.editorcore.getSelection().getRangeAt(0);
51403 var restoreSel = function()
51407 toolbar.editorcore.focus();
51408 var cr = toolbar.editorcore.getSelection();
51409 cr.removeAllRanges();
51411 toolbar.editorcore.onEditorEvent();
51412 }).defer(10, this);
51418 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
51420 var syncValue = toolbar.editorcore.syncValue;
51427 text : 'Edit Table',
51429 click : function() {
51430 var t = toolbar.tb.selectedNode.closest('table');
51431 toolbar.editorcore.selectNode(t);
51432 toolbar.editorcore.onEditorEvent();
51441 xtype : 'TextItem',
51442 text : "Column Width: ",
51443 xns : rooui.Toolbar
51450 click : function (_self, e)
51452 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51453 cell().shrinkColumn();
51455 toolbar.editorcore.onEditorEvent();
51458 xns : rooui.Toolbar
51464 click : function (_self, e)
51466 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51467 cell().growColumn();
51469 toolbar.editorcore.onEditorEvent();
51472 xns : rooui.Toolbar
51476 xtype : 'TextItem',
51477 text : "Vertical Align: ",
51478 xns : rooui.Toolbar //Boostrap?
51481 xtype : 'ComboBox',
51482 allowBlank : false,
51483 displayField : 'val',
51486 triggerAction : 'all',
51488 valueField : 'val',
51492 select : function (combo, r, index)
51494 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51496 b.valign = r.get('val');
51499 toolbar.editorcore.onEditorEvent();
51504 xtype : 'SimpleStore',
51508 ['bottom'] // there are afew more...
51516 xtype : 'TextItem',
51517 text : "Merge Cells: ",
51518 xns : rooui.Toolbar
51527 click : function (_self, e)
51529 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51530 cell().mergeRight();
51531 //block().growColumn();
51533 toolbar.editorcore.onEditorEvent();
51536 xns : rooui.Toolbar
51543 click : function (_self, e)
51545 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51546 cell().mergeBelow();
51547 //block().growColumn();
51549 toolbar.editorcore.onEditorEvent();
51552 xns : rooui.Toolbar
51555 xtype : 'TextItem',
51557 xns : rooui.Toolbar
51565 click : function (_self, e)
51567 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51570 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
51571 toolbar.editorcore.onEditorEvent();
51575 xns : rooui.Toolbar
51579 xns : rooui.Toolbar
51588 xns : rooui.Toolbar,
51597 click : function (_self, e)
51601 cell().deleteColumn();
51603 toolbar.editorcore.selectNode(t.node);
51604 toolbar.editorcore.onEditorEvent();
51613 click : function (_self, e)
51616 cell().deleteRow();
51619 toolbar.editorcore.selectNode(t.node);
51620 toolbar.editorcore.onEditorEvent();
51627 xtype : 'Separator',
51634 click : function (_self, e)
51637 var nn = t.node.nextSibling || t.node.previousSibling;
51638 t.node.parentNode.removeChild(t.node);
51640 toolbar.editorcore.selectNode(nn, true);
51642 toolbar.editorcore.onEditorEvent();
51652 // align... << fixme
51660 * create a DomHelper friendly object - for use with
51661 * Roo.DomHelper.markup / overwrite / etc..
51662 * ?? should it be called with option to hide all editing features?
51665 * create a DomHelper friendly object - for use with
51666 * Roo.DomHelper.markup / overwrite / etc..
51667 * ?? should it be called with option to hide all editing features?
51669 toObject : function()
51673 contenteditable : 'true', // this stops cell selection from picking the table.
51674 'data-block' : 'Td',
51675 valign : this.valign,
51677 'text-align' : this.textAlign,
51678 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
51679 'border-collapse' : 'collapse',
51680 padding : '6px', // 8 for desktop / 4 for mobile
51681 'vertical-align': this.valign
51685 if (this.width != '') {
51686 ret.width = this.width;
51687 ret.style.width = this.width;
51691 if (this.colspan > 1) {
51692 ret.colspan = this.colspan ;
51694 if (this.rowspan > 1) {
51695 ret.rowspan = this.rowspan ;
51704 readElement : function(node)
51706 node = node ? node : this.node ;
51707 this.width = node.style.width;
51708 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
51709 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
51710 this.html = node.innerHTML;
51711 if (node.style.textAlign != '') {
51712 this.textAlign = node.style.textAlign;
51718 // the default cell object... at present...
51719 emptyCell : function() {
51723 textAlign : 'left',
51724 html : " " // is this going to be editable now?
51729 removeNode : function()
51731 return this.node.closest('table');
51739 toTableArray : function()
51742 var tab = this.node.closest('tr').closest('table');
51743 Array.from(tab.rows).forEach(function(r, ri){
51747 this.colWidths = [];
51748 var all_auto = true;
51749 Array.from(tab.rows).forEach(function(r, ri){
51752 Array.from(r.cells).forEach(function(ce, ci){
51757 colspan : ce.colSpan,
51758 rowspan : ce.rowSpan
51760 if (ce.isEqualNode(this.node)) {
51763 // if we have been filled up by a row?
51764 if (typeof(ret[rn][cn]) != 'undefined') {
51765 while(typeof(ret[rn][cn]) != 'undefined') {
51771 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
51772 this.colWidths[cn] = ce.style.width;
51773 if (this.colWidths[cn] != '') {
51779 if (c.colspan < 2 && c.rowspan < 2 ) {
51784 for(var j = 0; j < c.rowspan; j++) {
51785 if (typeof(ret[rn+j]) == 'undefined') {
51786 continue; // we have a problem..
51789 for(var i = 0; i < c.colspan; i++) {
51790 ret[rn+j][cn+i] = c;
51799 // initalize widths.?
51800 // either all widths or no widths..
51802 this.colWidths[0] = false; // no widths flag.
51813 mergeRight: function()
51816 // get the contents of the next cell along..
51817 var tr = this.node.closest('tr');
51818 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
51819 if (i >= tr.childNodes.length - 1) {
51820 return; // no cells on right to merge with.
51822 var table = this.toTableArray();
51824 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
51825 return; // nothing right?
51827 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
51828 // right cell - must be same rowspan and on the same row.
51829 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
51830 return; // right hand side is not same rowspan.
51835 this.node.innerHTML += ' ' + rc.cell.innerHTML;
51836 tr.removeChild(rc.cell);
51837 this.colspan += rc.colspan;
51838 this.node.setAttribute('colspan', this.colspan);
51840 var table = this.toTableArray();
51841 this.normalizeWidths(table);
51842 this.updateWidths(table);
51846 mergeBelow : function()
51848 var table = this.toTableArray();
51849 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
51850 return; // no row below
51852 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
51853 return; // nothing right?
51855 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
51857 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
51858 return; // right hand side is not same rowspan.
51860 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
51861 rc.cell.parentNode.removeChild(rc.cell);
51862 this.rowspan += rc.rowspan;
51863 this.node.setAttribute('rowspan', this.rowspan);
51868 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
51871 var table = this.toTableArray();
51872 var cd = this.cellData;
51876 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
51879 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
51880 if (r == cd.row && c == cd.col) {
51881 this.node.removeAttribute('rowspan');
51882 this.node.removeAttribute('colspan');
51885 var ntd = this.node.cloneNode(); // which col/row should be 0..
51886 ntd.removeAttribute('id');
51887 ntd.style.width = this.colWidths[c];
51888 ntd.innerHTML = '';
51889 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
51893 this.redrawAllCells(table);
51899 redrawAllCells: function(table)
51903 var tab = this.node.closest('tr').closest('table');
51904 var ctr = tab.rows[0].parentNode;
51905 Array.from(tab.rows).forEach(function(r, ri){
51907 Array.from(r.cells).forEach(function(ce, ci){
51908 ce.parentNode.removeChild(ce);
51910 r.parentNode.removeChild(r);
51912 for(var r = 0 ; r < table.length; r++) {
51913 var re = tab.rows[r];
51915 var re = tab.ownerDocument.createElement('tr');
51916 ctr.appendChild(re);
51917 for(var c = 0 ; c < table[r].length; c++) {
51918 if (table[r][c].cell === false) {
51922 re.appendChild(table[r][c].cell);
51924 table[r][c].cell = false;
51929 updateWidths : function(table)
51931 for(var r = 0 ; r < table.length; r++) {
51933 for(var c = 0 ; c < table[r].length; c++) {
51934 if (table[r][c].cell === false) {
51938 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
51939 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
51940 el.width = Math.floor(this.colWidths[c]) +'%';
51941 el.updateElement(el.node);
51943 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
51944 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
51947 for(var i = 0; i < table[r][c].colspan; i ++) {
51948 if (typeof(this.colWidths[c + i]) != 'undefined') {
51949 lv = this.colWidths[c + i];
51951 this.colWidths[c + i] = lv;
51953 width += Math.floor(this.colWidths[c + i]);
51955 el.width = width +'%';
51956 el.updateElement(el.node);
51958 table[r][c].cell = false; // done
51962 normalizeWidths : function(table)
51964 if (this.colWidths[0] === false) {
51965 var nw = 100.0 / this.colWidths.length;
51966 this.colWidths.forEach(function(w,i) {
51967 this.colWidths[i] = nw;
51972 var t = 0, missing = [];
51974 this.colWidths.forEach(function(w,i) {
51976 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
51977 var add = this.colWidths[i];
51986 var nc = this.colWidths.length;
51987 if (missing.length) {
51988 var mult = (nc - missing.length) / (1.0 * nc);
51990 var ew = (100 -t) / (1.0 * missing.length);
51991 this.colWidths.forEach(function(w,i) {
51993 this.colWidths[i] = w * mult;
51997 this.colWidths[i] = ew;
51999 // have to make up numbers..
52002 // now we should have all the widths..
52007 shrinkColumn : function()
52009 var table = this.toTableArray();
52010 this.normalizeWidths(table);
52011 var col = this.cellData.col;
52012 var nw = this.colWidths[col] * 0.8;
52016 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
52017 this.colWidths.forEach(function(w,i) {
52019 this.colWidths[i] = nw;
52022 if (typeof(this.colWidths[i]) == 'undefined') {
52023 this.colWidths[i] = otherAdd;
52025 this.colWidths[i] += otherAdd;
52028 this.updateWidths(table);
52031 growColumn : function()
52033 var table = this.toTableArray();
52034 this.normalizeWidths(table);
52035 var col = this.cellData.col;
52036 var nw = this.colWidths[col] * 1.2;
52040 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
52041 this.colWidths.forEach(function(w,i) {
52043 this.colWidths[i] = nw;
52046 if (typeof(this.colWidths[i]) == 'undefined') {
52047 this.colWidths[i] = otherSub;
52049 this.colWidths[i] -= otherSub;
52053 this.updateWidths(table);
52056 deleteRow : function()
52058 // delete this rows 'tr'
52059 // if any of the cells in this row have a rowspan > 1 && row!= this row..
52060 // then reduce the rowspan.
52061 var table = this.toTableArray();
52062 // this.cellData.row;
52063 for (var i =0;i< table[this.cellData.row].length ; i++) {
52064 var c = table[this.cellData.row][i];
52065 if (c.row != this.cellData.row) {
52068 c.cell.setAttribute('rowspan', c.rowspan);
52071 if (c.rowspan > 1) {
52073 c.cell.setAttribute('rowspan', c.rowspan);
52076 table.splice(this.cellData.row,1);
52077 this.redrawAllCells(table);
52080 deleteColumn : function()
52082 var table = this.toTableArray();
52084 for (var i =0;i< table.length ; i++) {
52085 var c = table[i][this.cellData.col];
52086 if (c.col != this.cellData.col) {
52087 table[i][this.cellData.col].colspan--;
52088 } else if (c.colspan > 1) {
52090 c.cell.setAttribute('colspan', c.colspan);
52092 table[i].splice(this.cellData.col,1);
52095 this.redrawAllCells(table);
52103 //<script type="text/javascript">
52106 * Based Ext JS Library 1.1.1
52107 * Copyright(c) 2006-2007, Ext JS, LLC.
52113 * @class Roo.HtmlEditorCore
52114 * @extends Roo.Component
52115 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
52117 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
52120 Roo.HtmlEditorCore = function(config){
52123 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
52128 * @event initialize
52129 * Fires when the editor is fully initialized (including the iframe)
52130 * @param {Roo.HtmlEditorCore} this
52135 * Fires when the editor is first receives the focus. Any insertion must wait
52136 * until after this event.
52137 * @param {Roo.HtmlEditorCore} this
52141 * @event beforesync
52142 * Fires before the textarea is updated with content from the editor iframe. Return false
52143 * to cancel the sync.
52144 * @param {Roo.HtmlEditorCore} this
52145 * @param {String} html
52149 * @event beforepush
52150 * Fires before the iframe editor is updated with content from the textarea. Return false
52151 * to cancel the push.
52152 * @param {Roo.HtmlEditorCore} this
52153 * @param {String} html
52158 * Fires when the textarea is updated with content from the editor iframe.
52159 * @param {Roo.HtmlEditorCore} this
52160 * @param {String} html
52165 * Fires when the iframe editor is updated with content from the textarea.
52166 * @param {Roo.HtmlEditorCore} this
52167 * @param {String} html
52172 * @event editorevent
52173 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
52174 * @param {Roo.HtmlEditorCore} this
52181 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
52183 // defaults : white / black...
52184 this.applyBlacklists();
52191 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
52195 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
52201 * @cfg {String} css styling for resizing. (used on bootstrap only)
52205 * @cfg {Number} height (in pixels)
52209 * @cfg {Number} width (in pixels)
52213 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
52214 * if you are doing an email editor, this probably needs disabling, it's designed
52219 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
52221 enableBlocks : true,
52223 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
52226 stylesheets: false,
52228 * @cfg {String} language default en - language of text (usefull for rtl languages)
52234 * @cfg {boolean} allowComments - default false - allow comments in HTML source
52235 * - by default they are stripped - if you are editing email you may need this.
52237 allowComments: false,
52241 // private properties
52242 validationEvent : false,
52244 initialized : false,
52246 sourceEditMode : false,
52247 onFocus : Roo.emptyFn,
52249 hideMode:'offsets',
52253 // blacklist + whitelisted elements..
52260 undoManager : false,
52262 * Protected method that will not generally be called directly. It
52263 * is called when the editor initializes the iframe with HTML contents. Override this method if you
52264 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
52266 getDocMarkup : function(){
52270 // inherit styels from page...??
52271 if (this.stylesheets === false) {
52273 Roo.get(document.head).select('style').each(function(node) {
52274 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
52277 Roo.get(document.head).select('link').each(function(node) {
52278 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
52281 } else if (!this.stylesheets.length) {
52283 st = '<style type="text/css">' +
52284 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
52287 for (var i in this.stylesheets) {
52288 if (typeof(this.stylesheets[i]) != 'string') {
52291 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
52296 st += '<style type="text/css">' +
52297 'IMG { cursor: pointer } ' +
52300 st += '<meta name="google" content="notranslate">';
52302 var cls = 'notranslate roo-htmleditor-body';
52304 if(this.bodyCls.length){
52305 cls += ' ' + this.bodyCls;
52308 return '<html class="notranslate" translate="no"><head>' + st +
52309 //<style type="text/css">' +
52310 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
52312 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
52316 onRender : function(ct, position)
52319 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
52320 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
52323 this.el.dom.style.border = '0 none';
52324 this.el.dom.setAttribute('tabIndex', -1);
52325 this.el.addClass('x-hidden hide');
52329 if(Roo.isIE){ // fix IE 1px bogus margin
52330 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
52334 this.frameId = Roo.id();
52338 cls: 'form-control', // bootstrap..
52340 name: this.frameId,
52341 frameBorder : 'no',
52342 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
52345 ifcfg.style = { resize : this.resize };
52348 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
52351 this.iframe = iframe.dom;
52353 this.assignDocWin();
52355 this.doc.designMode = 'on';
52358 this.doc.write(this.getDocMarkup());
52362 var task = { // must defer to wait for browser to be ready
52364 //console.log("run task?" + this.doc.readyState);
52365 this.assignDocWin();
52366 if(this.doc.body || this.doc.readyState == 'complete'){
52368 this.doc.designMode="on";
52373 Roo.TaskMgr.stop(task);
52374 this.initEditor.defer(10, this);
52381 Roo.TaskMgr.start(task);
52386 onResize : function(w, h)
52388 Roo.log('resize: ' +w + ',' + h );
52389 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
52393 if(typeof w == 'number'){
52395 this.iframe.style.width = w + 'px';
52397 if(typeof h == 'number'){
52399 this.iframe.style.height = h + 'px';
52401 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
52408 * Toggles the editor between standard and source edit mode.
52409 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
52411 toggleSourceEdit : function(sourceEditMode){
52413 this.sourceEditMode = sourceEditMode === true;
52415 if(this.sourceEditMode){
52417 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
52420 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
52421 //this.iframe.className = '';
52424 //this.setSize(this.owner.wrap.getSize());
52425 //this.fireEvent('editmodechange', this, this.sourceEditMode);
52432 * Protected method that will not generally be called directly. If you need/want
52433 * custom HTML cleanup, this is the method you should override.
52434 * @param {String} html The HTML to be cleaned
52435 * return {String} The cleaned HTML
52437 cleanHtml : function(html)
52439 html = String(html);
52440 if(html.length > 5){
52441 if(Roo.isSafari){ // strip safari nonsense
52442 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
52445 if(html == ' '){
52452 * HTML Editor -> Textarea
52453 * Protected method that will not generally be called directly. Syncs the contents
52454 * of the editor iframe with the textarea.
52456 syncValue : function()
52458 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
52459 if(this.initialized){
52461 if (this.undoManager) {
52462 this.undoManager.addEvent();
52466 var bd = (this.doc.body || this.doc.documentElement);
52469 var sel = this.win.getSelection();
52471 var div = document.createElement('div');
52472 div.innerHTML = bd.innerHTML;
52473 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
52474 if (gtx.length > 0) {
52475 var rm = gtx.item(0).parentNode;
52476 rm.parentNode.removeChild(rm);
52480 if (this.enableBlocks) {
52481 Array.from(bd.getElementsByTagName('img')).forEach(function(img) {
52482 var fig = img.closest('figure');
52484 var bf = new Roo.htmleditor.BlockFigure({
52487 bf.updateElement();
52491 new Roo.htmleditor.FilterBlock({ node : div });
52494 var html = div.innerHTML;
52497 if (this.autoClean) {
52498 new Roo.htmleditor.FilterBlack({ node : div, tag : this.black});
52499 new Roo.htmleditor.FilterAttributes({
52509 'data-caption-display',
52522 attrib_clean : ['href', 'src' ]
52524 new Roo.htmleditor.FilterEmpty({ node : div});
52526 var tidy = new Roo.htmleditor.TidySerializer({
52529 html = tidy.serialize(div);
52535 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
52536 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
52538 html = '<div style="'+m[0]+'">' + html + '</div>';
52541 html = this.cleanHtml(html);
52542 // fix up the special chars.. normaly like back quotes in word...
52543 // however we do not want to do this with chinese..
52544 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
52546 var cc = match.charCodeAt();
52548 // Get the character value, handling surrogate pairs
52549 if (match.length == 2) {
52550 // It's a surrogate pair, calculate the Unicode code point
52551 var high = match.charCodeAt(0) - 0xD800;
52552 var low = match.charCodeAt(1) - 0xDC00;
52553 cc = (high * 0x400) + low + 0x10000;
52555 (cc >= 0x4E00 && cc < 0xA000 ) ||
52556 (cc >= 0x3400 && cc < 0x4E00 ) ||
52557 (cc >= 0xf900 && cc < 0xfb00 )
52562 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
52563 return "&#" + cc + ";";
52570 if(this.owner.fireEvent('beforesync', this, html) !== false){
52571 this.el.dom.value = html;
52572 this.owner.fireEvent('sync', this, html);
52578 * TEXTAREA -> EDITABLE
52579 * Protected method that will not generally be called directly. Pushes the value of the textarea
52580 * into the iframe editor.
52582 pushValue : function()
52584 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
52585 if(this.initialized){
52586 var v = this.el.dom.value.trim();
52589 if(this.owner.fireEvent('beforepush', this, v) !== false){
52590 var d = (this.doc.body || this.doc.documentElement);
52593 this.el.dom.value = d.innerHTML;
52594 this.owner.fireEvent('push', this, v);
52596 if (this.autoClean) {
52597 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
52598 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
52600 if (this.enableBlocks) {
52601 Roo.htmleditor.Block.initAll(this.doc.body);
52604 this.updateLanguage();
52606 var lc = this.doc.body.lastChild;
52607 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
52608 // add an extra line at the end.
52609 this.doc.body.appendChild(this.doc.createElement('br'));
52617 deferFocus : function(){
52618 this.focus.defer(10, this);
52622 focus : function(){
52623 if(this.win && !this.sourceEditMode){
52630 assignDocWin: function()
52632 var iframe = this.iframe;
52635 this.doc = iframe.contentWindow.document;
52636 this.win = iframe.contentWindow;
52638 // if (!Roo.get(this.frameId)) {
52641 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
52642 // this.win = Roo.get(this.frameId).dom.contentWindow;
52644 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
52648 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
52649 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
52654 initEditor : function(){
52655 //console.log("INIT EDITOR");
52656 this.assignDocWin();
52660 this.doc.designMode="on";
52662 this.doc.write(this.getDocMarkup());
52665 var dbody = (this.doc.body || this.doc.documentElement);
52666 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
52667 // this copies styles from the containing element into thsi one..
52668 // not sure why we need all of this..
52669 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
52671 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
52672 //ss['background-attachment'] = 'fixed'; // w3c
52673 dbody.bgProperties = 'fixed'; // ie
52674 dbody.setAttribute("translate", "no");
52676 //Roo.DomHelper.applyStyles(dbody, ss);
52677 Roo.EventManager.on(this.doc, {
52679 'mouseup': this.onEditorEvent,
52680 'dblclick': this.onEditorEvent,
52681 'click': this.onEditorEvent,
52682 'keyup': this.onEditorEvent,
52687 Roo.EventManager.on(this.doc, {
52688 'paste': this.onPasteEvent,
52692 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
52695 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
52696 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
52698 this.initialized = true;
52701 // initialize special key events - enter
52702 new Roo.htmleditor.KeyEnter({core : this});
52706 this.owner.fireEvent('initialize', this);
52709 // this is to prevent a href clicks resulting in a redirect?
52711 onPasteEvent : function(e,v)
52713 // I think we better assume paste is going to be a dirty load of rubish from word..
52715 // even pasting into a 'email version' of this widget will have to clean up that mess.
52716 var cd = (e.browserEvent.clipboardData || window.clipboardData);
52718 // check what type of paste - if it's an image, then handle it differently.
52719 if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
52721 var urlAPI = (window.createObjectURL && window) ||
52722 (window.URL && URL.revokeObjectURL && URL) ||
52723 (window.webkitURL && webkitURL);
52725 var r = new FileReader();
52727 r.addEventListener('load',function()
52730 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
52731 // is insert asycn?
52732 if (t.enableBlocks) {
52734 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
52735 if (img.closest('figure')) { // assume!! that it's aready
52738 var fig = new Roo.htmleditor.BlockFigure({
52739 image_src : img.src
52741 fig.updateElement(img); // replace it..
52745 t.insertAtCursor(d.innerHTML.replace(/ /g,' '));
52746 t.owner.fireEvent('paste', this);
52748 r.readAsDataURL(cd.files[0]);
52750 e.preventDefault();
52754 if (cd.types.indexOf('text/html') < 0 ) {
52758 var html = cd.getData('text/html'); // clipboard event
52759 if (cd.types.indexOf('text/rtf') > -1) {
52760 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
52761 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
52763 // Roo.log(images);
52766 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
52767 .map(function(g) { return g.toDataURL(); })
52768 .filter(function(g) { return g != 'about:blank'; });
52771 html = this.cleanWordChars(html);
52773 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
52776 var sn = this.getParentElement();
52777 // check if d contains a table, and prevent nesting??
52778 //Roo.log(d.getElementsByTagName('table'));
52780 //Roo.log(sn.closest('table'));
52781 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
52782 e.preventDefault();
52783 this.insertAtCursor("You can not nest tables");
52784 //Roo.log("prevent?"); // fixme -
52790 if (images.length > 0) {
52791 // replace all v:imagedata - with img.
52792 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
52793 Roo.each(ar, function(node) {
52794 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
52795 node.parentNode.removeChild(node);
52799 Roo.each(d.getElementsByTagName('img'), function(img, i) {
52800 img.setAttribute('src', images[i]);
52803 if (this.autoClean) {
52804 new Roo.htmleditor.FilterWord({ node : d });
52806 new Roo.htmleditor.FilterStyleToTag({ node : d });
52807 new Roo.htmleditor.FilterAttributes({
52816 /* THESE ARE NOT ALLWOED FOR PASTE
52818 'data-caption-display',
52832 attrib_clean : ['href', 'src' ]
52834 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
52835 // should be fonts..
52836 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
52837 new Roo.htmleditor.FilterParagraph({ node : d });
52838 new Roo.htmleditor.FilterHashLink({node : d});
52839 new Roo.htmleditor.FilterSpan({ node : d });
52840 new Roo.htmleditor.FilterLongBr({ node : d });
52841 new Roo.htmleditor.FilterComment({ node : d });
52842 new Roo.htmleditor.FilterEmpty({ node : d});
52846 if (this.enableBlocks) {
52848 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
52849 if (img.closest('figure')) { // assume!! that it's aready
52852 var fig = new Roo.htmleditor.BlockFigure({
52853 image_src : img.src
52855 fig.updateElement(img); // replace it..
52861 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
52862 if (this.enableBlocks) {
52863 Roo.htmleditor.Block.initAll(this.doc.body);
52867 e.preventDefault();
52868 this.owner.fireEvent('paste', this);
52870 // default behaveiour should be our local cleanup paste? (optional?)
52871 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
52872 //this.owner.fireEvent('paste', e, v);
52875 onDestroy : function(){
52881 //for (var i =0; i < this.toolbars.length;i++) {
52882 // // fixme - ask toolbars for heights?
52883 // this.toolbars[i].onDestroy();
52886 //this.wrap.dom.innerHTML = '';
52887 //this.wrap.remove();
52892 onFirstFocus : function(){
52894 this.assignDocWin();
52895 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
52897 this.activated = true;
52900 if(Roo.isGecko){ // prevent silly gecko errors
52902 var s = this.win.getSelection();
52903 if(!s.focusNode || s.focusNode.nodeType != 3){
52904 var r = s.getRangeAt(0);
52905 r.selectNodeContents((this.doc.body || this.doc.documentElement));
52910 this.execCmd('useCSS', true);
52911 this.execCmd('styleWithCSS', false);
52914 this.owner.fireEvent('activate', this);
52918 adjustFont: function(btn){
52919 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
52920 //if(Roo.isSafari){ // safari
52923 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
52924 if(Roo.isSafari){ // safari
52925 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
52926 v = (v < 10) ? 10 : v;
52927 v = (v > 48) ? 48 : v;
52928 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
52933 v = Math.max(1, v+adjust);
52935 this.execCmd('FontSize', v );
52938 onEditorEvent : function(e)
52942 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
52943 return; // we do not handle this.. (undo manager does..)
52945 // clicking a 'block'?
52947 // in theory this detects if the last element is not a br, then we try and do that.
52948 // its so clicking in space at bottom triggers adding a br and moving the cursor.
52950 e.target.nodeName == 'BODY' &&
52951 e.type == "mouseup" &&
52952 this.doc.body.lastChild
52954 var lc = this.doc.body.lastChild;
52955 // gtx-trans is google translate plugin adding crap.
52956 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
52957 lc = lc.previousSibling;
52959 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
52960 // if last element is <BR> - then dont do anything.
52962 var ns = this.doc.createElement('br');
52963 this.doc.body.appendChild(ns);
52964 range = this.doc.createRange();
52965 range.setStartAfter(ns);
52966 range.collapse(true);
52967 var sel = this.win.getSelection();
52968 sel.removeAllRanges();
52969 sel.addRange(range);
52975 this.fireEditorEvent(e);
52976 // this.updateToolbar();
52977 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
52980 fireEditorEvent: function(e)
52982 this.owner.fireEvent('editorevent', this, e);
52985 insertTag : function(tg)
52987 // could be a bit smarter... -> wrap the current selected tRoo..
52988 if (tg.toLowerCase() == 'span' ||
52989 tg.toLowerCase() == 'code' ||
52990 tg.toLowerCase() == 'sup' ||
52991 tg.toLowerCase() == 'sub'
52994 range = this.createRange(this.getSelection());
52995 var wrappingNode = this.doc.createElement(tg.toLowerCase());
52996 wrappingNode.appendChild(range.extractContents());
52997 range.insertNode(wrappingNode);
53004 this.execCmd("formatblock", tg);
53005 this.undoManager.addEvent();
53008 insertText : function(txt)
53012 var range = this.createRange();
53013 range.deleteContents();
53014 //alert(Sender.getAttribute('label'));
53016 range.insertNode(this.doc.createTextNode(txt));
53017 this.undoManager.addEvent();
53023 * Executes a Midas editor command on the editor document and performs necessary focus and
53024 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
53025 * @param {String} cmd The Midas command
53026 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
53028 relayCmd : function(cmd, value)
53032 case 'justifyleft':
53033 case 'justifyright':
53034 case 'justifycenter':
53035 // if we are in a cell, then we will adjust the
53036 var n = this.getParentElement();
53037 var td = n.closest('td');
53039 var bl = Roo.htmleditor.Block.factory(td);
53040 bl.textAlign = cmd.replace('justify','');
53041 bl.updateElement();
53042 this.owner.fireEvent('editorevent', this);
53045 this.execCmd('styleWithCSS', true); //
53050 // if there is no selection, then we insert, and set the curson inside it..
53051 this.execCmd('styleWithCSS', false);
53061 this.execCmd(cmd, value);
53062 this.owner.fireEvent('editorevent', this);
53063 //this.updateToolbar();
53064 this.owner.deferFocus();
53068 * Executes a Midas editor command directly on the editor document.
53069 * For visual commands, you should use {@link #relayCmd} instead.
53070 * <b>This should only be called after the editor is initialized.</b>
53071 * @param {String} cmd The Midas command
53072 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
53074 execCmd : function(cmd, value){
53075 this.doc.execCommand(cmd, false, value === undefined ? null : value);
53082 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
53084 * @param {String} text | dom node..
53086 insertAtCursor : function(text)
53089 if(!this.activated){
53093 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
53097 // from jquery ui (MIT licenced)
53099 var win = this.win;
53101 if (win.getSelection && win.getSelection().getRangeAt) {
53103 // delete the existing?
53105 this.createRange(this.getSelection()).deleteContents();
53106 range = win.getSelection().getRangeAt(0);
53107 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
53108 range.insertNode(node);
53109 range = range.cloneRange();
53110 range.collapse(false);
53112 win.getSelection().removeAllRanges();
53113 win.getSelection().addRange(range);
53117 } else if (win.document.selection && win.document.selection.createRange) {
53118 // no firefox support
53119 var txt = typeof(text) == 'string' ? text : text.outerHTML;
53120 win.document.selection.createRange().pasteHTML(txt);
53123 // no firefox support
53124 var txt = typeof(text) == 'string' ? text : text.outerHTML;
53125 this.execCmd('InsertHTML', txt);
53133 mozKeyPress : function(e){
53135 var c = e.getCharCode(), cmd;
53138 c = String.fromCharCode(c).toLowerCase();
53152 // this.cleanUpPaste.defer(100, this);
53158 this.relayCmd(cmd);
53159 //this.win.focus();
53160 //this.execCmd(cmd);
53161 //this.deferFocus();
53162 e.preventDefault();
53170 fixKeys : function(){ // load time branching for fastest keydown performance
53174 return function(e){
53175 var k = e.getKey(), r;
53178 r = this.doc.selection.createRange();
53181 r.pasteHTML('    ');
53186 /// this is handled by Roo.htmleditor.KeyEnter
53189 r = this.doc.selection.createRange();
53191 var target = r.parentElement();
53192 if(!target || target.tagName.toLowerCase() != 'li'){
53194 r.pasteHTML('<br/>');
53201 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53202 // this.cleanUpPaste.defer(100, this);
53208 }else if(Roo.isOpera){
53209 return function(e){
53210 var k = e.getKey();
53214 this.execCmd('InsertHTML','    ');
53218 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53219 // this.cleanUpPaste.defer(100, this);
53224 }else if(Roo.isSafari){
53225 return function(e){
53226 var k = e.getKey();
53230 this.execCmd('InsertText','\t');
53234 this.mozKeyPress(e);
53236 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
53237 // this.cleanUpPaste.defer(100, this);
53245 getAllAncestors: function()
53247 var p = this.getSelectedNode();
53250 a.push(p); // push blank onto stack..
53251 p = this.getParentElement();
53255 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
53259 a.push(this.doc.body);
53263 lastSelNode : false,
53266 getSelection : function()
53268 this.assignDocWin();
53269 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
53272 * Select a dom node
53273 * @param {DomElement} node the node to select
53275 selectNode : function(node, collapse)
53277 var nodeRange = node.ownerDocument.createRange();
53279 nodeRange.selectNode(node);
53281 nodeRange.selectNodeContents(node);
53283 if (collapse === true) {
53284 nodeRange.collapse(true);
53287 var s = this.win.getSelection();
53288 s.removeAllRanges();
53289 s.addRange(nodeRange);
53292 getSelectedNode: function()
53294 // this may only work on Gecko!!!
53296 // should we cache this!!!!
53300 var range = this.createRange(this.getSelection()).cloneRange();
53303 var parent = range.parentElement();
53305 var testRange = range.duplicate();
53306 testRange.moveToElementText(parent);
53307 if (testRange.inRange(range)) {
53310 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
53313 parent = parent.parentElement;
53318 // is ancestor a text element.
53319 var ac = range.commonAncestorContainer;
53320 if (ac.nodeType == 3) {
53321 ac = ac.parentNode;
53324 var ar = ac.childNodes;
53327 var other_nodes = [];
53328 var has_other_nodes = false;
53329 for (var i=0;i<ar.length;i++) {
53330 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
53333 // fullly contained node.
53335 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
53340 // probably selected..
53341 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
53342 other_nodes.push(ar[i]);
53346 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
53351 has_other_nodes = true;
53353 if (!nodes.length && other_nodes.length) {
53354 nodes= other_nodes;
53356 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
53364 createRange: function(sel)
53366 // this has strange effects when using with
53367 // top toolbar - not sure if it's a great idea.
53368 //this.editor.contentWindow.focus();
53369 if (typeof sel != "undefined") {
53371 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
53373 return this.doc.createRange();
53376 return this.doc.createRange();
53379 getParentElement: function()
53382 this.assignDocWin();
53383 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
53385 var range = this.createRange(sel);
53388 var p = range.commonAncestorContainer;
53389 while (p.nodeType == 3) { // text node
53400 * Range intersection.. the hard stuff...
53404 * [ -- selected range --- ]
53408 * if end is before start or hits it. fail.
53409 * if start is after end or hits it fail.
53411 * if either hits (but other is outside. - then it's not
53417 // @see http://www.thismuchiknow.co.uk/?p=64.
53418 rangeIntersectsNode : function(range, node)
53420 var nodeRange = node.ownerDocument.createRange();
53422 nodeRange.selectNode(node);
53424 nodeRange.selectNodeContents(node);
53427 var rangeStartRange = range.cloneRange();
53428 rangeStartRange.collapse(true);
53430 var rangeEndRange = range.cloneRange();
53431 rangeEndRange.collapse(false);
53433 var nodeStartRange = nodeRange.cloneRange();
53434 nodeStartRange.collapse(true);
53436 var nodeEndRange = nodeRange.cloneRange();
53437 nodeEndRange.collapse(false);
53439 return rangeStartRange.compareBoundaryPoints(
53440 Range.START_TO_START, nodeEndRange) == -1 &&
53441 rangeEndRange.compareBoundaryPoints(
53442 Range.START_TO_START, nodeStartRange) == 1;
53446 rangeCompareNode : function(range, node)
53448 var nodeRange = node.ownerDocument.createRange();
53450 nodeRange.selectNode(node);
53452 nodeRange.selectNodeContents(node);
53456 range.collapse(true);
53458 nodeRange.collapse(true);
53460 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
53461 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
53463 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
53465 var nodeIsBefore = ss == 1;
53466 var nodeIsAfter = ee == -1;
53468 if (nodeIsBefore && nodeIsAfter) {
53471 if (!nodeIsBefore && nodeIsAfter) {
53472 return 1; //right trailed.
53475 if (nodeIsBefore && !nodeIsAfter) {
53476 return 2; // left trailed.
53482 cleanWordChars : function(input) {// change the chars to hex code
53485 [ 8211, "–" ],
53486 [ 8212, "—" ],
53494 var output = input;
53495 Roo.each(swapCodes, function(sw) {
53496 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
53498 output = output.replace(swapper, sw[1]);
53508 cleanUpChild : function (node)
53511 new Roo.htmleditor.FilterComment({node : node});
53512 new Roo.htmleditor.FilterAttributes({
53514 attrib_black : this.ablack,
53515 attrib_clean : this.aclean,
53516 style_white : this.cwhite,
53517 style_black : this.cblack
53519 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
53520 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
53526 * Clean up MS wordisms...
53527 * @deprecated - use filter directly
53529 cleanWord : function(node)
53531 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
53532 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
53539 * @deprecated - use filters
53541 cleanTableWidths : function(node)
53543 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
53550 applyBlacklists : function()
53552 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
53553 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
53555 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
53556 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
53557 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
53561 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
53562 if (b.indexOf(tag) > -1) {
53565 this.white.push(tag);
53569 Roo.each(w, function(tag) {
53570 if (b.indexOf(tag) > -1) {
53573 if (this.white.indexOf(tag) > -1) {
53576 this.white.push(tag);
53581 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
53582 if (w.indexOf(tag) > -1) {
53585 this.black.push(tag);
53589 Roo.each(b, function(tag) {
53590 if (w.indexOf(tag) > -1) {
53593 if (this.black.indexOf(tag) > -1) {
53596 this.black.push(tag);
53601 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
53602 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
53606 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
53607 if (b.indexOf(tag) > -1) {
53610 this.cwhite.push(tag);
53614 Roo.each(w, function(tag) {
53615 if (b.indexOf(tag) > -1) {
53618 if (this.cwhite.indexOf(tag) > -1) {
53621 this.cwhite.push(tag);
53626 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
53627 if (w.indexOf(tag) > -1) {
53630 this.cblack.push(tag);
53634 Roo.each(b, function(tag) {
53635 if (w.indexOf(tag) > -1) {
53638 if (this.cblack.indexOf(tag) > -1) {
53641 this.cblack.push(tag);
53646 setStylesheets : function(stylesheets)
53648 if(typeof(stylesheets) == 'string'){
53649 Roo.get(this.iframe.contentDocument.head).createChild({
53651 rel : 'stylesheet',
53660 Roo.each(stylesheets, function(s) {
53665 Roo.get(_this.iframe.contentDocument.head).createChild({
53667 rel : 'stylesheet',
53677 updateLanguage : function()
53679 if (!this.iframe || !this.iframe.contentDocument) {
53682 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
53686 removeStylesheets : function()
53690 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
53695 setStyle : function(style)
53697 Roo.get(this.iframe.contentDocument.head).createChild({
53706 // hide stuff that is not compatible
53720 * @event specialkey
53724 * @cfg {String} fieldClass @hide
53727 * @cfg {String} focusClass @hide
53730 * @cfg {String} autoCreate @hide
53733 * @cfg {String} inputType @hide
53736 * @cfg {String} invalidClass @hide
53739 * @cfg {String} invalidText @hide
53742 * @cfg {String} msgFx @hide
53745 * @cfg {String} validateOnBlur @hide
53749 Roo.HtmlEditorCore.white = [
53750 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
53752 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
53753 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
53754 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
53755 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
53756 'TABLE', 'UL', 'XMP',
53758 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
53761 'DIR', 'MENU', 'OL', 'UL', 'DL',
53767 Roo.HtmlEditorCore.black = [
53768 // 'embed', 'object', // enable - backend responsiblity to clean thiese
53770 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
53771 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
53772 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
53773 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
53774 //'FONT' // CLEAN LATER..
53775 'COLGROUP', 'COL' // messy tables.
53779 Roo.HtmlEditorCore.clean = [ // ?? needed???
53780 'SCRIPT', 'STYLE', 'TITLE', 'XML'
53782 Roo.HtmlEditorCore.tag_remove = [
53787 Roo.HtmlEditorCore.ablack = [
53791 Roo.HtmlEditorCore.aclean = [
53792 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
53796 Roo.HtmlEditorCore.pwhite= [
53797 'http', 'https', 'mailto'
53800 // white listed style attributes.
53801 Roo.HtmlEditorCore.cwhite= [
53802 // 'text-align', /// default is to allow most things..
53808 // black listed style attributes.
53809 Roo.HtmlEditorCore.cblack= [
53810 // 'font-size' -- this can be set by the project
53816 //<script type="text/javascript">
53819 * Ext JS Library 1.1.1
53820 * Copyright(c) 2006-2007, Ext JS, LLC.
53826 Roo.form.HtmlEditor = function(config){
53830 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
53832 if (!this.toolbars) {
53833 this.toolbars = [];
53835 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
53841 * @class Roo.form.HtmlEditor
53842 * @extends Roo.form.Field
53843 * Provides a lightweight HTML Editor component.
53845 * This has been tested on Fireforx / Chrome.. IE may not be so great..
53847 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
53848 * supported by this editor.</b><br/><br/>
53849 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
53850 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
53852 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
53854 * @cfg {Boolean} clearUp
53858 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
53863 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
53868 * @cfg {Number} height (in pixels)
53872 * @cfg {Number} width (in pixels)
53877 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
53880 stylesheets: false,
53884 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
53889 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
53895 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
53900 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
53905 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
53907 allowComments: false,
53909 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
53911 enableBlocks : true,
53914 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
53915 * if you are doing an email editor, this probably needs disabling, it's designed
53919 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
53923 * @cfg {String} language default en - language of text (usefull for rtl languages)
53932 // private properties
53933 validationEvent : false,
53935 initialized : false,
53938 onFocus : Roo.emptyFn,
53940 hideMode:'offsets',
53942 actionMode : 'container', // defaults to hiding it...
53944 defaultAutoCreate : { // modified by initCompnoent..
53946 style:"width:500px;height:300px;",
53947 autocomplete: "new-password"
53951 initComponent : function(){
53954 * @event initialize
53955 * Fires when the editor is fully initialized (including the iframe)
53956 * @param {HtmlEditor} this
53961 * Fires when the editor is first receives the focus. Any insertion must wait
53962 * until after this event.
53963 * @param {HtmlEditor} this
53967 * @event beforesync
53968 * Fires before the textarea is updated with content from the editor iframe. Return false
53969 * to cancel the sync.
53970 * @param {HtmlEditor} this
53971 * @param {String} html
53975 * @event beforepush
53976 * Fires before the iframe editor is updated with content from the textarea. Return false
53977 * to cancel the push.
53978 * @param {HtmlEditor} this
53979 * @param {String} html
53984 * Fires when the textarea is updated with content from the editor iframe.
53985 * @param {HtmlEditor} this
53986 * @param {String} html
53991 * Fires when the iframe editor is updated with content from the textarea.
53992 * @param {HtmlEditor} this
53993 * @param {String} html
53997 * @event editmodechange
53998 * Fires when the editor switches edit modes
53999 * @param {HtmlEditor} this
54000 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
54002 editmodechange: true,
54004 * @event editorevent
54005 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
54006 * @param {HtmlEditor} this
54010 * @event firstfocus
54011 * Fires when on first focus - needed by toolbars..
54012 * @param {HtmlEditor} this
54017 * Auto save the htmlEditor value as a file into Events
54018 * @param {HtmlEditor} this
54022 * @event savedpreview
54023 * preview the saved version of htmlEditor
54024 * @param {HtmlEditor} this
54026 savedpreview: true,
54029 * @event stylesheetsclick
54030 * Fires when press the Sytlesheets button
54031 * @param {Roo.HtmlEditorCore} this
54033 stylesheetsclick: true,
54036 * Fires when press user pastes into the editor
54037 * @param {Roo.HtmlEditorCore} this
54042 this.defaultAutoCreate = {
54044 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
54045 autocomplete: "new-password"
54050 * Protected method that will not generally be called directly. It
54051 * is called when the editor creates its toolbar. Override this method if you need to
54052 * add custom toolbar buttons.
54053 * @param {HtmlEditor} editor
54055 createToolbar : function(editor){
54056 Roo.log("create toolbars");
54057 if (!editor.toolbars || !editor.toolbars.length) {
54058 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
54061 for (var i =0 ; i < editor.toolbars.length;i++) {
54062 editor.toolbars[i] = Roo.factory(
54063 typeof(editor.toolbars[i]) == 'string' ?
54064 { xtype: editor.toolbars[i]} : editor.toolbars[i],
54065 Roo.form.HtmlEditor);
54066 editor.toolbars[i].init(editor);
54072 * get the Context selected node
54073 * @returns {DomElement|boolean} selected node if active or false if none
54076 getSelectedNode : function()
54078 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
54081 return this.toolbars[1].tb.selectedNode;
54085 onRender : function(ct, position)
54088 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
54090 this.wrap = this.el.wrap({
54091 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
54094 this.editorcore.onRender(ct, position);
54096 if (this.resizable) {
54097 this.resizeEl = new Roo.Resizable(this.wrap, {
54101 minHeight : this.height,
54102 height: this.height,
54103 handles : this.resizable,
54106 resize : function(r, w, h) {
54107 _t.onResize(w,h); // -something
54113 this.createToolbar(this);
54117 this.setSize(this.wrap.getSize());
54119 if (this.resizeEl) {
54120 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
54121 // should trigger onReize..
54124 this.keyNav = new Roo.KeyNav(this.el, {
54126 "tab" : function(e){
54127 e.preventDefault();
54129 var value = this.getValue();
54131 var start = this.el.dom.selectionStart;
54132 var end = this.el.dom.selectionEnd;
54136 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
54137 this.el.dom.setSelectionRange(end + 1, end + 1);
54141 var f = value.substring(0, start).split("\t");
54143 if(f.pop().length != 0){
54147 this.setValue(f.join("\t") + value.substring(end));
54148 this.el.dom.setSelectionRange(start - 1, start - 1);
54152 "home" : function(e){
54153 e.preventDefault();
54155 var curr = this.el.dom.selectionStart;
54156 var lines = this.getValue().split("\n");
54163 this.el.dom.setSelectionRange(0, 0);
54169 for (var i = 0; i < lines.length;i++) {
54170 pos += lines[i].length;
54180 pos -= lines[i].length;
54186 this.el.dom.setSelectionRange(pos, pos);
54190 this.el.dom.selectionStart = pos;
54191 this.el.dom.selectionEnd = curr;
54194 "end" : function(e){
54195 e.preventDefault();
54197 var curr = this.el.dom.selectionStart;
54198 var lines = this.getValue().split("\n");
54205 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
54211 for (var i = 0; i < lines.length;i++) {
54213 pos += lines[i].length;
54227 this.el.dom.setSelectionRange(pos, pos);
54231 this.el.dom.selectionStart = curr;
54232 this.el.dom.selectionEnd = pos;
54237 doRelay : function(foo, bar, hname){
54238 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
54244 // if(this.autosave && this.w){
54245 // this.autoSaveFn = setInterval(this.autosave, 1000);
54250 onResize : function(w, h)
54252 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
54257 if(typeof w == 'number'){
54258 var aw = w - this.wrap.getFrameWidth('lr');
54259 this.el.setWidth(this.adjustWidth('textarea', aw));
54262 if(typeof h == 'number'){
54264 for (var i =0; i < this.toolbars.length;i++) {
54265 // fixme - ask toolbars for heights?
54266 tbh += this.toolbars[i].tb.el.getHeight();
54267 if (this.toolbars[i].footer) {
54268 tbh += this.toolbars[i].footer.el.getHeight();
54275 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
54276 ah -= 5; // knock a few pixes off for look..
54278 this.el.setHeight(this.adjustWidth('textarea', ah));
54282 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
54283 this.editorcore.onResize(ew,eh);
54288 * Toggles the editor between standard and source edit mode.
54289 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
54291 toggleSourceEdit : function(sourceEditMode)
54293 this.editorcore.toggleSourceEdit(sourceEditMode);
54295 if(this.editorcore.sourceEditMode){
54296 Roo.log('editor - showing textarea');
54299 // Roo.log(this.syncValue());
54300 this.editorcore.syncValue();
54301 this.el.removeClass('x-hidden');
54302 this.el.dom.removeAttribute('tabIndex');
54304 this.el.dom.scrollTop = 0;
54307 for (var i = 0; i < this.toolbars.length; i++) {
54308 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
54309 this.toolbars[i].tb.hide();
54310 this.toolbars[i].footer.hide();
54315 Roo.log('editor - hiding textarea');
54317 // Roo.log(this.pushValue());
54318 this.editorcore.pushValue();
54320 this.el.addClass('x-hidden');
54321 this.el.dom.setAttribute('tabIndex', -1);
54323 for (var i = 0; i < this.toolbars.length; i++) {
54324 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
54325 this.toolbars[i].tb.show();
54326 this.toolbars[i].footer.show();
54330 //this.deferFocus();
54333 this.setSize(this.wrap.getSize());
54334 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
54336 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
54339 // private (for BoxComponent)
54340 adjustSize : Roo.BoxComponent.prototype.adjustSize,
54342 // private (for BoxComponent)
54343 getResizeEl : function(){
54347 // private (for BoxComponent)
54348 getPositionEl : function(){
54353 initEvents : function(){
54354 this.originalValue = this.getValue();
54358 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
54361 markInvalid : Roo.emptyFn,
54363 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
54366 clearInvalid : Roo.emptyFn,
54368 setValue : function(v){
54369 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
54370 this.editorcore.pushValue();
54374 * update the language in the body - really done by core
54375 * @param {String} language - eg. en / ar / zh-CN etc..
54377 updateLanguage : function(lang)
54379 this.language = lang;
54380 this.editorcore.language = lang;
54381 this.editorcore.updateLanguage();
54385 deferFocus : function(){
54386 this.focus.defer(10, this);
54390 focus : function(){
54391 this.editorcore.focus();
54397 onDestroy : function(){
54403 for (var i =0; i < this.toolbars.length;i++) {
54404 // fixme - ask toolbars for heights?
54405 this.toolbars[i].onDestroy();
54408 this.wrap.dom.innerHTML = '';
54409 this.wrap.remove();
54414 onFirstFocus : function(){
54415 //Roo.log("onFirstFocus");
54416 this.editorcore.onFirstFocus();
54417 for (var i =0; i < this.toolbars.length;i++) {
54418 this.toolbars[i].onFirstFocus();
54424 syncValue : function()
54426 this.editorcore.syncValue();
54429 pushValue : function()
54431 this.editorcore.pushValue();
54434 setStylesheets : function(stylesheets)
54436 this.editorcore.setStylesheets(stylesheets);
54439 removeStylesheets : function()
54441 this.editorcore.removeStylesheets();
54445 // hide stuff that is not compatible
54459 * @event specialkey
54463 * @cfg {String} fieldClass @hide
54466 * @cfg {String} focusClass @hide
54469 * @cfg {String} autoCreate @hide
54472 * @cfg {String} inputType @hide
54475 * @cfg {String} invalidClass @hide
54478 * @cfg {String} invalidText @hide
54481 * @cfg {String} msgFx @hide
54484 * @cfg {String} validateOnBlur @hide
54490 * Ext JS Library 1.1.1
54491 * Copyright(c) 2006-2007, Ext JS, LLC.
54497 * @class Roo.form.HtmlEditor.ToolbarStandard
54502 new Roo.form.HtmlEditor({
54505 new Roo.form.HtmlEditorToolbar1({
54506 disable : { fonts: 1 , format: 1, ..., ... , ...],
54512 * @cfg {Object} disable List of elements to disable..
54513 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
54517 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
54520 Roo.form.HtmlEditor.ToolbarStandard = function(config)
54523 Roo.apply(this, config);
54525 // default disabled, based on 'good practice'..
54526 this.disable = this.disable || {};
54527 Roo.applyIf(this.disable, {
54530 specialElements : true
54534 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
54535 // dont call parent... till later.
54538 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
54545 editorcore : false,
54547 * @cfg {Object} disable List of toolbar elements to disable
54554 * @cfg {String} createLinkText The default text for the create link prompt
54556 createLinkText : 'Please enter the URL for the link:',
54558 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
54560 defaultLinkValue : 'http:/'+'/',
54564 * @cfg {Array} fontFamilies An array of available font families
54582 // "á" , ?? a acute?
54587 "°" // , // degrees
54589 // "é" , // e ecute
54590 // "ú" , // u ecute?
54593 specialElements : [
54595 text: "Insert Table",
54598 ihtml : '<table><tr><td>Cell</td></tr></table>'
54602 text: "Insert Image",
54605 ihtml : '<img src="about:blank"/>'
54614 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
54615 "input:submit", "input:button", "select", "textarea", "label" ],
54618 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
54620 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
54629 * @cfg {String} defaultFont default font to use.
54631 defaultFont: 'tahoma',
54633 fontSelect : false,
54636 formatCombo : false,
54638 init : function(editor)
54640 this.editor = editor;
54641 this.editorcore = editor.editorcore ? editor.editorcore : editor;
54642 var editorcore = this.editorcore;
54646 var fid = editorcore.frameId;
54648 function btn(id, toggle, handler){
54649 var xid = fid + '-'+ id ;
54653 cls : 'x-btn-icon x-edit-'+id,
54654 enableToggle:toggle !== false,
54655 scope: _t, // was editor...
54656 handler:handler||_t.relayBtnCmd,
54657 clickEvent:'mousedown',
54658 tooltip: etb.buttonTips[id] || undefined, ///tips ???
54665 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
54667 // stop form submits
54668 tb.el.on('click', function(e){
54669 e.preventDefault(); // what does this do?
54672 if(!this.disable.font) { // && !Roo.isSafari){
54673 /* why no safari for fonts
54674 editor.fontSelect = tb.el.createChild({
54677 cls:'x-font-select',
54678 html: this.createFontOptions()
54681 editor.fontSelect.on('change', function(){
54682 var font = editor.fontSelect.dom.value;
54683 editor.relayCmd('fontname', font);
54684 editor.deferFocus();
54688 editor.fontSelect.dom,
54694 if(!this.disable.formats){
54695 this.formatCombo = new Roo.form.ComboBox({
54696 store: new Roo.data.SimpleStore({
54699 data : this.formats // from states.js
54703 //autoCreate : {tag: "div", size: "20"},
54704 displayField:'tag',
54708 triggerAction: 'all',
54709 emptyText:'Add tag',
54710 selectOnFocus:true,
54713 'select': function(c, r, i) {
54714 editorcore.insertTag(r.get('tag'));
54720 tb.addField(this.formatCombo);
54724 if(!this.disable.format){
54729 btn('strikethrough')
54732 if(!this.disable.fontSize){
54737 btn('increasefontsize', false, editorcore.adjustFont),
54738 btn('decreasefontsize', false, editorcore.adjustFont)
54743 if(!this.disable.colors){
54746 id:editorcore.frameId +'-forecolor',
54747 cls:'x-btn-icon x-edit-forecolor',
54748 clickEvent:'mousedown',
54749 tooltip: this.buttonTips['forecolor'] || undefined,
54751 menu : new Roo.menu.ColorMenu({
54752 allowReselect: true,
54753 focus: Roo.emptyFn,
54756 selectHandler: function(cp, color){
54757 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
54758 editor.deferFocus();
54761 clickEvent:'mousedown'
54764 id:editorcore.frameId +'backcolor',
54765 cls:'x-btn-icon x-edit-backcolor',
54766 clickEvent:'mousedown',
54767 tooltip: this.buttonTips['backcolor'] || undefined,
54769 menu : new Roo.menu.ColorMenu({
54770 focus: Roo.emptyFn,
54773 allowReselect: true,
54774 selectHandler: function(cp, color){
54776 editorcore.execCmd('useCSS', false);
54777 editorcore.execCmd('hilitecolor', color);
54778 editorcore.execCmd('useCSS', true);
54779 editor.deferFocus();
54781 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
54782 Roo.isSafari || Roo.isIE ? '#'+color : color);
54783 editor.deferFocus();
54787 clickEvent:'mousedown'
54792 // now add all the items...
54795 if(!this.disable.alignments){
54798 btn('justifyleft'),
54799 btn('justifycenter'),
54800 btn('justifyright')
54804 //if(!Roo.isSafari){
54805 if(!this.disable.links){
54808 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
54812 if(!this.disable.lists){
54815 btn('insertorderedlist'),
54816 btn('insertunorderedlist')
54819 if(!this.disable.sourceEdit){
54822 btn('sourceedit', true, function(btn){
54823 this.toggleSourceEdit(btn.pressed);
54830 // special menu.. - needs to be tidied up..
54831 if (!this.disable.special) {
54834 cls: 'x-edit-none',
54840 for (var i =0; i < this.specialChars.length; i++) {
54841 smenu.menu.items.push({
54843 html: this.specialChars[i],
54844 handler: function(a,b) {
54845 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
54846 //editor.insertAtCursor(a.html);
54860 if (!this.disable.cleanStyles) {
54862 cls: 'x-btn-icon x-btn-clear',
54868 for (var i =0; i < this.cleanStyles.length; i++) {
54869 cmenu.menu.items.push({
54870 actiontype : this.cleanStyles[i],
54871 html: 'Remove ' + this.cleanStyles[i],
54872 handler: function(a,b) {
54875 var c = Roo.get(editorcore.doc.body);
54876 c.select('[style]').each(function(s) {
54877 s.dom.style.removeProperty(a.actiontype);
54879 editorcore.syncValue();
54884 cmenu.menu.items.push({
54885 actiontype : 'tablewidths',
54886 html: 'Remove Table Widths',
54887 handler: function(a,b) {
54888 editorcore.cleanTableWidths();
54889 editorcore.syncValue();
54893 cmenu.menu.items.push({
54894 actiontype : 'word',
54895 html: 'Remove MS Word Formating',
54896 handler: function(a,b) {
54897 editorcore.cleanWord();
54898 editorcore.syncValue();
54903 cmenu.menu.items.push({
54904 actiontype : 'all',
54905 html: 'Remove All Styles',
54906 handler: function(a,b) {
54908 var c = Roo.get(editorcore.doc.body);
54909 c.select('[style]').each(function(s) {
54910 s.dom.removeAttribute('style');
54912 editorcore.syncValue();
54917 cmenu.menu.items.push({
54918 actiontype : 'all',
54919 html: 'Remove All CSS Classes',
54920 handler: function(a,b) {
54922 var c = Roo.get(editorcore.doc.body);
54923 c.select('[class]').each(function(s) {
54924 s.dom.removeAttribute('class');
54926 editorcore.cleanWord();
54927 editorcore.syncValue();
54932 cmenu.menu.items.push({
54933 actiontype : 'tidy',
54934 html: 'Tidy HTML Source',
54935 handler: function(a,b) {
54936 new Roo.htmleditor.Tidy(editorcore.doc.body);
54937 editorcore.syncValue();
54946 if (!this.disable.specialElements) {
54949 cls: 'x-edit-none',
54954 for (var i =0; i < this.specialElements.length; i++) {
54955 semenu.menu.items.push(
54957 handler: function(a,b) {
54958 editor.insertAtCursor(this.ihtml);
54960 }, this.specialElements[i])
54972 for(var i =0; i< this.btns.length;i++) {
54973 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
54974 b.cls = 'x-edit-none';
54976 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
54977 b.cls += ' x-init-enable';
54980 b.scope = editorcore;
54988 // disable everything...
54990 this.tb.items.each(function(item){
54993 item.id != editorcore.frameId+ '-sourceedit' &&
54994 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
55000 this.rendered = true;
55002 // the all the btns;
55003 editor.on('editorevent', this.updateToolbar, this);
55004 // other toolbars need to implement this..
55005 //editor.on('editmodechange', this.updateToolbar, this);
55009 relayBtnCmd : function(btn) {
55010 this.editorcore.relayCmd(btn.cmd);
55012 // private used internally
55013 createLink : function(){
55014 //Roo.log("create link?");
55015 var ec = this.editorcore;
55016 var ar = ec.getAllAncestors();
55018 for(var i = 0;i< ar.length;i++) {
55019 if (ar[i] && ar[i].nodeName == 'A') {
55027 Roo.MessageBox.show({
55028 title : "Add / Edit Link URL",
55029 msg : "Enter the url for the link",
55030 buttons: Roo.MessageBox.OKCANCEL,
55031 fn: function(btn, url){
55035 if(url && url != 'http:/'+'/'){
55037 n.setAttribute('href', url);
55039 ec.relayCmd('createlink', url);
55045 //multiline: multiline,
55047 value : n ? n.getAttribute('href') : ''
55051 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
55057 * Protected method that will not generally be called directly. It triggers
55058 * a toolbar update by reading the markup state of the current selection in the editor.
55060 updateToolbar: function(){
55062 if(!this.editorcore.activated){
55063 this.editor.onFirstFocus();
55067 var btns = this.tb.items.map,
55068 doc = this.editorcore.doc,
55069 frameId = this.editorcore.frameId;
55071 if(!this.disable.font && !Roo.isSafari){
55073 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
55074 if(name != this.fontSelect.dom.value){
55075 this.fontSelect.dom.value = name;
55079 if(!this.disable.format){
55080 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
55081 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
55082 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
55083 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
55085 if(!this.disable.alignments){
55086 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
55087 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
55088 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
55090 if(!Roo.isSafari && !this.disable.lists){
55091 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
55092 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
55095 var ans = this.editorcore.getAllAncestors();
55096 if (this.formatCombo) {
55099 var store = this.formatCombo.store;
55100 this.formatCombo.setValue("");
55101 for (var i =0; i < ans.length;i++) {
55102 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
55104 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
55112 // hides menus... - so this cant be on a menu...
55113 Roo.menu.MenuMgr.hideAll();
55115 //this.editorsyncValue();
55119 createFontOptions : function(){
55120 var buf = [], fs = this.fontFamilies, ff, lc;
55124 for(var i = 0, len = fs.length; i< len; i++){
55126 lc = ff.toLowerCase();
55128 '<option value="',lc,'" style="font-family:',ff,';"',
55129 (this.defaultFont == lc ? ' selected="true">' : '>'),
55134 return buf.join('');
55137 toggleSourceEdit : function(sourceEditMode){
55139 Roo.log("toolbar toogle");
55140 if(sourceEditMode === undefined){
55141 sourceEditMode = !this.sourceEditMode;
55143 this.sourceEditMode = sourceEditMode === true;
55144 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
55145 // just toggle the button?
55146 if(btn.pressed !== this.sourceEditMode){
55147 btn.toggle(this.sourceEditMode);
55151 if(sourceEditMode){
55152 Roo.log("disabling buttons");
55153 this.tb.items.each(function(item){
55154 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
55160 Roo.log("enabling buttons");
55161 if(this.editorcore.initialized){
55162 this.tb.items.each(function(item){
55165 // initialize 'blocks'
55166 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
55167 Roo.htmleditor.Block.factory(e).updateElement(e);
55173 Roo.log("calling toggole on editor");
55174 // tell the editor that it's been pressed..
55175 this.editor.toggleSourceEdit(sourceEditMode);
55179 * Object collection of toolbar tooltips for the buttons in the editor. The key
55180 * is the command id associated with that button and the value is a valid QuickTips object.
55185 title: 'Bold (Ctrl+B)',
55186 text: 'Make the selected text bold.',
55187 cls: 'x-html-editor-tip'
55190 title: 'Italic (Ctrl+I)',
55191 text: 'Make the selected text italic.',
55192 cls: 'x-html-editor-tip'
55200 title: 'Bold (Ctrl+B)',
55201 text: 'Make the selected text bold.',
55202 cls: 'x-html-editor-tip'
55205 title: 'Italic (Ctrl+I)',
55206 text: 'Make the selected text italic.',
55207 cls: 'x-html-editor-tip'
55210 title: 'Underline (Ctrl+U)',
55211 text: 'Underline the selected text.',
55212 cls: 'x-html-editor-tip'
55215 title: 'Strikethrough',
55216 text: 'Strikethrough the selected text.',
55217 cls: 'x-html-editor-tip'
55219 increasefontsize : {
55220 title: 'Grow Text',
55221 text: 'Increase the font size.',
55222 cls: 'x-html-editor-tip'
55224 decreasefontsize : {
55225 title: 'Shrink Text',
55226 text: 'Decrease the font size.',
55227 cls: 'x-html-editor-tip'
55230 title: 'Text Highlight Color',
55231 text: 'Change the background color of the selected text.',
55232 cls: 'x-html-editor-tip'
55235 title: 'Font Color',
55236 text: 'Change the color of the selected text.',
55237 cls: 'x-html-editor-tip'
55240 title: 'Align Text Left',
55241 text: 'Align text to the left.',
55242 cls: 'x-html-editor-tip'
55245 title: 'Center Text',
55246 text: 'Center text in the editor.',
55247 cls: 'x-html-editor-tip'
55250 title: 'Align Text Right',
55251 text: 'Align text to the right.',
55252 cls: 'x-html-editor-tip'
55254 insertunorderedlist : {
55255 title: 'Bullet List',
55256 text: 'Start a bulleted list.',
55257 cls: 'x-html-editor-tip'
55259 insertorderedlist : {
55260 title: 'Numbered List',
55261 text: 'Start a numbered list.',
55262 cls: 'x-html-editor-tip'
55265 title: 'Hyperlink',
55266 text: 'Make the selected text a hyperlink.',
55267 cls: 'x-html-editor-tip'
55270 title: 'Source Edit',
55271 text: 'Switch to source editing mode.',
55272 cls: 'x-html-editor-tip'
55276 onDestroy : function(){
55279 this.tb.items.each(function(item){
55281 item.menu.removeAll();
55283 item.menu.el.destroy();
55291 onFirstFocus: function() {
55292 this.tb.items.each(function(item){
55301 // <script type="text/javascript">
55304 * Ext JS Library 1.1.1
55305 * Copyright(c) 2006-2007, Ext JS, LLC.
55312 * @class Roo.form.HtmlEditor.ToolbarContext
55317 new Roo.form.HtmlEditor({
55320 { xtype: 'ToolbarStandard', styles : {} }
55321 { xtype: 'ToolbarContext', disable : {} }
55327 * @config : {Object} disable List of elements to disable.. (not done yet.)
55328 * @config : {Object} styles Map of styles available.
55332 Roo.form.HtmlEditor.ToolbarContext = function(config)
55335 Roo.apply(this, config);
55336 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
55337 // dont call parent... till later.
55338 this.styles = this.styles || {};
55343 Roo.form.HtmlEditor.ToolbarContext.types = {
55358 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
55384 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
55455 name : 'selectoptions',
55461 // should we really allow this??
55462 // should this just be
55479 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
55480 Roo.form.HtmlEditor.ToolbarContext.stores = false;
55482 Roo.form.HtmlEditor.ToolbarContext.options = {
55484 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
55485 [ 'Courier New', 'Courier New'],
55486 [ 'Tahoma', 'Tahoma'],
55487 [ 'Times New Roman,serif', 'Times'],
55488 [ 'Verdana','Verdana' ]
55492 // fixme - these need to be configurable..
55495 //Roo.form.HtmlEditor.ToolbarContext.types
55498 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
55505 editorcore : false,
55507 * @cfg {Object} disable List of toolbar elements to disable
55512 * @cfg {Object} styles List of styles
55513 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
55515 * These must be defined in the page, so they get rendered correctly..
55526 init : function(editor)
55528 this.editor = editor;
55529 this.editorcore = editor.editorcore ? editor.editorcore : editor;
55530 var editorcore = this.editorcore;
55532 var fid = editorcore.frameId;
55534 function btn(id, toggle, handler){
55535 var xid = fid + '-'+ id ;
55539 cls : 'x-btn-icon x-edit-'+id,
55540 enableToggle:toggle !== false,
55541 scope: editorcore, // was editor...
55542 handler:handler||editorcore.relayBtnCmd,
55543 clickEvent:'mousedown',
55544 tooltip: etb.buttonTips[id] || undefined, ///tips ???
55548 // create a new element.
55549 var wdiv = editor.wrap.createChild({
55551 }, editor.wrap.dom.firstChild.nextSibling, true);
55553 // can we do this more than once??
55555 // stop form submits
55558 // disable everything...
55559 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
55560 this.toolbars = {};
55561 // block toolbars are built in updateToolbar when needed.
55562 for (var i in ty) {
55564 this.toolbars[i] = this.buildToolbar(ty[i],i);
55566 this.tb = this.toolbars.BODY;
55568 this.buildFooter();
55569 this.footer.show();
55570 editor.on('hide', function( ) { this.footer.hide() }, this);
55571 editor.on('show', function( ) { this.footer.show() }, this);
55574 this.rendered = true;
55576 // the all the btns;
55577 editor.on('editorevent', this.updateToolbar, this);
55578 // other toolbars need to implement this..
55579 //editor.on('editmodechange', this.updateToolbar, this);
55585 * Protected method that will not generally be called directly. It triggers
55586 * a toolbar update by reading the markup state of the current selection in the editor.
55588 * Note you can force an update by calling on('editorevent', scope, false)
55590 updateToolbar: function(editor ,ev, sel)
55594 ev.stopEvent(); // se if we can stop this looping with mutiple events.
55598 // capture mouse up - this is handy for selecting images..
55599 // perhaps should go somewhere else...
55600 if(!this.editorcore.activated){
55601 this.editor.onFirstFocus();
55604 //Roo.log(ev ? ev.target : 'NOTARGET');
55607 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
55608 // selectNode - might want to handle IE?
55613 (ev.type == 'mouseup' || ev.type == 'click' ) &&
55614 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
55615 // they have click on an image...
55616 // let's see if we can change the selection...
55619 // this triggers looping?
55620 //this.editorcore.selectNode(sel);
55624 // this forces an id..
55625 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
55626 e.classList.remove('roo-ed-selection');
55628 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
55629 //Roo.get(node).addClass('roo-ed-selection');
55631 //var updateFooter = sel ? false : true;
55634 var ans = this.editorcore.getAllAncestors();
55637 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
55640 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
55641 sel = sel ? sel : this.editorcore.doc.body;
55642 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
55646 var tn = sel.tagName.toUpperCase();
55647 var lastSel = this.tb.selectedNode;
55648 this.tb.selectedNode = sel;
55649 var left_label = tn;
55651 // ok see if we are editing a block?
55654 // you are not actually selecting the block.
55655 if (sel && sel.hasAttribute('data-block')) {
55657 } else if (sel && sel.closest('[data-block]')) {
55659 db = sel.closest('[data-block]');
55660 //var cepar = sel.closest('[contenteditable=true]');
55661 //if (db && cepar && cepar.tagName != 'BODY') {
55662 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
55668 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
55669 if (db && this.editorcore.enableBlocks) {
55670 block = Roo.htmleditor.Block.factory(db);
55675 db.classList.length > 0 ? db.className + ' ' : ''
55676 ) + 'roo-ed-selection';
55678 // since we removed it earlier... its not there..
55679 tn = 'BLOCK.' + db.getAttribute('data-block');
55681 //this.editorcore.selectNode(db);
55682 if (typeof(this.toolbars[tn]) == 'undefined') {
55683 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
55685 this.toolbars[tn].selectedNode = db;
55686 left_label = block.friendly_name;
55687 ans = this.editorcore.getAllAncestors();
55695 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
55696 return; // no change?
55702 ///console.log("show: " + tn);
55703 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
55707 this.tb.items.first().el.innerHTML = left_label + ': ';
55710 // update attributes
55711 if (block && this.tb.fields) {
55713 this.tb.fields.each(function(e) {
55714 e.setValue(block[e.name]);
55718 } else if (this.tb.fields && this.tb.selectedNode) {
55719 this.tb.fields.each( function(e) {
55721 e.setValue(this.tb.selectedNode.style[e.stylename]);
55724 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
55726 this.updateToolbarStyles(this.tb.selectedNode);
55731 Roo.menu.MenuMgr.hideAll();
55736 // update the footer
55738 this.updateFooter(ans);
55742 updateToolbarStyles : function(sel)
55744 var hasStyles = false;
55745 for(var i in this.styles) {
55751 if (hasStyles && this.tb.hasStyles) {
55752 var st = this.tb.fields.item(0);
55754 st.store.removeAll();
55755 var cn = sel.className.split(/\s+/);
55758 if (this.styles['*']) {
55760 Roo.each(this.styles['*'], function(v) {
55761 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
55764 if (this.styles[tn]) {
55765 Roo.each(this.styles[tn], function(v) {
55766 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
55770 st.store.loadData(avs);
55777 updateFooter : function(ans)
55780 if (ans === false) {
55781 this.footDisp.dom.innerHTML = '';
55785 this.footerEls = ans.reverse();
55786 Roo.each(this.footerEls, function(a,i) {
55787 if (!a) { return; }
55788 html += html.length ? ' > ' : '';
55790 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
55795 var sz = this.footDisp.up('td').getSize();
55796 this.footDisp.dom.style.width = (sz.width -10) + 'px';
55797 this.footDisp.dom.style.marginLeft = '5px';
55799 this.footDisp.dom.style.overflow = 'hidden';
55801 this.footDisp.dom.innerHTML = html;
55808 onDestroy : function(){
55811 this.tb.items.each(function(item){
55813 item.menu.removeAll();
55815 item.menu.el.destroy();
55823 onFirstFocus: function() {
55824 // need to do this for all the toolbars..
55825 this.tb.items.each(function(item){
55829 buildToolbar: function(tlist, nm, friendly_name, block)
55831 var editor = this.editor;
55832 var editorcore = this.editorcore;
55833 // create a new element.
55834 var wdiv = editor.wrap.createChild({
55836 }, editor.wrap.dom.firstChild.nextSibling, true);
55839 var tb = new Roo.Toolbar(wdiv);
55840 ///this.tb = tb; // << this sets the active toolbar..
55841 if (tlist === false && block) {
55842 tlist = block.contextMenu(this);
55845 tb.hasStyles = false;
55848 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
55850 var styles = Array.from(this.styles);
55854 if (styles && styles.length) {
55855 tb.hasStyles = true;
55856 // this needs a multi-select checkbox...
55857 tb.addField( new Roo.form.ComboBox({
55858 store: new Roo.data.SimpleStore({
55860 fields: ['val', 'selected'],
55863 name : '-roo-edit-className',
55864 attrname : 'className',
55865 displayField: 'val',
55869 triggerAction: 'all',
55870 emptyText:'Select Style',
55871 selectOnFocus:true,
55874 'select': function(c, r, i) {
55875 // initial support only for on class per el..
55876 tb.selectedNode.className = r ? r.get('val') : '';
55877 editorcore.syncValue();
55884 var tbc = Roo.form.HtmlEditor.ToolbarContext;
55887 for (var i = 0; i < tlist.length; i++) {
55889 // newer versions will use xtype cfg to create menus.
55890 if (typeof(tlist[i].xtype) != 'undefined') {
55892 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
55898 var item = tlist[i];
55899 tb.add(item.title + ": ");
55902 //optname == used so you can configure the options available..
55903 var opts = item.opts ? item.opts : false;
55904 if (item.optname) { // use the b
55905 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
55910 // opts == pulldown..
55911 tb.addField( new Roo.form.ComboBox({
55912 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
55914 fields: ['val', 'display'],
55917 name : '-roo-edit-' + tlist[i].name,
55919 attrname : tlist[i].name,
55920 stylename : item.style ? item.style : false,
55922 displayField: item.displayField ? item.displayField : 'val',
55923 valueField : 'val',
55925 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
55927 triggerAction: 'all',
55928 emptyText:'Select',
55929 selectOnFocus:true,
55930 width: item.width ? item.width : 130,
55932 'select': function(c, r, i) {
55936 tb.selectedNode.style[c.stylename] = r.get('val');
55937 editorcore.syncValue();
55941 tb.selectedNode.removeAttribute(c.attrname);
55942 editorcore.syncValue();
55945 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
55946 editorcore.syncValue();
55955 tb.addField( new Roo.form.TextField({
55958 //allowBlank:false,
55964 tb.addField( new Roo.form.TextField({
55965 name: '-roo-edit-' + tlist[i].name,
55966 attrname : tlist[i].name,
55972 'change' : function(f, nv, ov) {
55975 tb.selectedNode.setAttribute(f.attrname, nv);
55976 editorcore.syncValue();
55984 var show_delete = !block || block.deleteTitle !== false;
55986 show_delete = false;
55990 text: 'Stylesheets',
55993 click : function ()
55995 _this.editor.fireEvent('stylesheetsclick', _this.editor);
56004 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
56007 click : function ()
56009 var sn = tb.selectedNode;
56011 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
56017 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
56018 if (sn.hasAttribute('data-block')) {
56019 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
56020 sn.parentNode.removeChild(sn);
56022 } else if (sn && sn.tagName != 'BODY') {
56023 // remove and keep parents.
56024 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
56029 var range = editorcore.createRange();
56031 range.setStart(stn,0);
56032 range.setEnd(stn,0);
56033 var selection = editorcore.getSelection();
56034 selection.removeAllRanges();
56035 selection.addRange(range);
56038 //_this.updateToolbar(null, null, pn);
56039 _this.updateToolbar(null, null, null);
56040 _this.updateFooter(false);
56051 tb.el.on('click', function(e){
56052 e.preventDefault(); // what does this do?
56054 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
56057 // dont need to disable them... as they will get hidden
56062 buildFooter : function()
56065 var fel = this.editor.wrap.createChild();
56066 this.footer = new Roo.Toolbar(fel);
56067 // toolbar has scrolly on left / right?
56068 var footDisp= new Roo.Toolbar.Fill();
56074 handler : function() {
56075 _t.footDisp.scrollTo('left',0,true)
56079 this.footer.add( footDisp );
56084 handler : function() {
56086 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
56090 var fel = Roo.get(footDisp.el);
56091 fel.addClass('x-editor-context');
56092 this.footDispWrap = fel;
56093 this.footDispWrap.overflow = 'hidden';
56095 this.footDisp = fel.createChild();
56096 this.footDispWrap.on('click', this.onContextClick, this)
56100 // when the footer contect changes
56101 onContextClick : function (ev,dom)
56103 ev.preventDefault();
56104 var cn = dom.className;
56106 if (!cn.match(/x-ed-loc-/)) {
56109 var n = cn.split('-').pop();
56110 var ans = this.footerEls;
56113 this.editorcore.selectNode(sel);
56116 this.updateToolbar(null, null, sel);
56133 * Ext JS Library 1.1.1
56134 * Copyright(c) 2006-2007, Ext JS, LLC.
56136 * Originally Released Under LGPL - original licence link has changed is not relivant.
56139 * <script type="text/javascript">
56143 * @class Roo.form.BasicForm
56144 * @extends Roo.util.Observable
56145 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
56147 * @param {String/HTMLElement/Roo.Element} el The form element or its id
56148 * @param {Object} config Configuration options
56150 Roo.form.BasicForm = function(el, config){
56151 this.allItems = [];
56152 this.childForms = [];
56153 Roo.apply(this, config);
56155 * The Roo.form.Field items in this form.
56156 * @type MixedCollection
56160 this.items = new Roo.util.MixedCollection(false, function(o){
56161 return o.id || (o.id = Roo.id());
56165 * @event beforeaction
56166 * Fires before any action is performed. Return false to cancel the action.
56167 * @param {Form} this
56168 * @param {Action} action The action to be performed
56170 beforeaction: true,
56172 * @event actionfailed
56173 * Fires when an action fails.
56174 * @param {Form} this
56175 * @param {Action} action The action that failed
56177 actionfailed : true,
56179 * @event actioncomplete
56180 * Fires when an action is completed.
56181 * @param {Form} this
56182 * @param {Action} action The action that completed
56184 actioncomplete : true
56189 Roo.form.BasicForm.superclass.constructor.call(this);
56191 Roo.form.BasicForm.popover.apply();
56194 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
56196 * @cfg {String} method
56197 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
56200 * @cfg {DataReader} reader
56201 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
56202 * This is optional as there is built-in support for processing JSON.
56205 * @cfg {DataReader} errorReader
56206 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
56207 * This is completely optional as there is built-in support for processing JSON.
56210 * @cfg {String} url
56211 * The URL to use for form actions if one isn't supplied in the action options.
56214 * @cfg {Boolean} fileUpload
56215 * Set to true if this form is a file upload.
56219 * @cfg {Object} baseParams
56220 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
56225 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
56230 activeAction : null,
56233 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
56234 * or setValues() data instead of when the form was first created.
56236 trackResetOnLoad : false,
56240 * childForms - used for multi-tab forms
56243 childForms : false,
56246 * allItems - full list of fields.
56252 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
56253 * element by passing it or its id or mask the form itself by passing in true.
56256 waitMsgTarget : false,
56261 disableMask : false,
56264 * @cfg {Boolean} errorMask Should the form be masked (and the active element highlighted on error - default false
56269 * @cfg {Number} maskOffset space around form element to mask if there is an error Default 100
56274 initEl : function(el){
56275 this.el = Roo.get(el);
56276 this.id = this.el.id || Roo.id();
56277 this.el.on('submit', this.onSubmit, this);
56278 this.el.addClass('x-form');
56282 onSubmit : function(e){
56287 * Returns true if client-side validation on the form is successful.
56290 isValid : function(){
56292 var target = false;
56293 this.items.each(function(f){
56300 if(!target && f.el.isVisible(true)){
56305 if(this.errorMask && !valid){
56306 Roo.form.BasicForm.popover.mask(this, target);
56312 * Returns array of invalid form fields.
56316 invalidFields : function()
56319 this.items.each(function(f){
56332 * DEPRICATED Returns true if any fields in this form have changed since their original load.
56335 isDirty : function(){
56337 this.items.each(function(f){
56347 * Returns true if any fields in this form have changed since their original load. (New version)
56351 hasChanged : function()
56354 this.items.each(function(f){
56355 if(f.hasChanged()){
56364 * Resets all hasChanged to 'false' -
56365 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
56366 * So hasChanged storage is only to be used for this purpose
56369 resetHasChanged : function()
56371 this.items.each(function(f){
56372 f.resetHasChanged();
56379 * Performs a predefined action (submit or load) or custom actions you define on this form.
56380 * @param {String} actionName The name of the action type
56381 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
56382 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
56383 * accept other config options):
56385 Property Type Description
56386 ---------------- --------------- ----------------------------------------------------------------------------------
56387 url String The url for the action (defaults to the form's url)
56388 method String The form method to use (defaults to the form's method, or POST if not defined)
56389 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
56390 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
56391 validate the form on the client (defaults to false)
56393 * @return {BasicForm} this
56395 doAction : function(action, options){
56396 if(typeof action == 'string'){
56397 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
56399 if(this.fireEvent('beforeaction', this, action) !== false){
56400 this.beforeAction(action);
56401 action.run.defer(100, action);
56407 * Shortcut to do a submit action.
56408 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
56409 * @return {BasicForm} this
56411 submit : function(options){
56412 this.doAction('submit', options);
56417 * Shortcut to do a load action.
56418 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
56419 * @return {BasicForm} this
56421 load : function(options){
56422 this.doAction('load', options);
56427 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
56428 * @param {Record} record The record to edit
56429 * @return {BasicForm} this
56431 updateRecord : function(record){
56432 record.beginEdit();
56433 var fs = record.fields;
56434 fs.each(function(f){
56435 var field = this.findField(f.name);
56437 record.set(f.name, field.getValue());
56445 * Loads an Roo.data.Record into this form.
56446 * @param {Record} record The record to load
56447 * @return {BasicForm} this
56449 loadRecord : function(record){
56450 this.setValues(record.data);
56455 beforeAction : function(action){
56456 var o = action.options;
56458 if(!this.disableMask) {
56459 if(this.waitMsgTarget === true){
56460 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
56461 }else if(this.waitMsgTarget){
56462 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
56463 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
56465 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
56473 afterAction : function(action, success){
56474 this.activeAction = null;
56475 var o = action.options;
56477 if(!this.disableMask) {
56478 if(this.waitMsgTarget === true){
56480 }else if(this.waitMsgTarget){
56481 this.waitMsgTarget.unmask();
56483 Roo.MessageBox.updateProgress(1);
56484 Roo.MessageBox.hide();
56492 Roo.callback(o.success, o.scope, [this, action]);
56493 this.fireEvent('actioncomplete', this, action);
56497 // failure condition..
56498 // we have a scenario where updates need confirming.
56499 // eg. if a locking scenario exists..
56500 // we look for { errors : { needs_confirm : true }} in the response.
56502 (typeof(action.result) != 'undefined') &&
56503 (typeof(action.result.errors) != 'undefined') &&
56504 (typeof(action.result.errors.needs_confirm) != 'undefined')
56507 Roo.MessageBox.confirm(
56508 "Change requires confirmation",
56509 action.result.errorMsg,
56514 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
56524 Roo.callback(o.failure, o.scope, [this, action]);
56525 // show an error message if no failed handler is set..
56526 if (!this.hasListener('actionfailed')) {
56527 Roo.MessageBox.alert("Error",
56528 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
56529 action.result.errorMsg :
56530 "Saving Failed, please check your entries or try again"
56534 this.fireEvent('actionfailed', this, action);
56540 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
56541 * @param {String} id The value to search for
56544 findField : function(id){
56545 var field = this.items.get(id);
56547 this.items.each(function(f){
56548 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
56554 return field || null;
56558 * Add a secondary form to this one,
56559 * Used to provide tabbed forms. One form is primary, with hidden values
56560 * which mirror the elements from the other forms.
56562 * @param {Roo.form.Form} form to add.
56565 addForm : function(form)
56568 if (this.childForms.indexOf(form) > -1) {
56572 this.childForms.push(form);
56574 Roo.each(form.allItems, function (fe) {
56576 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
56577 if (this.findField(n)) { // already added..
56580 var add = new Roo.form.Hidden({
56583 add.render(this.el);
56590 * Mark fields in this form invalid in bulk.
56591 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
56592 * @return {BasicForm} this
56594 markInvalid : function(errors){
56595 if(errors instanceof Array){
56596 for(var i = 0, len = errors.length; i < len; i++){
56597 var fieldError = errors[i];
56598 var f = this.findField(fieldError.id);
56600 f.markInvalid(fieldError.msg);
56606 if(typeof errors[id] != 'function' && (field = this.findField(id))){
56607 field.markInvalid(errors[id]);
56611 Roo.each(this.childForms || [], function (f) {
56612 f.markInvalid(errors);
56619 * Set values for fields in this form in bulk.
56620 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
56621 * @return {BasicForm} this
56623 setValues : function(values){
56624 if(values instanceof Array){ // array of objects
56625 for(var i = 0, len = values.length; i < len; i++){
56627 var f = this.findField(v.id);
56629 f.setValue(v.value);
56630 if(this.trackResetOnLoad){
56631 f.originalValue = f.getValue();
56635 }else{ // object hash
56638 if(typeof values[id] != 'function' && (field = this.findField(id))){
56643 if (field.setFromData &&
56644 field.valueField &&
56645 field.displayField &&
56646 // combos' with local stores can
56647 // be queried via setValue()
56648 // to set their value..
56649 (field.store && !field.store.isLocal)
56653 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
56654 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
56655 field.setFromData(sd);
56657 } else if (field.inputType && field.inputType == 'radio') {
56659 field.setValue(values[id]);
56661 field.setValue(values[id]);
56665 if(this.trackResetOnLoad){
56666 field.originalValue = field.getValue();
56671 this.resetHasChanged();
56674 Roo.each(this.childForms || [], function (f) {
56675 f.setValues(values);
56676 f.resetHasChanged();
56683 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
56684 * they are returned as an array.
56685 * @param {Boolean} asString (def)
56688 getValues : function(asString)
56690 if (this.childForms) {
56691 // copy values from the child forms
56692 Roo.each(this.childForms, function (f) {
56693 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
56698 if (typeof(FormData) != 'undefined' && asString !== true) {
56699 // this relies on a 'recent' version of chrome apparently...
56701 var fd = (new FormData(this.el.dom)).entries();
56703 var ent = fd.next();
56704 while (!ent.done) {
56705 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
56716 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
56717 if(asString === true){
56720 return Roo.urlDecode(fs);
56724 * Returns the fields in this form as an object with key/value pairs.
56725 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
56726 * Normally this will not return readOnly data
56727 * @param {Boolean} with_readonly return readonly field data.
56730 getFieldValues : function(with_readonly)
56732 if (this.childForms) {
56733 // copy values from the child forms
56734 // should this call getFieldValues - probably not as we do not currently copy
56735 // hidden fields when we generate..
56736 Roo.each(this.childForms, function (f) {
56737 this.setValues(f.getFieldValues());
56742 this.items.each(function(f){
56744 if (f.readOnly && with_readonly !== true) {
56745 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
56746 // if a subform contains a copy of them.
56747 // if you have subforms with the same editable data, you will need to copy the data back
56751 if (!f.getName()) {
56754 var v = f.getValue();
56755 if (f.inputType =='radio') {
56756 if (typeof(ret[f.getName()]) == 'undefined') {
56757 ret[f.getName()] = ''; // empty..
56760 if (!f.el.dom.checked) {
56764 v = f.el.dom.value;
56768 // not sure if this supported any more..
56769 if ((typeof(v) == 'object') && f.getRawValue) {
56770 v = f.getRawValue() ; // dates..
56772 // combo boxes where name != hiddenName...
56773 if (f.name != f.getName()) {
56774 ret[f.name] = f.getRawValue();
56776 ret[f.getName()] = v;
56783 * Clears all invalid messages in this form.
56784 * @return {BasicForm} this
56786 clearInvalid : function(){
56787 this.items.each(function(f){
56791 Roo.each(this.childForms || [], function (f) {
56800 * Resets this form.
56801 * @return {BasicForm} this
56803 reset : function(){
56804 this.items.each(function(f){
56808 Roo.each(this.childForms || [], function (f) {
56811 this.resetHasChanged();
56817 * Add Roo.form components to this form.
56818 * @param {Field} field1
56819 * @param {Field} field2 (optional)
56820 * @param {Field} etc (optional)
56821 * @return {BasicForm} this
56824 this.items.addAll(Array.prototype.slice.call(arguments, 0));
56830 * Removes a field from the items collection (does NOT remove its markup).
56831 * @param {Field} field
56832 * @return {BasicForm} this
56834 remove : function(field){
56835 this.items.remove(field);
56840 * Looks at the fields in this form, checks them for an id attribute,
56841 * and calls applyTo on the existing dom element with that id.
56842 * @return {BasicForm} this
56844 render : function(){
56845 this.items.each(function(f){
56846 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
56854 * Calls {@link Ext#apply} for all fields in this form with the passed object.
56855 * @param {Object} values
56856 * @return {BasicForm} this
56858 applyToFields : function(o){
56859 this.items.each(function(f){
56866 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
56867 * @param {Object} values
56868 * @return {BasicForm} this
56870 applyIfToFields : function(o){
56871 this.items.each(function(f){
56879 Roo.BasicForm = Roo.form.BasicForm;
56881 Roo.apply(Roo.form.BasicForm, {
56895 intervalID : false,
56901 if(this.isApplied){
56906 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
56907 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
56908 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
56909 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
56912 this.maskEl.top.enableDisplayMode("block");
56913 this.maskEl.left.enableDisplayMode("block");
56914 this.maskEl.bottom.enableDisplayMode("block");
56915 this.maskEl.right.enableDisplayMode("block");
56917 Roo.get(document.body).on('click', function(){
56921 Roo.get(document.body).on('touchstart', function(){
56925 this.isApplied = true
56928 mask : function(form, target)
56932 this.target = target;
56934 if(!this.form.errorMask || !target.el){
56938 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
56940 var ot = this.target.el.calcOffsetsTo(scrollable);
56942 var scrollTo = ot[1] - this.form.maskOffset;
56944 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
56946 scrollable.scrollTo('top', scrollTo);
56948 var el = this.target.wrap || this.target.el;
56950 var box = el.getBox();
56952 this.maskEl.top.setStyle('position', 'absolute');
56953 this.maskEl.top.setStyle('z-index', 10000);
56954 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
56955 this.maskEl.top.setLeft(0);
56956 this.maskEl.top.setTop(0);
56957 this.maskEl.top.show();
56959 this.maskEl.left.setStyle('position', 'absolute');
56960 this.maskEl.left.setStyle('z-index', 10000);
56961 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
56962 this.maskEl.left.setLeft(0);
56963 this.maskEl.left.setTop(box.y - this.padding);
56964 this.maskEl.left.show();
56966 this.maskEl.bottom.setStyle('position', 'absolute');
56967 this.maskEl.bottom.setStyle('z-index', 10000);
56968 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
56969 this.maskEl.bottom.setLeft(0);
56970 this.maskEl.bottom.setTop(box.bottom + this.padding);
56971 this.maskEl.bottom.show();
56973 this.maskEl.right.setStyle('position', 'absolute');
56974 this.maskEl.right.setStyle('z-index', 10000);
56975 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
56976 this.maskEl.right.setLeft(box.right + this.padding);
56977 this.maskEl.right.setTop(box.y - this.padding);
56978 this.maskEl.right.show();
56980 this.intervalID = window.setInterval(function() {
56981 Roo.form.BasicForm.popover.unmask();
56984 window.onwheel = function(){ return false;};
56986 (function(){ this.isMasked = true; }).defer(500, this);
56990 unmask : function()
56992 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
56996 this.maskEl.top.setStyle('position', 'absolute');
56997 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
56998 this.maskEl.top.hide();
57000 this.maskEl.left.setStyle('position', 'absolute');
57001 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
57002 this.maskEl.left.hide();
57004 this.maskEl.bottom.setStyle('position', 'absolute');
57005 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
57006 this.maskEl.bottom.hide();
57008 this.maskEl.right.setStyle('position', 'absolute');
57009 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
57010 this.maskEl.right.hide();
57012 window.onwheel = function(){ return true;};
57014 if(this.intervalID){
57015 window.clearInterval(this.intervalID);
57016 this.intervalID = false;
57019 this.isMasked = false;
57027 * Ext JS Library 1.1.1
57028 * Copyright(c) 2006-2007, Ext JS, LLC.
57030 * Originally Released Under LGPL - original licence link has changed is not relivant.
57033 * <script type="text/javascript">
57037 * @class Roo.form.Form
57038 * @extends Roo.form.BasicForm
57039 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
57040 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
57042 * @param {Object} config Configuration options
57044 Roo.form.Form = function(config){
57046 if (config.items) {
57047 xitems = config.items;
57048 delete config.items;
57052 Roo.form.Form.superclass.constructor.call(this, null, config);
57053 this.url = this.url || this.action;
57055 this.root = new Roo.form.Layout(Roo.applyIf({
57059 this.active = this.root;
57061 * Array of all the buttons that have been added to this form via {@link addButton}
57065 this.allItems = [];
57068 * @event clientvalidation
57069 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
57070 * @param {Form} this
57071 * @param {Boolean} valid true if the form has passed client-side validation
57073 clientvalidation: true,
57076 * Fires when the form is rendered
57077 * @param {Roo.form.Form} form
57082 if (this.progressUrl) {
57083 // push a hidden field onto the list of fields..
57087 name : 'UPLOAD_IDENTIFIER'
57092 Roo.each(xitems, this.addxtype, this);
57096 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
57098 * @cfg {Roo.Button} buttons[] buttons at bottom of form
57102 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
57105 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
57108 * @cfg {String} buttonAlign (left|center|right) Valid values are "left," "center" and "right" (defaults to "center")
57110 buttonAlign:'center',
57113 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
57118 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
57119 * This property cascades to child containers if not set.
57124 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
57125 * fires a looping event with that state. This is required to bind buttons to the valid
57126 * state using the config value formBind:true on the button.
57128 monitorValid : false,
57131 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
57136 * @cfg {String} progressUrl - Url to return progress data
57139 progressUrl : false,
57141 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
57142 * sending a formdata with extra parameters - eg uploaded elements.
57148 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
57149 * fields are added and the column is closed. If no fields are passed the column remains open
57150 * until end() is called.
57151 * @param {Object} config The config to pass to the column
57152 * @param {Field} field1 (optional)
57153 * @param {Field} field2 (optional)
57154 * @param {Field} etc (optional)
57155 * @return Column The column container object
57157 column : function(c){
57158 var col = new Roo.form.Column(c);
57160 if(arguments.length > 1){ // duplicate code required because of Opera
57161 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57168 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
57169 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
57170 * until end() is called.
57171 * @param {Object} config The config to pass to the fieldset
57172 * @param {Field} field1 (optional)
57173 * @param {Field} field2 (optional)
57174 * @param {Field} etc (optional)
57175 * @return FieldSet The fieldset container object
57177 fieldset : function(c){
57178 var fs = new Roo.form.FieldSet(c);
57180 if(arguments.length > 1){ // duplicate code required because of Opera
57181 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57188 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
57189 * fields are added and the container is closed. If no fields are passed the container remains open
57190 * until end() is called.
57191 * @param {Object} config The config to pass to the Layout
57192 * @param {Field} field1 (optional)
57193 * @param {Field} field2 (optional)
57194 * @param {Field} etc (optional)
57195 * @return Layout The container object
57197 container : function(c){
57198 var l = new Roo.form.Layout(c);
57200 if(arguments.length > 1){ // duplicate code required because of Opera
57201 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
57208 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
57209 * @param {Object} container A Roo.form.Layout or subclass of Layout
57210 * @return {Form} this
57212 start : function(c){
57213 // cascade label info
57214 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
57215 this.active.stack.push(c);
57216 c.ownerCt = this.active;
57222 * Closes the current open container
57223 * @return {Form} this
57226 if(this.active == this.root){
57229 this.active = this.active.ownerCt;
57234 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
57235 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
57236 * as the label of the field.
57237 * @param {Field} field1
57238 * @param {Field} field2 (optional)
57239 * @param {Field} etc. (optional)
57240 * @return {Form} this
57243 this.active.stack.push.apply(this.active.stack, arguments);
57244 this.allItems.push.apply(this.allItems,arguments);
57246 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
57247 if(a[i].isFormField){
57252 Roo.form.Form.superclass.add.apply(this, r);
57262 * Find any element that has been added to a form, using it's ID or name
57263 * This can include framesets, columns etc. along with regular fields..
57264 * @param {String} id - id or name to find.
57266 * @return {Element} e - or false if nothing found.
57268 findbyId : function(id)
57274 Roo.each(this.allItems, function(f){
57275 if (f.id == id || f.name == id ){
57286 * Render this form into the passed container. This should only be called once!
57287 * @param {String/HTMLElement/Element} container The element this component should be rendered into
57288 * @return {Form} this
57290 render : function(ct)
57296 var o = this.autoCreate || {
57298 method : this.method || 'POST',
57299 id : this.id || Roo.id()
57301 this.initEl(ct.createChild(o));
57303 this.root.render(this.el);
57307 this.items.each(function(f){
57308 f.render('x-form-el-'+f.id);
57311 if(this.buttons.length > 0){
57312 // tables are required to maintain order and for correct IE layout
57313 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
57314 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
57315 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
57317 var tr = tb.getElementsByTagName('tr')[0];
57318 for(var i = 0, len = this.buttons.length; i < len; i++) {
57319 var b = this.buttons[i];
57320 var td = document.createElement('td');
57321 td.className = 'x-form-btn-td';
57322 b.render(tr.appendChild(td));
57325 if(this.monitorValid){ // initialize after render
57326 this.startMonitoring();
57328 this.fireEvent('rendered', this);
57333 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
57334 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
57335 * object or a valid Roo.DomHelper element config
57336 * @param {Function} handler The function called when the button is clicked
57337 * @param {Object} scope (optional) The scope of the handler function
57338 * @return {Roo.Button}
57340 addButton : function(config, handler, scope){
57344 minWidth: this.minButtonWidth,
57347 if(typeof config == "string"){
57350 Roo.apply(bc, config);
57352 var btn = new Roo.Button(null, bc);
57353 this.buttons.push(btn);
57358 * Adds a series of form elements (using the xtype property as the factory method.
57359 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
57360 * @param {Object} config
57363 addxtype : function()
57365 var ar = Array.prototype.slice.call(arguments, 0);
57367 for(var i = 0; i < ar.length; i++) {
57369 continue; // skip -- if this happends something invalid got sent, we
57370 // should ignore it, as basically that interface element will not show up
57371 // and that should be pretty obvious!!
57374 if (Roo.form[ar[i].xtype]) {
57376 var fe = Roo.factory(ar[i], Roo.form);
57382 fe.store.form = this;
57387 this.allItems.push(fe);
57388 if (fe.items && fe.addxtype) {
57389 fe.addxtype.apply(fe, fe.items);
57399 // console.log('adding ' + ar[i].xtype);
57401 if (ar[i].xtype == 'Button') {
57402 //console.log('adding button');
57403 //console.log(ar[i]);
57404 this.addButton(ar[i]);
57405 this.allItems.push(fe);
57409 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
57410 alert('end is not supported on xtype any more, use items');
57412 // //console.log('adding end');
57420 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
57421 * option "monitorValid"
57423 startMonitoring : function(){
57426 Roo.TaskMgr.start({
57427 run : this.bindHandler,
57428 interval : this.monitorPoll || 200,
57435 * Stops monitoring of the valid state of this form
57437 stopMonitoring : function(){
57438 this.bound = false;
57442 bindHandler : function(){
57444 return false; // stops binding
57447 this.items.each(function(f){
57448 if(!f.isValid(true)){
57453 for(var i = 0, len = this.buttons.length; i < len; i++){
57454 var btn = this.buttons[i];
57455 if(btn.formBind === true && btn.disabled === valid){
57456 btn.setDisabled(!valid);
57459 this.fireEvent('clientvalidation', this, valid);
57473 Roo.Form = Roo.form.Form;
57476 * Ext JS Library 1.1.1
57477 * Copyright(c) 2006-2007, Ext JS, LLC.
57479 * Originally Released Under LGPL - original licence link has changed is not relivant.
57482 * <script type="text/javascript">
57485 // as we use this in bootstrap.
57486 Roo.namespace('Roo.form');
57488 * @class Roo.form.Action
57489 * Internal Class used to handle form actions
57491 * @param {Roo.form.BasicForm} el The form element or its id
57492 * @param {Object} config Configuration options
57497 // define the action interface
57498 Roo.form.Action = function(form, options){
57500 this.options = options || {};
57503 * Client Validation Failed
57506 Roo.form.Action.CLIENT_INVALID = 'client';
57508 * Server Validation Failed
57511 Roo.form.Action.SERVER_INVALID = 'server';
57513 * Connect to Server Failed
57516 Roo.form.Action.CONNECT_FAILURE = 'connect';
57518 * Reading Data from Server Failed
57521 Roo.form.Action.LOAD_FAILURE = 'load';
57523 Roo.form.Action.prototype = {
57525 failureType : undefined,
57526 response : undefined,
57527 result : undefined,
57529 // interface method
57530 run : function(options){
57534 // interface method
57535 success : function(response){
57539 // interface method
57540 handleResponse : function(response){
57544 // default connection failure
57545 failure : function(response){
57547 this.response = response;
57548 this.failureType = Roo.form.Action.CONNECT_FAILURE;
57549 this.form.afterAction(this, false);
57552 processResponse : function(response){
57553 this.response = response;
57554 if(!response.responseText){
57557 this.result = this.handleResponse(response);
57558 return this.result;
57561 // utility functions used internally
57562 getUrl : function(appendParams){
57563 var url = this.options.url || this.form.url || this.form.el.dom.action;
57565 var p = this.getParams();
57567 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
57573 getMethod : function(){
57574 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
57577 getParams : function(){
57578 var bp = this.form.baseParams;
57579 var p = this.options.params;
57581 if(typeof p == "object"){
57582 p = Roo.urlEncode(Roo.applyIf(p, bp));
57583 }else if(typeof p == 'string' && bp){
57584 p += '&' + Roo.urlEncode(bp);
57587 p = Roo.urlEncode(bp);
57592 createCallback : function(){
57594 success: this.success,
57595 failure: this.failure,
57597 timeout: (this.form.timeout*1000),
57598 upload: this.form.fileUpload ? this.success : undefined
57603 Roo.form.Action.Submit = function(form, options){
57604 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
57607 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
57610 haveProgress : false,
57611 uploadComplete : false,
57613 // uploadProgress indicator.
57614 uploadProgress : function()
57616 if (!this.form.progressUrl) {
57620 if (!this.haveProgress) {
57621 Roo.MessageBox.progress("Uploading", "Uploading");
57623 if (this.uploadComplete) {
57624 Roo.MessageBox.hide();
57628 this.haveProgress = true;
57630 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
57632 var c = new Roo.data.Connection();
57634 url : this.form.progressUrl,
57639 success : function(req){
57640 //console.log(data);
57644 rdata = Roo.decode(req.responseText)
57646 Roo.log("Invalid data from server..");
57650 if (!rdata || !rdata.success) {
57652 Roo.MessageBox.alert(Roo.encode(rdata));
57655 var data = rdata.data;
57657 if (this.uploadComplete) {
57658 Roo.MessageBox.hide();
57663 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
57664 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
57667 this.uploadProgress.defer(2000,this);
57670 failure: function(data) {
57671 Roo.log('progress url failed ');
57682 // run get Values on the form, so it syncs any secondary forms.
57683 this.form.getValues();
57685 var o = this.options;
57686 var method = this.getMethod();
57687 var isPost = method == 'POST';
57688 if(o.clientValidation === false || this.form.isValid()){
57690 if (this.form.progressUrl) {
57691 this.form.findField('UPLOAD_IDENTIFIER').setValue(
57692 (new Date() * 1) + '' + Math.random());
57697 Roo.Ajax.request(Roo.apply(this.createCallback(), {
57698 form:this.form.el.dom,
57699 url:this.getUrl(!isPost),
57701 params:isPost ? this.getParams() : null,
57702 isUpload: this.form.fileUpload,
57703 formData : this.form.formData
57706 this.uploadProgress();
57708 }else if (o.clientValidation !== false){ // client validation failed
57709 this.failureType = Roo.form.Action.CLIENT_INVALID;
57710 this.form.afterAction(this, false);
57714 success : function(response)
57716 this.uploadComplete= true;
57717 if (this.haveProgress) {
57718 Roo.MessageBox.hide();
57722 var result = this.processResponse(response);
57723 if(result === true || result.success){
57724 this.form.afterAction(this, true);
57728 this.form.markInvalid(result.errors);
57729 this.failureType = Roo.form.Action.SERVER_INVALID;
57731 this.form.afterAction(this, false);
57733 failure : function(response)
57735 this.uploadComplete= true;
57736 if (this.haveProgress) {
57737 Roo.MessageBox.hide();
57740 this.response = response;
57741 this.failureType = Roo.form.Action.CONNECT_FAILURE;
57742 this.form.afterAction(this, false);
57745 handleResponse : function(response){
57746 if(this.form.errorReader){
57747 var rs = this.form.errorReader.read(response);
57750 for(var i = 0, len = rs.records.length; i < len; i++) {
57751 var r = rs.records[i];
57752 errors[i] = r.data;
57755 if(errors.length < 1){
57759 success : rs.success,
57765 var rt = response.responseText;
57766 if (rt.match(/^\<!--\[CDATA\[/)) {
57767 rt = rt.replace(/^\<!--\[CDATA\[/,'');
57768 rt = rt.replace(/\]\]--\>$/,'');
57771 ret = Roo.decode(rt);
57775 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
57785 Roo.form.Action.Load = function(form, options){
57786 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
57787 this.reader = this.form.reader;
57790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
57795 Roo.Ajax.request(Roo.apply(
57796 this.createCallback(), {
57797 method:this.getMethod(),
57798 url:this.getUrl(false),
57799 params:this.getParams()
57803 success : function(response){
57805 var result = this.processResponse(response);
57806 if(result === true || !result.success || !result.data){
57807 this.failureType = Roo.form.Action.LOAD_FAILURE;
57808 this.form.afterAction(this, false);
57811 this.form.clearInvalid();
57812 this.form.setValues(result.data);
57813 this.form.afterAction(this, true);
57816 handleResponse : function(response){
57817 if(this.form.reader){
57818 var rs = this.form.reader.read(response);
57819 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
57821 success : rs.success,
57825 return Roo.decode(response.responseText);
57829 Roo.form.Action.ACTION_TYPES = {
57830 'load' : Roo.form.Action.Load,
57831 'submit' : Roo.form.Action.Submit
57834 * Ext JS Library 1.1.1
57835 * Copyright(c) 2006-2007, Ext JS, LLC.
57837 * Originally Released Under LGPL - original licence link has changed is not relivant.
57840 * <script type="text/javascript">
57844 * @class Roo.form.Layout
57845 * @extends Roo.Component
57846 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
57847 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
57849 * @param {Object} config Configuration options
57851 Roo.form.Layout = function(config){
57853 if (config.items) {
57854 xitems = config.items;
57855 delete config.items;
57857 Roo.form.Layout.superclass.constructor.call(this, config);
57859 Roo.each(xitems, this.addxtype, this);
57863 Roo.extend(Roo.form.Layout, Roo.Component, {
57865 * @cfg {String/Object} autoCreate
57866 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
57869 * @cfg {String/Object/Function} style
57870 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
57871 * a function which returns such a specification.
57874 * @cfg {String} labelAlign (left|top|right)
57875 * Valid values are "left," "top" and "right" (defaults to "left")
57878 * @cfg {Number} labelWidth
57879 * Fixed width in pixels of all field labels (defaults to undefined)
57882 * @cfg {Boolean} clear
57883 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
57887 * @cfg {String} labelSeparator
57888 * The separator to use after field labels (defaults to ':')
57890 labelSeparator : ':',
57892 * @cfg {Boolean} hideLabels
57893 * True to suppress the display of field labels in this layout (defaults to false)
57895 hideLabels : false,
57898 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
57903 onRender : function(ct, position){
57904 if(this.el){ // from markup
57905 this.el = Roo.get(this.el);
57906 }else { // generate
57907 var cfg = this.getAutoCreate();
57908 this.el = ct.createChild(cfg, position);
57911 this.el.applyStyles(this.style);
57913 if(this.labelAlign){
57914 this.el.addClass('x-form-label-'+this.labelAlign);
57916 if(this.hideLabels){
57917 this.labelStyle = "display:none";
57918 this.elementStyle = "padding-left:0;";
57920 if(typeof this.labelWidth == 'number'){
57921 this.labelStyle = "width:"+this.labelWidth+"px;";
57922 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
57924 if(this.labelAlign == 'top'){
57925 this.labelStyle = "width:auto;";
57926 this.elementStyle = "padding-left:0;";
57929 var stack = this.stack;
57930 var slen = stack.length;
57932 if(!this.fieldTpl){
57933 var t = new Roo.Template(
57934 '<div class="x-form-item {5}">',
57935 '<label for="{0}" style="{2}">{1}{4}</label>',
57936 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
57938 '</div><div class="x-form-clear-left"></div>'
57940 t.disableFormats = true;
57942 Roo.form.Layout.prototype.fieldTpl = t;
57944 for(var i = 0; i < slen; i++) {
57945 if(stack[i].isFormField){
57946 this.renderField(stack[i]);
57948 this.renderComponent(stack[i]);
57953 this.el.createChild({cls:'x-form-clear'});
57958 renderField : function(f){
57959 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
57962 f.labelStyle||this.labelStyle||'', //2
57963 this.elementStyle||'', //3
57964 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
57965 f.itemCls||this.itemCls||'' //5
57966 ], true).getPrevSibling());
57970 renderComponent : function(c){
57971 c.render(c.isLayout ? this.el : this.el.createChild());
57974 * Adds a object form elements (using the xtype property as the factory method.)
57975 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
57976 * @param {Object} config
57978 addxtype : function(o)
57980 // create the lement.
57981 o.form = this.form;
57982 var fe = Roo.factory(o, Roo.form);
57983 this.form.allItems.push(fe);
57984 this.stack.push(fe);
57986 if (fe.isFormField) {
57987 this.form.items.add(fe);
57996 * @class Roo.form.Column
57997 * @extends Roo.form.Layout
57998 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
57999 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
58001 * @param {Object} config Configuration options
58003 Roo.form.Column = function(config){
58004 Roo.form.Column.superclass.constructor.call(this, config);
58007 Roo.extend(Roo.form.Column, Roo.form.Layout, {
58009 * @cfg {Number/String} width
58010 * The fixed width of the column in pixels or CSS value (defaults to "auto")
58013 * @cfg {String/Object} autoCreate
58014 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
58018 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
58021 onRender : function(ct, position){
58022 Roo.form.Column.superclass.onRender.call(this, ct, position);
58024 this.el.setWidth(this.width);
58030 * @class Roo.form.Row
58031 * @extends Roo.form.Layout
58032 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
58033 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
58035 * @param {Object} config Configuration options
58039 Roo.form.Row = function(config){
58040 Roo.form.Row.superclass.constructor.call(this, config);
58043 Roo.extend(Roo.form.Row, Roo.form.Layout, {
58045 * @cfg {Number/String} width
58046 * The fixed width of the column in pixels or CSS value (defaults to "auto")
58049 * @cfg {Number/String} height
58050 * The fixed height of the column in pixels or CSS value (defaults to "auto")
58052 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
58056 onRender : function(ct, position){
58057 //console.log('row render');
58059 var t = new Roo.Template(
58060 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
58061 '<label for="{0}" style="{2}">{1}{4}</label>',
58062 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
58066 t.disableFormats = true;
58068 Roo.form.Layout.prototype.rowTpl = t;
58070 this.fieldTpl = this.rowTpl;
58072 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
58073 var labelWidth = 100;
58075 if ((this.labelAlign != 'top')) {
58076 if (typeof this.labelWidth == 'number') {
58077 labelWidth = this.labelWidth
58079 this.padWidth = 20 + labelWidth;
58083 Roo.form.Column.superclass.onRender.call(this, ct, position);
58085 this.el.setWidth(this.width);
58088 this.el.setHeight(this.height);
58093 renderField : function(f){
58094 f.fieldEl = this.fieldTpl.append(this.el, [
58095 f.id, f.fieldLabel,
58096 f.labelStyle||this.labelStyle||'',
58097 this.elementStyle||'',
58098 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
58099 f.itemCls||this.itemCls||'',
58100 f.width ? f.width + this.padWidth : 160 + this.padWidth
58107 * @class Roo.form.FieldSet
58108 * @extends Roo.form.Layout
58109 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
58110 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
58112 * @param {Object} config Configuration options
58114 Roo.form.FieldSet = function(config){
58115 Roo.form.FieldSet.superclass.constructor.call(this, config);
58118 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
58120 * @cfg {String} legend
58121 * The text to display as the legend for the FieldSet (defaults to '')
58124 * @cfg {String/Object} autoCreate
58125 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
58129 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
58132 onRender : function(ct, position){
58133 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
58135 this.setLegend(this.legend);
58140 setLegend : function(text){
58142 this.el.child('legend').update(text);
58147 * Ext JS Library 1.1.1
58148 * Copyright(c) 2006-2007, Ext JS, LLC.
58150 * Originally Released Under LGPL - original licence link has changed is not relivant.
58153 * <script type="text/javascript">
58156 * @class Roo.form.VTypes
58157 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
58160 Roo.form.VTypes = function(){
58161 // closure these in so they are only created once.
58162 var alpha = /^[a-zA-Z_]+$/;
58163 var alphanum = /^[a-zA-Z0-9_]+$/;
58164 var email = /^([\w'-]+)(\.[\w'-]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
58165 var url = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
58166 var urlWeb = /^((https?):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
58168 // All these messages and functions are configurable
58171 * The function used to validate email addresses
58172 * @param {String} value The email address
58174 email : function(v){
58175 return email.test(v);
58178 * The error text to display when the email validation function returns false
58181 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
58183 * The keystroke filter mask to be applied on email input
58186 emailMask : /[a-z0-9_\.\-@]/i,
58189 * The function used to validate URLs
58190 * @param {String} value The URL
58193 return url.test(v);
58196 * The funciton used to validate URLs (only allow schemes 'https' and 'http')
58197 * @param {String} v The URL
58199 urlWeb : function(v) {
58200 return urlWeb.test(v);
58203 * The error text to display when the url validation function returns false
58206 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
58209 * The function used to validate alpha values
58210 * @param {String} value The value
58212 alpha : function(v){
58213 return alpha.test(v);
58216 * The error text to display when the alpha validation function returns false
58219 alphaText : 'This field should only contain letters and _',
58221 * The keystroke filter mask to be applied on alpha input
58224 alphaMask : /[a-z_]/i,
58227 * The function used to validate alphanumeric values
58228 * @param {String} value The value
58230 alphanum : function(v){
58231 return alphanum.test(v);
58234 * The error text to display when the alphanumeric validation function returns false
58237 alphanumText : 'This field should only contain letters, numbers and _',
58239 * The keystroke filter mask to be applied on alphanumeric input
58242 alphanumMask : /[a-z0-9_]/i
58244 }();//<script type="text/javascript">
58247 * @class Roo.form.FCKeditor
58248 * @extends Roo.form.TextArea
58249 * Wrapper around the FCKEditor http://www.fckeditor.net
58251 * Creates a new FCKeditor
58252 * @param {Object} config Configuration options
58254 Roo.form.FCKeditor = function(config){
58255 Roo.form.FCKeditor.superclass.constructor.call(this, config);
58258 * @event editorinit
58259 * Fired when the editor is initialized - you can add extra handlers here..
58260 * @param {FCKeditor} this
58261 * @param {Object} the FCK object.
58268 Roo.form.FCKeditor.editors = { };
58269 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
58271 //defaultAutoCreate : {
58272 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
58276 * @cfg {Object} fck options - see fck manual for details.
58281 * @cfg {Object} fck toolbar set (Basic or Default)
58283 toolbarSet : 'Basic',
58285 * @cfg {Object} fck BasePath
58287 basePath : '/fckeditor/',
58295 onRender : function(ct, position)
58298 this.defaultAutoCreate = {
58300 style:"width:300px;height:60px;",
58301 autocomplete: "new-password"
58304 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
58307 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
58308 if(this.preventScrollbars){
58309 this.el.setStyle("overflow", "hidden");
58311 this.el.setHeight(this.growMin);
58314 //console.log('onrender' + this.getId() );
58315 Roo.form.FCKeditor.editors[this.getId()] = this;
58318 this.replaceTextarea() ;
58322 getEditor : function() {
58323 return this.fckEditor;
58326 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
58327 * @param {Mixed} value The value to set
58331 setValue : function(value)
58333 //console.log('setValue: ' + value);
58335 if(typeof(value) == 'undefined') { // not sure why this is happending...
58338 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
58340 //if(!this.el || !this.getEditor()) {
58341 // this.value = value;
58342 //this.setValue.defer(100,this,[value]);
58346 if(!this.getEditor()) {
58350 this.getEditor().SetData(value);
58357 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
58358 * @return {Mixed} value The field value
58360 getValue : function()
58363 if (this.frame && this.frame.dom.style.display == 'none') {
58364 return Roo.form.FCKeditor.superclass.getValue.call(this);
58367 if(!this.el || !this.getEditor()) {
58369 // this.getValue.defer(100,this);
58374 var value=this.getEditor().GetData();
58375 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
58376 return Roo.form.FCKeditor.superclass.getValue.call(this);
58382 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
58383 * @return {Mixed} value The field value
58385 getRawValue : function()
58387 if (this.frame && this.frame.dom.style.display == 'none') {
58388 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
58391 if(!this.el || !this.getEditor()) {
58392 //this.getRawValue.defer(100,this);
58399 var value=this.getEditor().GetData();
58400 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
58401 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
58405 setSize : function(w,h) {
58409 //if (this.frame && this.frame.dom.style.display == 'none') {
58410 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
58413 //if(!this.el || !this.getEditor()) {
58414 // this.setSize.defer(100,this, [w,h]);
58420 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
58422 this.frame.dom.setAttribute('width', w);
58423 this.frame.dom.setAttribute('height', h);
58424 this.frame.setSize(w,h);
58428 toggleSourceEdit : function(value) {
58432 this.el.dom.style.display = value ? '' : 'none';
58433 this.frame.dom.style.display = value ? 'none' : '';
58438 focus: function(tag)
58440 if (this.frame.dom.style.display == 'none') {
58441 return Roo.form.FCKeditor.superclass.focus.call(this);
58443 if(!this.el || !this.getEditor()) {
58444 this.focus.defer(100,this, [tag]);
58451 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
58452 this.getEditor().Focus();
58454 if (!this.getEditor().Selection.GetSelection()) {
58455 this.focus.defer(100,this, [tag]);
58460 var r = this.getEditor().EditorDocument.createRange();
58461 r.setStart(tgs[0],0);
58462 r.setEnd(tgs[0],0);
58463 this.getEditor().Selection.GetSelection().removeAllRanges();
58464 this.getEditor().Selection.GetSelection().addRange(r);
58465 this.getEditor().Focus();
58472 replaceTextarea : function()
58474 if ( document.getElementById( this.getId() + '___Frame' ) ) {
58477 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
58479 // We must check the elements firstly using the Id and then the name.
58480 var oTextarea = document.getElementById( this.getId() );
58482 var colElementsByName = document.getElementsByName( this.getId() ) ;
58484 oTextarea.style.display = 'none' ;
58486 if ( oTextarea.tabIndex ) {
58487 this.TabIndex = oTextarea.tabIndex ;
58490 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
58491 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
58492 this.frame = Roo.get(this.getId() + '___Frame')
58495 _getConfigHtml : function()
58499 for ( var o in this.fckconfig ) {
58500 sConfig += sConfig.length > 0 ? '&' : '';
58501 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
58504 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
58508 _getIFrameHtml : function()
58510 var sFile = 'fckeditor.html' ;
58511 /* no idea what this is about..
58514 if ( (/fcksource=true/i).test( window.top.location.search ) )
58515 sFile = 'fckeditor.original.html' ;
58520 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
58521 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
58524 var html = '<iframe id="' + this.getId() +
58525 '___Frame" src="' + sLink +
58526 '" width="' + this.width +
58527 '" height="' + this.height + '"' +
58528 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
58529 ' frameborder="0" scrolling="no"></iframe>' ;
58534 _insertHtmlBefore : function( html, element )
58536 if ( element.insertAdjacentHTML ) {
58538 element.insertAdjacentHTML( 'beforeBegin', html ) ;
58540 var oRange = document.createRange() ;
58541 oRange.setStartBefore( element ) ;
58542 var oFragment = oRange.createContextualFragment( html );
58543 element.parentNode.insertBefore( oFragment, element ) ;
58556 //Roo.reg('fckeditor', Roo.form.FCKeditor);
58558 function FCKeditor_OnComplete(editorInstance){
58559 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
58560 f.fckEditor = editorInstance;
58561 //console.log("loaded");
58562 f.fireEvent('editorinit', f, editorInstance);
58582 //<script type="text/javascript">
58584 * @class Roo.form.GridField
58585 * @extends Roo.form.Field
58586 * Embed a grid (or editable grid into a form)
58589 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
58591 * xgrid.store = Roo.data.Store
58592 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
58593 * xgrid.store.reader = Roo.data.JsonReader
58597 * Creates a new GridField
58598 * @param {Object} config Configuration options
58600 Roo.form.GridField = function(config){
58601 Roo.form.GridField.superclass.constructor.call(this, config);
58605 Roo.extend(Roo.form.GridField, Roo.form.Field, {
58607 * @cfg {Number} width - used to restrict width of grid..
58611 * @cfg {Number} height - used to restrict height of grid..
58615 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
58621 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58622 * {tag: "input", type: "checkbox", autocomplete: "off"})
58624 // defaultAutoCreate : { tag: 'div' },
58625 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
58627 * @cfg {String} addTitle Text to include for adding a title.
58631 onResize : function(){
58632 Roo.form.Field.superclass.onResize.apply(this, arguments);
58635 initEvents : function(){
58636 // Roo.form.Checkbox.superclass.initEvents.call(this);
58637 // has no events...
58642 getResizeEl : function(){
58646 getPositionEl : function(){
58651 onRender : function(ct, position){
58653 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
58654 var style = this.style;
58657 Roo.form.GridField.superclass.onRender.call(this, ct, position);
58658 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
58659 this.viewEl = this.wrap.createChild({ tag: 'div' });
58661 this.viewEl.applyStyles(style);
58664 this.viewEl.setWidth(this.width);
58667 this.viewEl.setHeight(this.height);
58669 //if(this.inputValue !== undefined){
58670 //this.setValue(this.value);
58673 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
58676 this.grid.render();
58677 this.grid.getDataSource().on('remove', this.refreshValue, this);
58678 this.grid.getDataSource().on('update', this.refreshValue, this);
58679 this.grid.on('afteredit', this.refreshValue, this);
58685 * Sets the value of the item.
58686 * @param {String} either an object or a string..
58688 setValue : function(v){
58690 v = v || []; // empty set..
58691 // this does not seem smart - it really only affects memoryproxy grids..
58692 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
58693 var ds = this.grid.getDataSource();
58694 // assumes a json reader..
58696 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
58697 ds.loadData( data);
58699 // clear selection so it does not get stale.
58700 if (this.grid.sm) {
58701 this.grid.sm.clearSelections();
58704 Roo.form.GridField.superclass.setValue.call(this, v);
58705 this.refreshValue();
58706 // should load data in the grid really....
58710 refreshValue: function() {
58712 this.grid.getDataSource().each(function(r) {
58715 this.el.dom.value = Roo.encode(val);
58723 * Ext JS Library 1.1.1
58724 * Copyright(c) 2006-2007, Ext JS, LLC.
58726 * Originally Released Under LGPL - original licence link has changed is not relivant.
58729 * <script type="text/javascript">
58732 * @class Roo.form.DisplayField
58733 * @extends Roo.form.Field
58734 * A generic Field to display non-editable data.
58735 * @cfg {Boolean} closable (true|false) default false
58737 * Creates a new Display Field item.
58738 * @param {Object} config Configuration options
58740 Roo.form.DisplayField = function(config){
58741 Roo.form.DisplayField.superclass.constructor.call(this, config);
58746 * Fires after the click the close btn
58747 * @param {Roo.form.DisplayField} this
58753 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
58754 inputType: 'hidden',
58760 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
58762 focusClass : undefined,
58764 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
58766 fieldClass: 'x-form-field',
58769 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
58771 valueRenderer: undefined,
58775 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58776 * {tag: "input", type: "checkbox", autocomplete: "off"})
58779 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
58783 onResize : function(){
58784 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
58788 initEvents : function(){
58789 // Roo.form.Checkbox.superclass.initEvents.call(this);
58790 // has no events...
58793 this.closeEl.on('click', this.onClose, this);
58799 getResizeEl : function(){
58803 getPositionEl : function(){
58808 onRender : function(ct, position){
58810 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
58811 //if(this.inputValue !== undefined){
58812 this.wrap = this.el.wrap();
58814 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
58817 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
58820 if (this.bodyStyle) {
58821 this.viewEl.applyStyles(this.bodyStyle);
58823 //this.viewEl.setStyle('padding', '2px');
58825 this.setValue(this.value);
58830 initValue : Roo.emptyFn,
58835 onClick : function(){
58840 * Sets the checked state of the checkbox.
58841 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
58843 setValue : function(v){
58845 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
58846 // this might be called before we have a dom element..
58847 if (!this.viewEl) {
58850 this.viewEl.dom.innerHTML = html;
58851 Roo.form.DisplayField.superclass.setValue.call(this, v);
58855 onClose : function(e)
58857 e.preventDefault();
58859 this.fireEvent('close', this);
58868 * @class Roo.form.DayPicker
58869 * @extends Roo.form.Field
58870 * A Day picker show [M] [T] [W] ....
58872 * Creates a new Day Picker
58873 * @param {Object} config Configuration options
58875 Roo.form.DayPicker= function(config){
58876 Roo.form.DayPicker.superclass.constructor.call(this, config);
58880 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
58882 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
58884 focusClass : undefined,
58886 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
58888 fieldClass: "x-form-field",
58891 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
58892 * {tag: "input", type: "checkbox", autocomplete: "off"})
58894 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
58897 actionMode : 'viewEl',
58901 inputType : 'hidden',
58904 inputElement: false, // real input element?
58905 basedOn: false, // ????
58907 isFormField: true, // not sure where this is needed!!!!
58909 onResize : function(){
58910 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
58911 if(!this.boxLabel){
58912 this.el.alignTo(this.wrap, 'c-c');
58916 initEvents : function(){
58917 Roo.form.Checkbox.superclass.initEvents.call(this);
58918 this.el.on("click", this.onClick, this);
58919 this.el.on("change", this.onClick, this);
58923 getResizeEl : function(){
58927 getPositionEl : function(){
58933 onRender : function(ct, position){
58934 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
58936 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
58938 var r1 = '<table><tr>';
58939 var r2 = '<tr class="x-form-daypick-icons">';
58940 for (var i=0; i < 7; i++) {
58941 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
58942 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
58945 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
58946 viewEl.select('img').on('click', this.onClick, this);
58947 this.viewEl = viewEl;
58950 // this will not work on Chrome!!!
58951 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
58952 this.el.on('propertychange', this.setFromHidden, this); //ie
58960 initValue : Roo.emptyFn,
58963 * Returns the checked state of the checkbox.
58964 * @return {Boolean} True if checked, else false
58966 getValue : function(){
58967 return this.el.dom.value;
58972 onClick : function(e){
58973 //this.setChecked(!this.checked);
58974 Roo.get(e.target).toggleClass('x-menu-item-checked');
58975 this.refreshValue();
58976 //if(this.el.dom.checked != this.checked){
58977 // this.setValue(this.el.dom.checked);
58982 refreshValue : function()
58985 this.viewEl.select('img',true).each(function(e,i,n) {
58986 val += e.is(".x-menu-item-checked") ? String(n) : '';
58988 this.setValue(val, true);
58992 * Sets the checked state of the checkbox.
58993 * On is always based on a string comparison between inputValue and the param.
58994 * @param {Boolean/String} value - the value to set
58995 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
58997 setValue : function(v,suppressEvent){
58998 if (!this.el.dom) {
59001 var old = this.el.dom.value ;
59002 this.el.dom.value = v;
59003 if (suppressEvent) {
59007 // update display..
59008 this.viewEl.select('img',true).each(function(e,i,n) {
59010 var on = e.is(".x-menu-item-checked");
59011 var newv = v.indexOf(String(n)) > -1;
59013 e.toggleClass('x-menu-item-checked');
59019 this.fireEvent('change', this, v, old);
59024 // handle setting of hidden value by some other method!!?!?
59025 setFromHidden: function()
59030 //console.log("SET FROM HIDDEN");
59031 //alert('setFrom hidden');
59032 this.setValue(this.el.dom.value);
59035 onDestroy : function()
59038 Roo.get(this.viewEl).remove();
59041 Roo.form.DayPicker.superclass.onDestroy.call(this);
59045 * RooJS Library 1.1.1
59046 * Copyright(c) 2008-2011 Alan Knowles
59053 * @class Roo.form.ComboCheck
59054 * @extends Roo.form.ComboBox
59055 * A combobox for multiple select items.
59057 * FIXME - could do with a reset button..
59060 * Create a new ComboCheck
59061 * @param {Object} config Configuration options
59063 Roo.form.ComboCheck = function(config){
59064 Roo.form.ComboCheck.superclass.constructor.call(this, config);
59065 // should verify some data...
59067 // hiddenName = required..
59068 // displayField = required
59069 // valudField == required
59070 var req= [ 'hiddenName', 'displayField', 'valueField' ];
59072 Roo.each(req, function(e) {
59073 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
59074 throw "Roo.form.ComboCheck : missing value for: " + e;
59081 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
59086 selectedClass: 'x-menu-item-checked',
59089 onRender : function(ct, position){
59095 var cls = 'x-combo-list';
59098 this.tpl = new Roo.Template({
59099 html : '<div class="'+cls+'-item x-menu-check-item">' +
59100 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
59101 '<span>{' + this.displayField + '}</span>' +
59108 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
59109 this.view.singleSelect = false;
59110 this.view.multiSelect = true;
59111 this.view.toggleSelect = true;
59112 this.pageTb.add(new Roo.Toolbar.Fill(),{
59114 text: 'Select All',
59115 handler: function() {
59121 handler: function() {
59127 cleanLeadingSpace : function(e)
59129 // this is disabled, as it retriggers setvalue on blur
59132 doForce : function() {
59133 // no idea what this did, but it blanks out our values.
59136 onViewOver : function(e, t){
59142 onViewClick : function(doFocus,index){
59146 select: function () {
59147 //Roo.log("SELECT CALLED");
59150 selectByValue : function(xv, scrollIntoView){
59151 var ar = this.getValueArray();
59154 Roo.each(ar, function(v) {
59155 if(v === undefined || v === null){
59158 var r = this.findRecord(this.valueField, v);
59160 sels.push(this.store.indexOf(r))
59164 this.view.select(sels);
59168 selectAll : function()
59171 this.store.each(function(r,i) {
59174 this.view.select(sels);
59180 onSelect : function(record, index){
59181 // Roo.log("onselect Called");
59182 // this is only called by the clear button now..
59183 this.view.clearSelections();
59184 this.setValue('[]');
59185 if (this.value != this.valueBefore) {
59186 this.fireEvent('change', this, this.value, this.valueBefore);
59187 this.valueBefore = this.value;
59190 getValueArray : function()
59195 //Roo.log(this.value);
59196 if (typeof(this.value) == 'undefined') {
59199 var ar = Roo.decode(this.value);
59200 return ar instanceof Array ? ar : []; //?? valid?
59203 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
59208 expand : function ()
59211 Roo.form.ComboCheck.superclass.expand.call(this);
59212 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
59213 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
59218 collapse : function(){
59219 Roo.form.ComboCheck.superclass.collapse.call(this);
59220 var sl = this.view.getSelectedIndexes();
59221 var st = this.store;
59225 Roo.each(sl, function(i) {
59227 nv.push(r.get(this.valueField));
59229 this.setValue(Roo.encode(nv));
59230 if (this.value != this.valueBefore) {
59232 this.fireEvent('change', this, this.value, this.valueBefore);
59233 this.valueBefore = this.value;
59238 setValue : function(v){
59242 var vals = this.getValueArray();
59244 Roo.each(vals, function(k) {
59245 var r = this.findRecord(this.valueField, k);
59247 tv.push(r.data[this.displayField]);
59248 }else if(this.valueNotFoundText !== undefined){
59249 tv.push( this.valueNotFoundText );
59254 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
59255 this.hiddenField.value = v;
59261 * Ext JS Library 1.1.1
59262 * Copyright(c) 2006-2007, Ext JS, LLC.
59264 * Originally Released Under LGPL - original licence link has changed is not relivant.
59267 * <script type="text/javascript">
59271 * @class Roo.form.Signature
59272 * @extends Roo.form.Field
59276 * @param {Object} config Configuration options
59279 Roo.form.Signature = function(config){
59280 Roo.form.Signature.superclass.constructor.call(this, config);
59282 this.addEvents({// not in used??
59285 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
59286 * @param {Roo.form.Signature} combo This combo box
59291 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
59292 * @param {Roo.form.ComboBox} combo This combo box
59293 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
59299 Roo.extend(Roo.form.Signature, Roo.form.Field, {
59301 * @cfg {Object} labels Label to use when rendering a form.
59305 * confirm : "Confirm"
59310 confirm : "Confirm"
59313 * @cfg {Number} width The signature panel width (defaults to 300)
59317 * @cfg {Number} height The signature panel height (defaults to 100)
59321 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
59323 allowBlank : false,
59326 // {Object} signPanel The signature SVG panel element (defaults to {})
59328 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
59329 isMouseDown : false,
59330 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
59331 isConfirmed : false,
59332 // {String} signatureTmp SVG mapping string (defaults to empty string)
59336 defaultAutoCreate : { // modified by initCompnoent..
59342 onRender : function(ct, position){
59344 Roo.form.Signature.superclass.onRender.call(this, ct, position);
59346 this.wrap = this.el.wrap({
59347 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
59350 this.createToolbar(this);
59351 this.signPanel = this.wrap.createChild({
59353 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
59357 this.svgID = Roo.id();
59358 this.svgEl = this.signPanel.createChild({
59359 xmlns : 'http://www.w3.org/2000/svg',
59361 id : this.svgID + "-svg",
59363 height: this.height,
59364 viewBox: '0 0 '+this.width+' '+this.height,
59368 id: this.svgID + "-svg-r",
59370 height: this.height,
59375 id: this.svgID + "-svg-l",
59377 y1: (this.height*0.8), // start set the line in 80% of height
59378 x2: this.width, // end
59379 y2: (this.height*0.8), // end set the line in 80% of height
59381 'stroke-width': "1",
59382 'stroke-dasharray': "3",
59383 'shape-rendering': "crispEdges",
59384 'pointer-events': "none"
59388 id: this.svgID + "-svg-p",
59390 'stroke-width': "3",
59392 'pointer-events': 'none'
59397 this.svgBox = this.svgEl.dom.getScreenCTM();
59399 createSVG : function(){
59400 var svg = this.signPanel;
59401 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
59404 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
59405 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
59406 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
59407 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
59408 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
59409 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
59410 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
59413 isTouchEvent : function(e){
59414 return e.type.match(/^touch/);
59416 getCoords : function (e) {
59417 var pt = this.svgEl.dom.createSVGPoint();
59420 if (this.isTouchEvent(e)) {
59421 pt.x = e.targetTouches[0].clientX;
59422 pt.y = e.targetTouches[0].clientY;
59424 var a = this.svgEl.dom.getScreenCTM();
59425 var b = a.inverse();
59426 var mx = pt.matrixTransform(b);
59427 return mx.x + ',' + mx.y;
59429 //mouse event headler
59430 down : function (e) {
59431 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
59432 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
59434 this.isMouseDown = true;
59436 e.preventDefault();
59438 move : function (e) {
59439 if (this.isMouseDown) {
59440 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
59441 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
59444 e.preventDefault();
59446 up : function (e) {
59447 this.isMouseDown = false;
59448 var sp = this.signatureTmp.split(' ');
59451 if(!sp[sp.length-2].match(/^L/)){
59455 this.signatureTmp = sp.join(" ");
59458 if(this.getValue() != this.signatureTmp){
59459 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59460 this.isConfirmed = false;
59462 e.preventDefault();
59466 * Protected method that will not generally be called directly. It
59467 * is called when the editor creates its toolbar. Override this method if you need to
59468 * add custom toolbar buttons.
59469 * @param {HtmlEditor} editor
59471 createToolbar : function(editor){
59472 function btn(id, toggle, handler){
59473 var xid = fid + '-'+ id ;
59477 cls : 'x-btn-icon x-edit-'+id,
59478 enableToggle:toggle !== false,
59479 scope: editor, // was editor...
59480 handler:handler||editor.relayBtnCmd,
59481 clickEvent:'mousedown',
59482 tooltip: etb.buttonTips[id] || undefined, ///tips ???
59488 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
59492 cls : ' x-signature-btn x-signature-'+id,
59493 scope: editor, // was editor...
59494 handler: this.reset,
59495 clickEvent:'mousedown',
59496 text: this.labels.clear
59503 cls : ' x-signature-btn x-signature-'+id,
59504 scope: editor, // was editor...
59505 handler: this.confirmHandler,
59506 clickEvent:'mousedown',
59507 text: this.labels.confirm
59514 * when user is clicked confirm then show this image.....
59516 * @return {String} Image Data URI
59518 getImageDataURI : function(){
59519 var svg = this.svgEl.dom.parentNode.innerHTML;
59520 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
59525 * @return {Boolean} this.isConfirmed
59527 getConfirmed : function(){
59528 return this.isConfirmed;
59532 * @return {Number} this.width
59534 getWidth : function(){
59539 * @return {Number} this.height
59541 getHeight : function(){
59542 return this.height;
59545 getSignature : function(){
59546 return this.signatureTmp;
59549 reset : function(){
59550 this.signatureTmp = '';
59551 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59552 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
59553 this.isConfirmed = false;
59554 Roo.form.Signature.superclass.reset.call(this);
59556 setSignature : function(s){
59557 this.signatureTmp = s;
59558 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
59559 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
59561 this.isConfirmed = false;
59562 Roo.form.Signature.superclass.reset.call(this);
59565 // Roo.log(this.signPanel.dom.contentWindow.up())
59568 setConfirmed : function(){
59572 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
59575 confirmHandler : function(){
59576 if(!this.getSignature()){
59580 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
59581 this.setValue(this.getSignature());
59582 this.isConfirmed = true;
59584 this.fireEvent('confirm', this);
59587 // Subclasses should provide the validation implementation by overriding this
59588 validateValue : function(value){
59589 if(this.allowBlank){
59593 if(this.isConfirmed){
59600 * Ext JS Library 1.1.1
59601 * Copyright(c) 2006-2007, Ext JS, LLC.
59603 * Originally Released Under LGPL - original licence link has changed is not relivant.
59606 * <script type="text/javascript">
59611 * @class Roo.form.ComboBox
59612 * @extends Roo.form.TriggerField
59613 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
59615 * Create a new ComboBox.
59616 * @param {Object} config Configuration options
59618 Roo.form.Select = function(config){
59619 Roo.form.Select.superclass.constructor.call(this, config);
59623 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
59625 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
59628 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
59629 * rendering into an Roo.Editor, defaults to false)
59632 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
59633 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
59636 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
59639 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
59640 * the dropdown list (defaults to undefined, with no header element)
59644 * @cfg {String/Roo.Template} tpl The template to use to render the output
59648 defaultAutoCreate : {tag: "select" },
59650 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
59652 listWidth: undefined,
59654 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
59655 * mode = 'remote' or 'text' if mode = 'local')
59657 displayField: undefined,
59659 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
59660 * mode = 'remote' or 'value' if mode = 'local').
59661 * Note: use of a valueField requires the user make a selection
59662 * in order for a value to be mapped.
59664 valueField: undefined,
59668 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
59669 * field's data value (defaults to the underlying DOM element's name)
59671 hiddenName: undefined,
59673 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
59677 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
59679 selectedClass: 'x-combo-selected',
59681 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
59682 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
59683 * which displays a downward arrow icon).
59685 triggerClass : 'x-form-arrow-trigger',
59687 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
59691 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
59692 * anchor positions (defaults to 'tl-bl')
59694 listAlign: 'tl-bl?',
59696 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
59700 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
59701 * query specified by the allQuery config option (defaults to 'query')
59703 triggerAction: 'query',
59705 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
59706 * (defaults to 4, does not apply if editable = false)
59710 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
59711 * delay (typeAheadDelay) if it matches a known value (defaults to false)
59715 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
59716 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
59720 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
59721 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
59725 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
59726 * when editable = true (defaults to false)
59728 selectOnFocus:false,
59730 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
59732 queryParam: 'query',
59734 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
59735 * when mode = 'remote' (defaults to 'Loading...')
59737 loadingText: 'Loading...',
59739 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
59743 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
59747 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
59748 * traditional select (defaults to true)
59752 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
59756 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
59760 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
59761 * listWidth has a higher value)
59765 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
59766 * allow the user to set arbitrary text into the field (defaults to false)
59768 forceSelection:false,
59770 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
59771 * if typeAhead = true (defaults to 250)
59773 typeAheadDelay : 250,
59775 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
59776 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
59778 valueNotFoundText : undefined,
59781 * @cfg {String} defaultValue The value displayed after loading the store.
59786 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
59788 blockFocus : false,
59791 * @cfg {Boolean} disableClear Disable showing of clear button.
59793 disableClear : false,
59795 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
59797 alwaysQuery : false,
59803 // element that contains real text value.. (when hidden is used..)
59806 onRender : function(ct, position){
59807 Roo.form.Field.prototype.onRender.call(this, ct, position);
59810 this.store.on('beforeload', this.onBeforeLoad, this);
59811 this.store.on('load', this.onLoad, this);
59812 this.store.on('loadexception', this.onLoadException, this);
59813 this.store.load({});
59821 initEvents : function(){
59822 //Roo.form.ComboBox.superclass.initEvents.call(this);
59826 onDestroy : function(){
59829 this.store.un('beforeload', this.onBeforeLoad, this);
59830 this.store.un('load', this.onLoad, this);
59831 this.store.un('loadexception', this.onLoadException, this);
59833 //Roo.form.ComboBox.superclass.onDestroy.call(this);
59837 fireKey : function(e){
59838 if(e.isNavKeyPress() && !this.list.isVisible()){
59839 this.fireEvent("specialkey", this, e);
59844 onResize: function(w, h){
59852 * Allow or prevent the user from directly editing the field text. If false is passed,
59853 * the user will only be able to select from the items defined in the dropdown list. This method
59854 * is the runtime equivalent of setting the 'editable' config option at config time.
59855 * @param {Boolean} value True to allow the user to directly edit the field text
59857 setEditable : function(value){
59862 onBeforeLoad : function(){
59864 Roo.log("Select before load");
59867 this.innerList.update(this.loadingText ?
59868 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
59869 //this.restrictHeight();
59870 this.selectedIndex = -1;
59874 onLoad : function(){
59877 var dom = this.el.dom;
59878 dom.innerHTML = '';
59879 var od = dom.ownerDocument;
59881 if (this.emptyText) {
59882 var op = od.createElement('option');
59883 op.setAttribute('value', '');
59884 op.innerHTML = String.format('{0}', this.emptyText);
59885 dom.appendChild(op);
59887 if(this.store.getCount() > 0){
59889 var vf = this.valueField;
59890 var df = this.displayField;
59891 this.store.data.each(function(r) {
59892 // which colmsn to use... testing - cdoe / title..
59893 var op = od.createElement('option');
59894 op.setAttribute('value', r.data[vf]);
59895 op.innerHTML = String.format('{0}', r.data[df]);
59896 dom.appendChild(op);
59898 if (typeof(this.defaultValue != 'undefined')) {
59899 this.setValue(this.defaultValue);
59904 //this.onEmptyResults();
59909 onLoadException : function()
59911 dom.innerHTML = '';
59913 Roo.log("Select on load exception");
59917 Roo.log(this.store.reader.jsonData);
59918 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
59919 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
59925 onTypeAhead : function(){
59930 onSelect : function(record, index){
59931 Roo.log('on select?');
59933 if(this.fireEvent('beforeselect', this, record, index) !== false){
59934 this.setFromData(index > -1 ? record.data : false);
59936 this.fireEvent('select', this, record, index);
59941 * Returns the currently selected field value or empty string if no value is set.
59942 * @return {String} value The selected value
59944 getValue : function(){
59945 var dom = this.el.dom;
59946 this.value = dom.options[dom.selectedIndex].value;
59952 * Clears any text/value currently set in the field
59954 clearValue : function(){
59956 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
59961 * Sets the specified value into the field. If the value finds a match, the corresponding record text
59962 * will be displayed in the field. If the value does not match the data value of an existing item,
59963 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
59964 * Otherwise the field will be blank (although the value will still be set).
59965 * @param {String} value The value to match
59967 setValue : function(v){
59968 var d = this.el.dom;
59969 for (var i =0; i < d.options.length;i++) {
59970 if (v == d.options[i].value) {
59971 d.selectedIndex = i;
59979 * @property {Object} the last set data for the element
59984 * Sets the value of the field based on a object which is related to the record format for the store.
59985 * @param {Object} value the value to set as. or false on reset?
59987 setFromData : function(o){
59988 Roo.log('setfrom data?');
59994 reset : function(){
59998 findRecord : function(prop, value){
60003 if(this.store.getCount() > 0){
60004 this.store.each(function(r){
60005 if(r.data[prop] == value){
60015 getName: function()
60017 // returns hidden if it's set..
60018 if (!this.rendered) {return ''};
60019 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
60027 onEmptyResults : function(){
60028 Roo.log('empty results');
60033 * Returns true if the dropdown list is expanded, else false.
60035 isExpanded : function(){
60040 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
60041 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
60042 * @param {String} value The data value of the item to select
60043 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
60044 * selected item if it is not currently in view (defaults to true)
60045 * @return {Boolean} True if the value matched an item in the list, else false
60047 selectByValue : function(v, scrollIntoView){
60048 Roo.log('select By Value');
60051 if(v !== undefined && v !== null){
60052 var r = this.findRecord(this.valueField || this.displayField, v);
60054 this.select(this.store.indexOf(r), scrollIntoView);
60062 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
60063 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
60064 * @param {Number} index The zero-based index of the list item to select
60065 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
60066 * selected item if it is not currently in view (defaults to true)
60068 select : function(index, scrollIntoView){
60069 Roo.log('select ');
60072 this.selectedIndex = index;
60073 this.view.select(index);
60074 if(scrollIntoView !== false){
60075 var el = this.view.getNode(index);
60077 this.innerList.scrollChildIntoView(el, false);
60085 validateBlur : function(){
60092 initQuery : function(){
60093 this.doQuery(this.getRawValue());
60097 doForce : function(){
60098 if(this.el.dom.value.length > 0){
60099 this.el.dom.value =
60100 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
60106 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
60107 * query allowing the query action to be canceled if needed.
60108 * @param {String} query The SQL query to execute
60109 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
60110 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
60111 * saved in the current store (defaults to false)
60113 doQuery : function(q, forceAll){
60115 Roo.log('doQuery?');
60116 if(q === undefined || q === null){
60121 forceAll: forceAll,
60125 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
60129 forceAll = qe.forceAll;
60130 if(forceAll === true || (q.length >= this.minChars)){
60131 if(this.lastQuery != q || this.alwaysQuery){
60132 this.lastQuery = q;
60133 if(this.mode == 'local'){
60134 this.selectedIndex = -1;
60136 this.store.clearFilter();
60138 this.store.filter(this.displayField, q);
60142 this.store.baseParams[this.queryParam] = q;
60144 params: this.getParams(q)
60149 this.selectedIndex = -1;
60156 getParams : function(q){
60158 //p[this.queryParam] = q;
60161 p.limit = this.pageSize;
60167 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
60169 collapse : function(){
60174 collapseIf : function(e){
60179 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
60181 expand : function(){
60189 * @cfg {Boolean} grow
60193 * @cfg {Number} growMin
60197 * @cfg {Number} growMax
60205 setWidth : function()
60209 getResizeEl : function(){
60212 });//<script type="text/javasscript">
60216 * @class Roo.DDView
60217 * A DnD enabled version of Roo.View.
60218 * @param {Element/String} container The Element in which to create the View.
60219 * @param {String} tpl The template string used to create the markup for each element of the View
60220 * @param {Object} config The configuration properties. These include all the config options of
60221 * {@link Roo.View} plus some specific to this class.<br>
60223 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
60224 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
60226 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
60227 .x-view-drag-insert-above {
60228 border-top:1px dotted #3366cc;
60230 .x-view-drag-insert-below {
60231 border-bottom:1px dotted #3366cc;
60237 Roo.DDView = function(container, tpl, config) {
60238 Roo.DDView.superclass.constructor.apply(this, arguments);
60239 this.getEl().setStyle("outline", "0px none");
60240 this.getEl().unselectable();
60241 if (this.dragGroup) {
60242 this.setDraggable(this.dragGroup.split(","));
60244 if (this.dropGroup) {
60245 this.setDroppable(this.dropGroup.split(","));
60247 if (this.deletable) {
60248 this.setDeletable();
60250 this.isDirtyFlag = false;
60256 Roo.extend(Roo.DDView, Roo.View, {
60257 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
60258 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
60259 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
60260 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
60264 reset: Roo.emptyFn,
60266 clearInvalid: Roo.form.Field.prototype.clearInvalid,
60268 validate: function() {
60272 destroy: function() {
60273 this.purgeListeners();
60274 this.getEl.removeAllListeners();
60275 this.getEl().remove();
60276 if (this.dragZone) {
60277 if (this.dragZone.destroy) {
60278 this.dragZone.destroy();
60281 if (this.dropZone) {
60282 if (this.dropZone.destroy) {
60283 this.dropZone.destroy();
60288 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
60289 getName: function() {
60293 /** Loads the View from a JSON string representing the Records to put into the Store. */
60294 setValue: function(v) {
60296 throw "DDView.setValue(). DDView must be constructed with a valid Store";
60299 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
60300 this.store.proxy = new Roo.data.MemoryProxy(data);
60304 /** @return {String} a parenthesised list of the ids of the Records in the View. */
60305 getValue: function() {
60307 this.store.each(function(rec) {
60308 result += rec.id + ',';
60310 return result.substr(0, result.length - 1) + ')';
60313 getIds: function() {
60314 var i = 0, result = new Array(this.store.getCount());
60315 this.store.each(function(rec) {
60316 result[i++] = rec.id;
60321 isDirty: function() {
60322 return this.isDirtyFlag;
60326 * Part of the Roo.dd.DropZone interface. If no target node is found, the
60327 * whole Element becomes the target, and this causes the drop gesture to append.
60329 getTargetFromEvent : function(e) {
60330 var target = e.getTarget();
60331 while ((target !== null) && (target.parentNode != this.el.dom)) {
60332 target = target.parentNode;
60335 target = this.el.dom.lastChild || this.el.dom;
60341 * Create the drag data which consists of an object which has the property "ddel" as
60342 * the drag proxy element.
60344 getDragData : function(e) {
60345 var target = this.findItemFromChild(e.getTarget());
60347 this.handleSelection(e);
60348 var selNodes = this.getSelectedNodes();
60351 copy: this.copy || (this.allowCopy && e.ctrlKey),
60355 var selectedIndices = this.getSelectedIndexes();
60356 for (var i = 0; i < selectedIndices.length; i++) {
60357 dragData.records.push(this.store.getAt(selectedIndices[i]));
60359 if (selNodes.length == 1) {
60360 dragData.ddel = target.cloneNode(true); // the div element
60362 var div = document.createElement('div'); // create the multi element drag "ghost"
60363 div.className = 'multi-proxy';
60364 for (var i = 0, len = selNodes.length; i < len; i++) {
60365 div.appendChild(selNodes[i].cloneNode(true));
60367 dragData.ddel = div;
60369 //console.log(dragData)
60370 //console.log(dragData.ddel.innerHTML)
60373 //console.log('nodragData')
60377 /** Specify to which ddGroup items in this DDView may be dragged. */
60378 setDraggable: function(ddGroup) {
60379 if (ddGroup instanceof Array) {
60380 Roo.each(ddGroup, this.setDraggable, this);
60383 if (this.dragZone) {
60384 this.dragZone.addToGroup(ddGroup);
60386 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
60387 containerScroll: true,
60391 // Draggability implies selection. DragZone's mousedown selects the element.
60392 if (!this.multiSelect) { this.singleSelect = true; }
60394 // Wire the DragZone's handlers up to methods in *this*
60395 this.dragZone.getDragData = this.getDragData.createDelegate(this);
60399 /** Specify from which ddGroup this DDView accepts drops. */
60400 setDroppable: function(ddGroup) {
60401 if (ddGroup instanceof Array) {
60402 Roo.each(ddGroup, this.setDroppable, this);
60405 if (this.dropZone) {
60406 this.dropZone.addToGroup(ddGroup);
60408 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
60409 containerScroll: true,
60413 // Wire the DropZone's handlers up to methods in *this*
60414 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
60415 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
60416 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
60417 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
60418 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
60422 /** Decide whether to drop above or below a View node. */
60423 getDropPoint : function(e, n, dd){
60424 if (n == this.el.dom) { return "above"; }
60425 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
60426 var c = t + (b - t) / 2;
60427 var y = Roo.lib.Event.getPageY(e);
60435 onNodeEnter : function(n, dd, e, data){
60439 onNodeOver : function(n, dd, e, data){
60440 var pt = this.getDropPoint(e, n, dd);
60441 // set the insert point style on the target node
60442 var dragElClass = this.dropNotAllowed;
60445 if (pt == "above"){
60446 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
60447 targetElClass = "x-view-drag-insert-above";
60449 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
60450 targetElClass = "x-view-drag-insert-below";
60452 if (this.lastInsertClass != targetElClass){
60453 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
60454 this.lastInsertClass = targetElClass;
60457 return dragElClass;
60460 onNodeOut : function(n, dd, e, data){
60461 this.removeDropIndicators(n);
60464 onNodeDrop : function(n, dd, e, data){
60465 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
60468 var pt = this.getDropPoint(e, n, dd);
60469 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
60470 if (pt == "below") { insertAt++; }
60471 for (var i = 0; i < data.records.length; i++) {
60472 var r = data.records[i];
60473 var dup = this.store.getById(r.id);
60474 if (dup && (dd != this.dragZone)) {
60475 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
60478 this.store.insert(insertAt++, r.copy());
60480 data.source.isDirtyFlag = true;
60482 this.store.insert(insertAt++, r);
60484 this.isDirtyFlag = true;
60487 this.dragZone.cachedTarget = null;
60491 removeDropIndicators : function(n){
60493 Roo.fly(n).removeClass([
60494 "x-view-drag-insert-above",
60495 "x-view-drag-insert-below"]);
60496 this.lastInsertClass = "_noclass";
60501 * Utility method. Add a delete option to the DDView's context menu.
60502 * @param {String} imageUrl The URL of the "delete" icon image.
60504 setDeletable: function(imageUrl) {
60505 if (!this.singleSelect && !this.multiSelect) {
60506 this.singleSelect = true;
60508 var c = this.getContextMenu();
60509 this.contextMenu.on("itemclick", function(item) {
60512 this.remove(this.getSelectedIndexes());
60516 this.contextMenu.add({
60523 /** Return the context menu for this DDView. */
60524 getContextMenu: function() {
60525 if (!this.contextMenu) {
60526 // Create the View's context menu
60527 this.contextMenu = new Roo.menu.Menu({
60528 id: this.id + "-contextmenu"
60530 this.el.on("contextmenu", this.showContextMenu, this);
60532 return this.contextMenu;
60535 disableContextMenu: function() {
60536 if (this.contextMenu) {
60537 this.el.un("contextmenu", this.showContextMenu, this);
60541 showContextMenu: function(e, item) {
60542 item = this.findItemFromChild(e.getTarget());
60545 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
60546 this.contextMenu.showAt(e.getXY());
60551 * Remove {@link Roo.data.Record}s at the specified indices.
60552 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
60554 remove: function(selectedIndices) {
60555 selectedIndices = [].concat(selectedIndices);
60556 for (var i = 0; i < selectedIndices.length; i++) {
60557 var rec = this.store.getAt(selectedIndices[i]);
60558 this.store.remove(rec);
60563 * Double click fires the event, but also, if this is draggable, and there is only one other
60564 * related DropZone, it transfers the selected node.
60566 onDblClick : function(e){
60567 var item = this.findItemFromChild(e.getTarget());
60569 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
60572 if (this.dragGroup) {
60573 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
60574 while (targets.indexOf(this.dropZone) > -1) {
60575 targets.remove(this.dropZone);
60577 if (targets.length == 1) {
60578 this.dragZone.cachedTarget = null;
60579 var el = Roo.get(targets[0].getEl());
60580 var box = el.getBox(true);
60581 targets[0].onNodeDrop(el.dom, {
60583 xy: [box.x, box.y + box.height - 1]
60584 }, null, this.getDragData(e));
60590 handleSelection: function(e) {
60591 this.dragZone.cachedTarget = null;
60592 var item = this.findItemFromChild(e.getTarget());
60594 this.clearSelections(true);
60597 if (item && (this.multiSelect || this.singleSelect)){
60598 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
60599 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
60600 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
60601 this.unselect(item);
60603 this.select(item, this.multiSelect && e.ctrlKey);
60604 this.lastSelection = item;
60609 onItemClick : function(item, index, e){
60610 if(this.fireEvent("beforeclick", this, index, item, e) === false){
60616 unselect : function(nodeInfo, suppressEvent){
60617 var node = this.getNode(nodeInfo);
60618 if(node && this.isSelected(node)){
60619 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
60620 Roo.fly(node).removeClass(this.selectedClass);
60621 this.selections.remove(node);
60622 if(!suppressEvent){
60623 this.fireEvent("selectionchange", this, this.selections);
60631 * Ext JS Library 1.1.1
60632 * Copyright(c) 2006-2007, Ext JS, LLC.
60634 * Originally Released Under LGPL - original licence link has changed is not relivant.
60637 * <script type="text/javascript">
60641 * @class Roo.layout.Manager
60642 * @extends Roo.util.Observable
60643 * Base class for layout managers.
60645 Roo.layout.Manager = function(container, config){
60646 Roo.layout.Manager.superclass.constructor.call(this);
60647 this.el = Roo.get(container);
60648 // ie scrollbar fix
60649 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
60650 document.body.scroll = "no";
60651 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
60652 this.el.position('relative');
60654 this.id = this.el.id;
60655 this.el.addClass("x-layout-container");
60656 /** false to disable window resize monitoring @type Boolean */
60657 this.monitorWindowResize = true;
60662 * Fires when a layout is performed.
60663 * @param {Roo.layout.Manager} this
60667 * @event regionresized
60668 * Fires when the user resizes a region.
60669 * @param {Roo.layout.Region} region The resized region
60670 * @param {Number} newSize The new size (width for east/west, height for north/south)
60672 "regionresized" : true,
60674 * @event regioncollapsed
60675 * Fires when a region is collapsed.
60676 * @param {Roo.layout.Region} region The collapsed region
60678 "regioncollapsed" : true,
60680 * @event regionexpanded
60681 * Fires when a region is expanded.
60682 * @param {Roo.layout.Region} region The expanded region
60684 "regionexpanded" : true
60686 this.updating = false;
60687 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60690 Roo.extend(Roo.layout.Manager, Roo.util.Observable, {
60692 * Returns true if this layout is currently being updated
60693 * @return {Boolean}
60695 isUpdating : function(){
60696 return this.updating;
60700 * Suspend the LayoutManager from doing auto-layouts while
60701 * making multiple add or remove calls
60703 beginUpdate : function(){
60704 this.updating = true;
60708 * Restore auto-layouts and optionally disable the manager from performing a layout
60709 * @param {Boolean} noLayout true to disable a layout update
60711 endUpdate : function(noLayout){
60712 this.updating = false;
60718 layout: function(){
60722 onRegionResized : function(region, newSize){
60723 this.fireEvent("regionresized", region, newSize);
60727 onRegionCollapsed : function(region){
60728 this.fireEvent("regioncollapsed", region);
60731 onRegionExpanded : function(region){
60732 this.fireEvent("regionexpanded", region);
60736 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
60737 * performs box-model adjustments.
60738 * @return {Object} The size as an object {width: (the width), height: (the height)}
60740 getViewSize : function(){
60742 if(this.el.dom != document.body){
60743 size = this.el.getSize();
60745 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
60747 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
60748 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
60753 * Returns the Element this layout is bound to.
60754 * @return {Roo.Element}
60756 getEl : function(){
60761 * Returns the specified region.
60762 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
60763 * @return {Roo.layout.Region}
60765 getRegion : function(target){
60766 return this.regions[target.toLowerCase()];
60769 onWindowResize : function(){
60770 if(this.monitorWindowResize){
60776 * Ext JS Library 1.1.1
60777 * Copyright(c) 2006-2007, Ext JS, LLC.
60779 * Originally Released Under LGPL - original licence link has changed is not relivant.
60782 * <script type="text/javascript">
60785 * @class Roo.layout.Border
60786 * @extends Roo.layout.Manager
60787 * @children Roo.panel.Content
60788 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
60789 * please see: <br><br>
60790 * <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>
60791 * <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>
60794 var layout = new Roo.layout.Border(document.body, {
60828 preferredTabWidth: 150
60833 var CP = Roo.panel.Content;
60835 layout.beginUpdate();
60836 layout.add("north", new CP("north", "North"));
60837 layout.add("south", new CP("south", {title: "South", closable: true}));
60838 layout.add("west", new CP("west", {title: "West"}));
60839 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
60840 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
60841 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
60842 layout.getRegion("center").showPanel("center1");
60843 layout.endUpdate();
60846 <b>The container the layout is rendered into can be either the body element or any other element.
60847 If it is not the body element, the container needs to either be an absolute positioned element,
60848 or you will need to add "position:relative" to the css of the container. You will also need to specify
60849 the container size if it is not the body element.</b>
60852 * Create a new BorderLayout
60853 * @param {String/HTMLElement/Element} container The container this layout is bound to
60854 * @param {Object} config Configuration options
60856 Roo.layout.Border = function(container, config){
60857 config = config || {};
60858 Roo.layout.Border.superclass.constructor.call(this, container, config);
60859 this.factory = config.factory || Roo.layout.Border.RegionFactory;
60860 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
60861 var target = this.factory.validRegions[i];
60862 if(config[target]){
60863 this.addRegion(target, config[target]);
60868 Roo.extend(Roo.layout.Border, Roo.layout.Manager, {
60871 * @cfg {Roo.layout.Region} east
60874 * @cfg {Roo.layout.Region} west
60877 * @cfg {Roo.layout.Region} north
60880 * @cfg {Roo.layout.Region} south
60883 * @cfg {Roo.layout.Region} center
60886 * Creates and adds a new region if it doesn't already exist.
60887 * @param {String} target The target region key (north, south, east, west or center).
60888 * @param {Object} config The regions config object
60889 * @return {BorderLayoutRegion} The new region
60891 addRegion : function(target, config){
60892 if(!this.regions[target]){
60893 var r = this.factory.create(target, this, config);
60894 this.bindRegion(target, r);
60896 return this.regions[target];
60900 bindRegion : function(name, r){
60901 this.regions[name] = r;
60902 r.on("visibilitychange", this.layout, this);
60903 r.on("paneladded", this.layout, this);
60904 r.on("panelremoved", this.layout, this);
60905 r.on("invalidated", this.layout, this);
60906 r.on("resized", this.onRegionResized, this);
60907 r.on("collapsed", this.onRegionCollapsed, this);
60908 r.on("expanded", this.onRegionExpanded, this);
60912 * Performs a layout update.
60914 layout : function(){
60915 if(this.updating) {
60918 var size = this.getViewSize();
60919 var w = size.width;
60920 var h = size.height;
60925 //var x = 0, y = 0;
60927 var rs = this.regions;
60928 var north = rs["north"];
60929 var south = rs["south"];
60930 var west = rs["west"];
60931 var east = rs["east"];
60932 var center = rs["center"];
60933 //if(this.hideOnLayout){ // not supported anymore
60934 //c.el.setStyle("display", "none");
60936 if(north && north.isVisible()){
60937 var b = north.getBox();
60938 var m = north.getMargins();
60939 b.width = w - (m.left+m.right);
60942 centerY = b.height + b.y + m.bottom;
60943 centerH -= centerY;
60944 north.updateBox(this.safeBox(b));
60946 if(south && south.isVisible()){
60947 var b = south.getBox();
60948 var m = south.getMargins();
60949 b.width = w - (m.left+m.right);
60951 var totalHeight = (b.height + m.top + m.bottom);
60952 b.y = h - totalHeight + m.top;
60953 centerH -= totalHeight;
60954 south.updateBox(this.safeBox(b));
60956 if(west && west.isVisible()){
60957 var b = west.getBox();
60958 var m = west.getMargins();
60959 b.height = centerH - (m.top+m.bottom);
60961 b.y = centerY + m.top;
60962 var totalWidth = (b.width + m.left + m.right);
60963 centerX += totalWidth;
60964 centerW -= totalWidth;
60965 west.updateBox(this.safeBox(b));
60967 if(east && east.isVisible()){
60968 var b = east.getBox();
60969 var m = east.getMargins();
60970 b.height = centerH - (m.top+m.bottom);
60971 var totalWidth = (b.width + m.left + m.right);
60972 b.x = w - totalWidth + m.left;
60973 b.y = centerY + m.top;
60974 centerW -= totalWidth;
60975 east.updateBox(this.safeBox(b));
60978 var m = center.getMargins();
60980 x: centerX + m.left,
60981 y: centerY + m.top,
60982 width: centerW - (m.left+m.right),
60983 height: centerH - (m.top+m.bottom)
60985 //if(this.hideOnLayout){
60986 //center.el.setStyle("display", "block");
60988 center.updateBox(this.safeBox(centerBox));
60991 this.fireEvent("layout", this);
60995 safeBox : function(box){
60996 box.width = Math.max(0, box.width);
60997 box.height = Math.max(0, box.height);
61002 * Adds a ContentPanel (or subclass) to this layout.
61003 * @param {String} target The target region key (north, south, east, west or center).
61004 * @param {Roo.panel.Content} panel The panel to add
61005 * @return {Roo.panel.Content} The added panel
61007 add : function(target, panel){
61009 target = target.toLowerCase();
61010 return this.regions[target].add(panel);
61014 * Remove a ContentPanel (or subclass) to this layout.
61015 * @param {String} target The target region key (north, south, east, west or center).
61016 * @param {Number/String/Roo.panel.Content} panel The index, id or panel to remove
61017 * @return {Roo.panel.Content} The removed panel
61019 remove : function(target, panel){
61020 target = target.toLowerCase();
61021 return this.regions[target].remove(panel);
61025 * Searches all regions for a panel with the specified id
61026 * @param {String} panelId
61027 * @return {Roo.panel.Content} The panel or null if it wasn't found
61029 findPanel : function(panelId){
61030 var rs = this.regions;
61031 for(var target in rs){
61032 if(typeof rs[target] != "function"){
61033 var p = rs[target].getPanel(panelId);
61043 * Searches all regions for a panel with the specified id and activates (shows) it.
61044 * @param {String/panel.Content} panelId The panels id or the panel itself
61045 * @return {Roo.panel.Content} The shown panel or null
61047 showPanel : function(panelId) {
61048 var rs = this.regions;
61049 for(var target in rs){
61050 var r = rs[target];
61051 if(typeof r != "function"){
61052 if(r.hasPanel(panelId)){
61053 return r.showPanel(panelId);
61061 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
61062 * @param {Roo.state.Provider} provider (optional) An alternate state provider
61064 restoreState : function(provider){
61066 provider = Roo.state.Manager;
61068 var sm = new Roo.layout.StateManager();
61069 sm.init(this, provider);
61073 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
61074 * object should contain properties for each region to add ContentPanels to, and each property's value should be
61075 * a valid ContentPanel config object. Example:
61077 // Create the main layout
61078 var layout = new Roo.layout.Border('main-ct', {
61089 // Create and add multiple ContentPanels at once via configs
61092 id: 'source-files',
61094 title:'Ext Source Files',
61107 * @param {Object} regions An object containing ContentPanel configs by region name
61109 batchAdd : function(regions){
61110 this.beginUpdate();
61111 for(var rname in regions){
61112 var lr = this.regions[rname];
61114 this.addTypedPanels(lr, regions[rname]);
61121 addTypedPanels : function(lr, ps){
61122 if(typeof ps == 'string'){
61123 lr.add(new Roo.panel.Content(ps));
61125 else if(ps instanceof Array){
61126 for(var i =0, len = ps.length; i < len; i++){
61127 this.addTypedPanels(lr, ps[i]);
61130 else if(!ps.events){ // raw config?
61132 delete ps.el; // prevent conflict
61133 lr.add(new Roo.panel.Content(el || Roo.id(), ps));
61135 else { // panel object assumed!
61140 * Adds a xtype elements to the layout.
61144 xtype : 'ContentPanel',
61151 xtype : 'NestedLayoutPanel',
61157 items : [ ... list of content panels or nested layout panels.. ]
61161 * @param {Object} cfg Xtype definition of item to add.
61163 addxtype : function(cfg)
61165 // basically accepts a pannel...
61166 // can accept a layout region..!?!?
61167 //Roo.log('Roo.layout.Border add ' + cfg.xtype)
61169 // if (!cfg.xtype.match(/Panel$/)) {
61174 if (typeof(cfg.region) == 'undefined') {
61175 Roo.log("Failed to add Panel, region was not set");
61179 var region = cfg.region;
61185 xitems = cfg.items;
61193 if(cfg.autoCreate) {
61194 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61196 var el = this.el.createChild();
61197 ret = new Roo.panel[cfg.xtype](el, cfg); // new panel!!!!!
61200 this.add(region, ret);
61203 // needs grid and region
61205 //var el = this.getRegion(region).el.createChild();
61206 var el = this.el.createChild();
61207 // create the grid first...
61209 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
61211 if (region == 'center' && this.active ) {
61212 cfg.background = false;
61214 ret = new Roo.panel[cfg.xtype](grid, cfg); // new panel!!!!!
61216 this.add(region, ret);
61217 if (cfg.background) {
61218 ret.on('activate', function(gp) {
61219 if (!gp.grid.rendered) {
61227 case 'NestedLayout':
61228 // create a new Layout (which is a Border Layout...
61229 var el = this.el.createChild();
61230 var clayout = cfg.layout;
61232 clayout.items = clayout.items || [];
61233 // replace this exitems with the clayout ones..
61234 xitems = clayout.items;
61237 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
61238 cfg.background = false;
61240 var layout = new Roo.layout.Border(el, clayout);
61242 ret = new Roo.panel[cfg.xtype](layout, cfg); // new panel!!!!!
61243 //console.log('adding nested layout panel ' + cfg.toSource());
61244 this.add(region, ret);
61245 nb = {}; /// find first...
61249 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61250 this.add(region, ret);
61252 case 'Tree': // our new panel!
61253 cfg.el = this.el.createChild();
61254 ret = new Roo.panel[cfg.xtype](cfg); // new panel!!!!!
61255 this.add(region, ret);
61257 case 'ContentPanel':
61258 case 'ScrollPanel': // ContentPanel (el, cfg)
61260 if(cfg.autoCreate) {
61261 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61263 var el = this.el.createChild();
61264 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
61267 this.add(region, ret);
61271 case 'TreePanel': // our new panel!
61272 cfg.el = this.el.createChild();
61273 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61274 this.add(region, ret);
61277 case 'NestedLayoutPanel':
61278 // create a new Layout (which is a Border Layout...
61279 var el = this.el.createChild();
61280 var clayout = cfg.layout;
61282 clayout.items = clayout.items || [];
61283 // replace this exitems with the clayout ones..
61284 xitems = clayout.items;
61287 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
61288 cfg.background = false;
61290 var layout = new Roo.layout.Border(el, clayout);
61292 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
61293 //console.log('adding nested layout panel ' + cfg.toSource());
61294 this.add(region, ret);
61295 nb = {}; /// find first...
61300 // needs grid and region
61302 //var el = this.getRegion(region).el.createChild();
61303 var el = this.el.createChild();
61304 // create the grid first...
61306 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
61308 if (region == 'center' && this.active ) {
61309 cfg.background = false;
61311 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
61313 this.add(region, ret);
61314 if (cfg.background) {
61315 ret.on('activate', function(gp) {
61316 if (!gp.grid.rendered) {
61331 if (typeof(Roo[cfg.xtype]) != 'undefined') {
61333 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
61334 this.add(region, ret);
61337 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
61341 // GridPanel (grid, cfg)
61344 this.beginUpdate();
61348 Roo.each(xitems, function(i) {
61349 region = nb && i.region ? i.region : false;
61351 var add = ret.addxtype(i);
61354 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
61355 if (!i.background) {
61356 abn[region] = nb[region] ;
61363 // make the last non-background panel active..
61364 //if (nb) { Roo.log(abn); }
61367 for(var r in abn) {
61368 region = this.getRegion(r);
61370 // tried using nb[r], but it does not work..
61372 region.showPanel(abn[r]);
61383 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
61384 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
61385 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
61386 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
61389 var CP = Roo.ContentPanel;
61391 var layout = Roo.layout.Border.create({
61395 panels: [new CP("north", "North")]
61404 panels: [new CP("west", {title: "West"})]
61413 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
61422 panels: [new CP("south", {title: "South", closable: true})]
61429 preferredTabWidth: 150,
61431 new CP("center1", {title: "Close Me", closable: true}),
61432 new CP("center2", {title: "Center Panel", closable: false})
61437 layout.getRegion("center").showPanel("center1");
61442 Roo.layout.Border.create = function(config, targetEl){
61443 var layout = new Roo.layout.Border(targetEl || document.body, config);
61444 layout.beginUpdate();
61445 var regions = Roo.layout.Border.RegionFactory.validRegions;
61446 for(var j = 0, jlen = regions.length; j < jlen; j++){
61447 var lr = regions[j];
61448 if(layout.regions[lr] && config[lr].panels){
61449 var r = layout.regions[lr];
61450 var ps = config[lr].panels;
61451 layout.addTypedPanels(r, ps);
61454 layout.endUpdate();
61459 Roo.layout.Border.RegionFactory = {
61461 validRegions : ["north","south","east","west","center"],
61464 create : function(target, mgr, config){
61465 target = target.toLowerCase();
61466 if(config.lightweight || config.basic){
61467 return new Roo.layout.BasicRegion(mgr, config, target);
61469 var cn = target.charAt(0).toUpperCase() + target.slice(1);
61470 if (typeof (Roo.layout[cn]) == 'undefined') {
61471 throw 'Layout region "'+target+'" not supported.';
61473 return new Roo.layout[cn](mgr, config);
61479 * Ext JS Library 1.1.1
61480 * Copyright(c) 2006-2007, Ext JS, LLC.
61482 * Originally Released Under LGPL - original licence link has changed is not relivant.
61485 * <script type="text/javascript">
61489 * @class Roo.layout.BasicRegion
61490 * @extends Roo.util.Observable
61491 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
61492 * and does not have a titlebar, tabs or any other features. All it does is size and position
61493 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
61495 Roo.layout.BasicRegion= function(mgr, config, pos, skipConfig){
61497 this.position = pos;
61500 * @scope Roo.layout.BasicRegion
61504 * @event beforeremove
61505 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
61506 * @param {Roo.layout.Region} this
61507 * @param {Roo.panel.Content} panel The panel
61508 * @param {Object} e The cancel event object
61510 "beforeremove" : true,
61512 * @event invalidated
61513 * Fires when the layout for this region is changed.
61514 * @param {Roo.layout.Region} this
61516 "invalidated" : true,
61518 * @event visibilitychange
61519 * Fires when this region is shown or hidden
61520 * @param {Roo.layout.Region} this
61521 * @param {Boolean} visibility true or false
61523 "visibilitychange" : true,
61525 * @event paneladded
61526 * Fires when a panel is added.
61527 * @param {Roo.layout.Region} this
61528 * @param {Roo.panel.Content} panel The panel
61530 "paneladded" : true,
61532 * @event panelremoved
61533 * Fires when a panel is removed.
61534 * @param {Roo.layout.Region} this
61535 * @param {Roo.panel.Content} panel The panel
61537 "panelremoved" : true,
61539 * @event beforecollapse
61540 * Fires when this region before collapse.
61541 * @param {Roo.layout.Region} this
61543 "beforecollapse" : true,
61546 * Fires when this region is collapsed.
61547 * @param {Roo.layout.Region} this
61549 "collapsed" : true,
61552 * Fires when this region is expanded.
61553 * @param {Roo.layout.Region} this
61558 * Fires when this region is slid into view.
61559 * @param {Roo.layout.Region} this
61561 "slideshow" : true,
61564 * Fires when this region slides out of view.
61565 * @param {Roo.layout.Region} this
61567 "slidehide" : true,
61569 * @event panelactivated
61570 * Fires when a panel is activated.
61571 * @param {Roo.layout.Region} this
61572 * @param {Roo.panel.Content} panel The activated panel
61574 "panelactivated" : true,
61577 * Fires when the user resizes this region.
61578 * @param {Roo.layout.Region} this
61579 * @param {Number} newSize The new size (width for east/west, height for north/south)
61583 /** A collection of panels in this region. @type Roo.util.MixedCollection */
61584 this.panels = new Roo.util.MixedCollection();
61585 this.panels.getKey = this.getPanelId.createDelegate(this);
61587 this.activePanel = null;
61588 // ensure listeners are added...
61590 if (config.listeners || config.events) {
61591 Roo.layout.BasicRegion.superclass.constructor.call(this, {
61592 listeners : config.listeners || {},
61593 events : config.events || {}
61597 if(skipConfig !== true){
61598 this.applyConfig(config);
61602 Roo.extend(Roo.layout.BasicRegion, Roo.util.Observable, {
61603 getPanelId : function(p){
61607 applyConfig : function(config){
61608 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
61609 this.config = config;
61614 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
61615 * the width, for horizontal (north, south) the height.
61616 * @param {Number} newSize The new width or height
61618 resizeTo : function(newSize){
61619 var el = this.el ? this.el :
61620 (this.activePanel ? this.activePanel.getEl() : null);
61622 switch(this.position){
61625 el.setWidth(newSize);
61626 this.fireEvent("resized", this, newSize);
61630 el.setHeight(newSize);
61631 this.fireEvent("resized", this, newSize);
61637 getBox : function(){
61638 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
61641 getMargins : function(){
61642 return this.margins;
61645 updateBox : function(box){
61647 var el = this.activePanel.getEl();
61648 el.dom.style.left = box.x + "px";
61649 el.dom.style.top = box.y + "px";
61650 this.activePanel.setSize(box.width, box.height);
61654 * Returns the container element for this region.
61655 * @return {Roo.Element}
61657 getEl : function(){
61658 return this.activePanel;
61662 * Returns true if this region is currently visible.
61663 * @return {Boolean}
61665 isVisible : function(){
61666 return this.activePanel ? true : false;
61669 setActivePanel : function(panel){
61670 panel = this.getPanel(panel);
61671 if(this.activePanel && this.activePanel != panel){
61672 this.activePanel.setActiveState(false);
61673 this.activePanel.getEl().setLeftTop(-10000,-10000);
61675 this.activePanel = panel;
61676 panel.setActiveState(true);
61678 panel.setSize(this.box.width, this.box.height);
61680 this.fireEvent("panelactivated", this, panel);
61681 this.fireEvent("invalidated");
61685 * Show the specified panel.
61686 * @param {Number/String/panel.Content} panelId The panels index, id or the panel itself
61687 * @return {Roo.panel.Content} The shown panel or null
61689 showPanel : function(panel){
61690 if(panel = this.getPanel(panel)){
61691 this.setActivePanel(panel);
61697 * Get the active panel for this region.
61698 * @return {Roo.panel.Content} The active panel or null
61700 getActivePanel : function(){
61701 return this.activePanel;
61705 * Add the passed ContentPanel(s)
61706 * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
61707 * @return {Roo.panel.Content} The panel added (if only one was added)
61709 add : function(panel){
61710 if(arguments.length > 1){
61711 for(var i = 0, len = arguments.length; i < len; i++) {
61712 this.add(arguments[i]);
61716 if(this.hasPanel(panel)){
61717 this.showPanel(panel);
61720 var el = panel.getEl();
61721 if(el.dom.parentNode != this.mgr.el.dom){
61722 this.mgr.el.dom.appendChild(el.dom);
61724 if(panel.setRegion){
61725 panel.setRegion(this);
61727 this.panels.add(panel);
61728 el.setStyle("position", "absolute");
61729 if(!panel.background){
61730 this.setActivePanel(panel);
61731 if(this.config.initialSize && this.panels.getCount()==1){
61732 this.resizeTo(this.config.initialSize);
61735 this.fireEvent("paneladded", this, panel);
61740 * Returns true if the panel is in this region.
61741 * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61742 * @return {Boolean}
61744 hasPanel : function(panel){
61745 if(typeof panel == "object"){ // must be panel obj
61746 panel = panel.getId();
61748 return this.getPanel(panel) ? true : false;
61752 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
61753 * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61754 * @param {Boolean} preservePanel Overrides the config preservePanel option
61755 * @return {Roo.panel.Content} The panel that was removed
61757 remove : function(panel, preservePanel){
61758 panel = this.getPanel(panel);
61763 this.fireEvent("beforeremove", this, panel, e);
61764 if(e.cancel === true){
61767 var panelId = panel.getId();
61768 this.panels.removeKey(panelId);
61773 * Returns the panel specified or null if it's not in this region.
61774 * @param {Number/String/panel.Content} panel The panels index, id or the panel itself
61775 * @return {Roo.panel.Content}
61777 getPanel : function(id){
61778 if(typeof id == "object"){ // must be panel obj
61781 return this.panels.get(id);
61785 * Returns this regions position (north/south/east/west/center).
61788 getPosition: function(){
61789 return this.position;
61793 * Ext JS Library 1.1.1
61794 * Copyright(c) 2006-2007, Ext JS, LLC.
61796 * Originally Released Under LGPL - original licence link has changed is not relivant.
61799 * <script type="text/javascript">
61803 * @class Roo.layout.Region
61804 * @extends Roo.layout.BasicRegion
61805 * This class represents a region in a layout manager.
61806 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
61807 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
61808 * @cfg {Boolean} floatable False to disable floating (defaults to true)
61809 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
61810 * @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})
61811 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
61812 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
61813 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
61814 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
61815 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
61816 * @cfg {String} title The title for the region (overrides panel titles)
61817 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
61818 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
61819 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
61820 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
61821 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
61822 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
61823 * the space available, similar to FireFox 1.5 tabs (defaults to false)
61824 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
61825 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
61826 * @cfg {Boolean} showPin True to show a pin button
61827 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
61828 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
61829 * @cfg {Boolean} disableTabTips True to disable tab tooltips
61830 * @cfg {Number} width For East/West panels
61831 * @cfg {Number} height For North/South panels
61832 * @cfg {Boolean} split To show the splitter
61833 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
61835 Roo.layout.Region = function(mgr, config, pos){
61836 Roo.layout.Region.superclass.constructor.call(this, mgr, config, pos, true);
61837 var dh = Roo.DomHelper;
61838 /** This region's container element
61839 * @type Roo.Element */
61840 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
61841 /** This region's title element
61842 * @type Roo.Element */
61844 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
61845 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
61846 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
61848 this.titleEl.enableDisplayMode();
61849 /** This region's title text element
61850 * @type HTMLElement */
61851 this.titleTextEl = this.titleEl.dom.firstChild;
61852 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
61853 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
61854 this.closeBtn.enableDisplayMode();
61855 this.closeBtn.on("click", this.closeClicked, this);
61856 this.closeBtn.hide();
61858 this.createBody(config);
61859 this.visible = true;
61860 this.collapsed = false;
61862 if(config.hideWhenEmpty){
61864 this.on("paneladded", this.validateVisibility, this);
61865 this.on("panelremoved", this.validateVisibility, this);
61867 this.applyConfig(config);
61870 Roo.extend(Roo.layout.Region, Roo.layout.BasicRegion, {
61872 createBody : function(){
61873 /** This region's body element
61874 * @type Roo.Element */
61875 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
61878 applyConfig : function(c){
61879 if(c.collapsible && this.position != "center" && !this.collapsedEl){
61880 var dh = Roo.DomHelper;
61881 if(c.titlebar !== false){
61882 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
61883 this.collapseBtn.on("click", this.collapse, this);
61884 this.collapseBtn.enableDisplayMode();
61886 if(c.showPin === true || this.showPin){
61887 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
61888 this.stickBtn.enableDisplayMode();
61889 this.stickBtn.on("click", this.expand, this);
61890 this.stickBtn.hide();
61893 /** This region's collapsed element
61894 * @type Roo.Element */
61895 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
61896 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
61898 if(c.floatable !== false){
61899 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
61900 this.collapsedEl.on("click", this.collapseClick, this);
61903 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
61904 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
61905 id: "message", unselectable: "on", style:{"float":"left"}});
61906 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
61908 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
61909 this.expandBtn.on("click", this.expand, this);
61911 if(this.collapseBtn){
61912 this.collapseBtn.setVisible(c.collapsible == true);
61914 this.cmargins = c.cmargins || this.cmargins ||
61915 (this.position == "west" || this.position == "east" ?
61916 {top: 0, left: 2, right:2, bottom: 0} :
61917 {top: 2, left: 0, right:0, bottom: 2});
61918 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
61919 this.bottomTabs = c.tabPosition != "top";
61920 this.autoScroll = c.autoScroll || false;
61921 if(this.autoScroll){
61922 this.bodyEl.setStyle("overflow", "auto");
61924 this.bodyEl.setStyle("overflow", "hidden");
61926 //if(c.titlebar !== false){
61927 if((!c.titlebar && !c.title) || c.titlebar === false){
61928 this.titleEl.hide();
61930 this.titleEl.show();
61932 this.titleTextEl.innerHTML = c.title;
61936 this.duration = c.duration || .30;
61937 this.slideDuration = c.slideDuration || .45;
61940 this.collapse(true);
61947 * Returns true if this region is currently visible.
61948 * @return {Boolean}
61950 isVisible : function(){
61951 return this.visible;
61955 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
61956 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
61958 setCollapsedTitle : function(title){
61959 title = title || " ";
61960 if(this.collapsedTitleTextEl){
61961 this.collapsedTitleTextEl.innerHTML = title;
61965 getBox : function(){
61967 if(!this.collapsed){
61968 b = this.el.getBox(false, true);
61970 b = this.collapsedEl.getBox(false, true);
61975 getMargins : function(){
61976 return this.collapsed ? this.cmargins : this.margins;
61979 highlight : function(){
61980 this.el.addClass("x-layout-panel-dragover");
61983 unhighlight : function(){
61984 this.el.removeClass("x-layout-panel-dragover");
61987 updateBox : function(box){
61989 if(!this.collapsed){
61990 this.el.dom.style.left = box.x + "px";
61991 this.el.dom.style.top = box.y + "px";
61992 this.updateBody(box.width, box.height);
61994 this.collapsedEl.dom.style.left = box.x + "px";
61995 this.collapsedEl.dom.style.top = box.y + "px";
61996 this.collapsedEl.setSize(box.width, box.height);
61999 this.tabs.autoSizeTabs();
62003 updateBody : function(w, h){
62005 this.el.setWidth(w);
62006 w -= this.el.getBorderWidth("rl");
62007 if(this.config.adjustments){
62008 w += this.config.adjustments[0];
62012 this.el.setHeight(h);
62013 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
62014 h -= this.el.getBorderWidth("tb");
62015 if(this.config.adjustments){
62016 h += this.config.adjustments[1];
62018 this.bodyEl.setHeight(h);
62020 h = this.tabs.syncHeight(h);
62023 if(this.panelSize){
62024 w = w !== null ? w : this.panelSize.width;
62025 h = h !== null ? h : this.panelSize.height;
62027 if(this.activePanel){
62028 var el = this.activePanel.getEl();
62029 w = w !== null ? w : el.getWidth();
62030 h = h !== null ? h : el.getHeight();
62031 this.panelSize = {width: w, height: h};
62032 this.activePanel.setSize(w, h);
62034 if(Roo.isIE && this.tabs){
62035 this.tabs.el.repaint();
62040 * Returns the container element for this region.
62041 * @return {Roo.Element}
62043 getEl : function(){
62048 * Hides this region.
62051 if(!this.collapsed){
62052 this.el.dom.style.left = "-2000px";
62055 this.collapsedEl.dom.style.left = "-2000px";
62056 this.collapsedEl.hide();
62058 this.visible = false;
62059 this.fireEvent("visibilitychange", this, false);
62063 * Shows this region if it was previously hidden.
62066 if(!this.collapsed){
62069 this.collapsedEl.show();
62071 this.visible = true;
62072 this.fireEvent("visibilitychange", this, true);
62075 closeClicked : function(){
62076 if(this.activePanel){
62077 this.remove(this.activePanel);
62081 collapseClick : function(e){
62083 e.stopPropagation();
62086 e.stopPropagation();
62092 * Collapses this region.
62093 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
62095 collapse : function(skipAnim, skipCheck){
62096 if(this.collapsed) {
62100 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
62102 this.collapsed = true;
62104 this.split.el.hide();
62106 if(this.config.animate && skipAnim !== true){
62107 this.fireEvent("invalidated", this);
62108 this.animateCollapse();
62110 this.el.setLocation(-20000,-20000);
62112 this.collapsedEl.show();
62113 this.fireEvent("collapsed", this);
62114 this.fireEvent("invalidated", this);
62120 animateCollapse : function(){
62125 * Expands this region if it was previously collapsed.
62126 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
62127 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
62129 expand : function(e, skipAnim){
62131 e.stopPropagation();
62133 if(!this.collapsed || this.el.hasActiveFx()) {
62137 this.afterSlideIn();
62140 this.collapsed = false;
62141 if(this.config.animate && skipAnim !== true){
62142 this.animateExpand();
62146 this.split.el.show();
62148 this.collapsedEl.setLocation(-2000,-2000);
62149 this.collapsedEl.hide();
62150 this.fireEvent("invalidated", this);
62151 this.fireEvent("expanded", this);
62155 animateExpand : function(){
62159 initTabs : function()
62161 this.bodyEl.setStyle("overflow", "hidden");
62162 var ts = new Roo.panel.Tab(
62165 tabPosition: this.bottomTabs ? 'bottom' : 'top',
62166 disableTooltips: this.config.disableTabTips,
62167 toolbar : this.config.toolbar
62170 if(this.config.hideTabs){
62171 ts.stripWrap.setDisplayed(false);
62174 ts.resizeTabs = this.config.resizeTabs === true;
62175 ts.minTabWidth = this.config.minTabWidth || 40;
62176 ts.maxTabWidth = this.config.maxTabWidth || 250;
62177 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
62178 ts.monitorResize = false;
62179 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
62180 ts.bodyEl.addClass('x-layout-tabs-body');
62181 this.panels.each(this.initPanelAsTab, this);
62184 initPanelAsTab : function(panel){
62185 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
62186 this.config.closeOnTab && panel.isClosable());
62187 if(panel.tabTip !== undefined){
62188 ti.setTooltip(panel.tabTip);
62190 ti.on("activate", function(){
62191 this.setActivePanel(panel);
62193 if(this.config.closeOnTab){
62194 ti.on("beforeclose", function(t, e){
62196 this.remove(panel);
62202 updatePanelTitle : function(panel, title){
62203 if(this.activePanel == panel){
62204 this.updateTitle(title);
62207 var ti = this.tabs.getTab(panel.getEl().id);
62209 if(panel.tabTip !== undefined){
62210 ti.setTooltip(panel.tabTip);
62215 updateTitle : function(title){
62216 if(this.titleTextEl && !this.config.title){
62217 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
62221 setActivePanel : function(panel){
62222 panel = this.getPanel(panel);
62223 if(this.activePanel && this.activePanel != panel){
62224 this.activePanel.setActiveState(false);
62226 this.activePanel = panel;
62227 panel.setActiveState(true);
62228 if(this.panelSize){
62229 panel.setSize(this.panelSize.width, this.panelSize.height);
62232 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
62234 this.updateTitle(panel.getTitle());
62236 this.fireEvent("invalidated", this);
62238 this.fireEvent("panelactivated", this, panel);
62242 * Shows the specified panel.
62243 * @param {Number/String/panel.Content} panelId The panel's index, id or the panel itself
62244 * @return {Roo.panel.Content} The shown panel, or null if a panel could not be found from panelId
62246 showPanel : function(panel)
62248 panel = this.getPanel(panel);
62251 var tab = this.tabs.getTab(panel.getEl().id);
62252 if(tab.isHidden()){
62253 this.tabs.unhideTab(tab.id);
62257 this.setActivePanel(panel);
62264 * Get the active panel for this region.
62265 * @return {Roo.panel.Content} The active panel or null
62267 getActivePanel : function(){
62268 return this.activePanel;
62271 validateVisibility : function(){
62272 if(this.panels.getCount() < 1){
62273 this.updateTitle(" ");
62274 this.closeBtn.hide();
62277 if(!this.isVisible()){
62284 * Adds the passed ContentPanel(s) to this region.
62285 * @param {panel.Content...} panel The ContentPanel(s) to add (you can pass more than one)
62286 * @return {Roo.panel.Content} The panel added (if only one was added; null otherwise)
62288 add : function(panel){
62289 if(arguments.length > 1){
62290 for(var i = 0, len = arguments.length; i < len; i++) {
62291 this.add(arguments[i]);
62295 if(this.hasPanel(panel)){
62296 this.showPanel(panel);
62299 panel.setRegion(this);
62300 this.panels.add(panel);
62301 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
62302 this.bodyEl.dom.appendChild(panel.getEl().dom);
62303 if(panel.background !== true){
62304 this.setActivePanel(panel);
62306 this.fireEvent("paneladded", this, panel);
62312 this.initPanelAsTab(panel);
62314 if(panel.background !== true){
62315 this.tabs.activate(panel.getEl().id);
62317 this.fireEvent("paneladded", this, panel);
62322 * Hides the tab for the specified panel.
62323 * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62325 hidePanel : function(panel){
62326 if(this.tabs && (panel = this.getPanel(panel))){
62327 this.tabs.hideTab(panel.getEl().id);
62332 * Unhides the tab for a previously hidden panel.
62333 * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62335 unhidePanel : function(panel){
62336 if(this.tabs && (panel = this.getPanel(panel))){
62337 this.tabs.unhideTab(panel.getEl().id);
62341 clearPanels : function(){
62342 while(this.panels.getCount() > 0){
62343 this.remove(this.panels.first());
62348 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
62349 * @param {Number/String/panel.Content} panel The panel's index, id or the panel itself
62350 * @param {Boolean} preservePanel Overrides the config preservePanel option
62351 * @return {Roo.panel.Content} The panel that was removed
62353 remove : function(panel, preservePanel){
62354 panel = this.getPanel(panel);
62359 this.fireEvent("beforeremove", this, panel, e);
62360 if(e.cancel === true){
62363 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
62364 var panelId = panel.getId();
62365 this.panels.removeKey(panelId);
62367 document.body.appendChild(panel.getEl().dom);
62370 this.tabs.removeTab(panel.getEl().id);
62371 }else if (!preservePanel){
62372 this.bodyEl.dom.removeChild(panel.getEl().dom);
62374 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
62375 var p = this.panels.first();
62376 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
62377 tempEl.appendChild(p.getEl().dom);
62378 this.bodyEl.update("");
62379 this.bodyEl.dom.appendChild(p.getEl().dom);
62381 this.updateTitle(p.getTitle());
62383 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
62384 this.setActivePanel(p);
62386 panel.setRegion(null);
62387 if(this.activePanel == panel){
62388 this.activePanel = null;
62390 if(this.config.autoDestroy !== false && preservePanel !== true){
62391 try{panel.destroy();}catch(e){}
62393 this.fireEvent("panelremoved", this, panel);
62398 * Returns the TabPanel component used by this region
62399 * @return {Roo.panel.Tab}
62401 getTabs : function(){
62405 createTool : function(parentEl, className){
62406 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
62407 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
62408 btn.addClassOnOver("x-layout-tools-button-over");
62413 * Ext JS Library 1.1.1
62414 * Copyright(c) 2006-2007, Ext JS, LLC.
62416 * Originally Released Under LGPL - original licence link has changed is not relivant.
62419 * <script type="text/javascript">
62425 * @class Roo.layout.SplitRegion
62426 * @extends Roo.layout.Region
62427 * Adds a splitbar and other (private) useful functionality to a {@link Roo.layout.Region}.
62429 Roo.layout.SplitRegion = function(mgr, config, pos, cursor){
62430 this.cursor = cursor;
62431 Roo.layout.SplitRegion.superclass.constructor.call(this, mgr, config, pos);
62434 Roo.extend(Roo.layout.SplitRegion, Roo.layout.Region, {
62435 splitTip : "Drag to resize.",
62436 collapsibleSplitTip : "Drag to resize. Double click to hide.",
62437 useSplitTips : false,
62439 applyConfig : function(config){
62440 Roo.layout.SplitRegion.superclass.applyConfig.call(this, config);
62443 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
62444 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
62445 /** The SplitBar for this region
62446 * @type Roo.SplitBar */
62447 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
62448 this.split.on("moved", this.onSplitMove, this);
62449 this.split.useShim = config.useShim === true;
62450 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
62451 if(this.useSplitTips){
62452 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
62454 if(config.collapsible){
62455 this.split.el.on("dblclick", this.collapse, this);
62458 if(typeof config.minSize != "undefined"){
62459 this.split.minSize = config.minSize;
62461 if(typeof config.maxSize != "undefined"){
62462 this.split.maxSize = config.maxSize;
62464 if(config.hideWhenEmpty || config.hidden || config.collapsed){
62465 this.hideSplitter();
62470 getHMaxSize : function(){
62471 var cmax = this.config.maxSize || 10000;
62472 var center = this.mgr.getRegion("center");
62473 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
62476 getVMaxSize : function(){
62477 var cmax = this.config.maxSize || 10000;
62478 var center = this.mgr.getRegion("center");
62479 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
62482 onSplitMove : function(split, newSize){
62483 this.fireEvent("resized", this, newSize);
62487 * Returns the {@link Roo.SplitBar} for this region.
62488 * @return {Roo.SplitBar}
62490 getSplitBar : function(){
62495 this.hideSplitter();
62496 Roo.layout.SplitRegion.superclass.hide.call(this);
62499 hideSplitter : function(){
62501 this.split.el.setLocation(-2000,-2000);
62502 this.split.el.hide();
62508 this.split.el.show();
62510 Roo.layout.SplitRegion.superclass.show.call(this);
62513 beforeSlide: function(){
62514 if(Roo.isGecko){// firefox overflow auto bug workaround
62515 this.bodyEl.clip();
62517 this.tabs.bodyEl.clip();
62519 if(this.activePanel){
62520 this.activePanel.getEl().clip();
62522 if(this.activePanel.beforeSlide){
62523 this.activePanel.beforeSlide();
62529 afterSlide : function(){
62530 if(Roo.isGecko){// firefox overflow auto bug workaround
62531 this.bodyEl.unclip();
62533 this.tabs.bodyEl.unclip();
62535 if(this.activePanel){
62536 this.activePanel.getEl().unclip();
62537 if(this.activePanel.afterSlide){
62538 this.activePanel.afterSlide();
62544 initAutoHide : function(){
62545 if(this.autoHide !== false){
62546 if(!this.autoHideHd){
62547 var st = new Roo.util.DelayedTask(this.slideIn, this);
62548 this.autoHideHd = {
62549 "mouseout": function(e){
62550 if(!e.within(this.el, true)){
62554 "mouseover" : function(e){
62560 this.el.on(this.autoHideHd);
62564 clearAutoHide : function(){
62565 if(this.autoHide !== false){
62566 this.el.un("mouseout", this.autoHideHd.mouseout);
62567 this.el.un("mouseover", this.autoHideHd.mouseover);
62571 clearMonitor : function(){
62572 Roo.get(document).un("click", this.slideInIf, this);
62575 // these names are backwards but not changed for compat
62576 slideOut : function(){
62577 if(this.isSlid || this.el.hasActiveFx()){
62580 this.isSlid = true;
62581 if(this.collapseBtn){
62582 this.collapseBtn.hide();
62584 this.closeBtnState = this.closeBtn.getStyle('display');
62585 this.closeBtn.hide();
62587 this.stickBtn.show();
62590 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
62591 this.beforeSlide();
62592 this.el.setStyle("z-index", 10001);
62593 this.el.slideIn(this.getSlideAnchor(), {
62594 callback: function(){
62596 this.initAutoHide();
62597 Roo.get(document).on("click", this.slideInIf, this);
62598 this.fireEvent("slideshow", this);
62605 afterSlideIn : function(){
62606 this.clearAutoHide();
62607 this.isSlid = false;
62608 this.clearMonitor();
62609 this.el.setStyle("z-index", "");
62610 if(this.collapseBtn){
62611 this.collapseBtn.show();
62613 this.closeBtn.setStyle('display', this.closeBtnState);
62615 this.stickBtn.hide();
62617 this.fireEvent("slidehide", this);
62620 slideIn : function(cb){
62621 if(!this.isSlid || this.el.hasActiveFx()){
62625 this.isSlid = false;
62626 this.beforeSlide();
62627 this.el.slideOut(this.getSlideAnchor(), {
62628 callback: function(){
62629 this.el.setLeftTop(-10000, -10000);
62631 this.afterSlideIn();
62639 slideInIf : function(e){
62640 if(!e.within(this.el)){
62645 animateCollapse : function(){
62646 this.beforeSlide();
62647 this.el.setStyle("z-index", 20000);
62648 var anchor = this.getSlideAnchor();
62649 this.el.slideOut(anchor, {
62650 callback : function(){
62651 this.el.setStyle("z-index", "");
62652 this.collapsedEl.slideIn(anchor, {duration:.3});
62654 this.el.setLocation(-10000,-10000);
62656 this.fireEvent("collapsed", this);
62663 animateExpand : function(){
62664 this.beforeSlide();
62665 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
62666 this.el.setStyle("z-index", 20000);
62667 this.collapsedEl.hide({
62670 this.el.slideIn(this.getSlideAnchor(), {
62671 callback : function(){
62672 this.el.setStyle("z-index", "");
62675 this.split.el.show();
62677 this.fireEvent("invalidated", this);
62678 this.fireEvent("expanded", this);
62706 getAnchor : function(){
62707 return this.anchors[this.position];
62710 getCollapseAnchor : function(){
62711 return this.canchors[this.position];
62714 getSlideAnchor : function(){
62715 return this.sanchors[this.position];
62718 getAlignAdj : function(){
62719 var cm = this.cmargins;
62720 switch(this.position){
62736 getExpandAdj : function(){
62737 var c = this.collapsedEl, cm = this.cmargins;
62738 switch(this.position){
62740 return [-(cm.right+c.getWidth()+cm.left), 0];
62743 return [cm.right+c.getWidth()+cm.left, 0];
62746 return [0, -(cm.top+cm.bottom+c.getHeight())];
62749 return [0, cm.top+cm.bottom+c.getHeight()];
62755 * Ext JS Library 1.1.1
62756 * Copyright(c) 2006-2007, Ext JS, LLC.
62758 * Originally Released Under LGPL - original licence link has changed is not relivant.
62761 * <script type="text/javascript">
62764 * These classes are private internal classes
62766 Roo.layout.Center = function(mgr, config){
62767 Roo.layout.Region.call(this, mgr, config, "center");
62768 this.visible = true;
62769 this.minWidth = config.minWidth || 20;
62770 this.minHeight = config.minHeight || 20;
62773 Roo.extend(Roo.layout.Center, Roo.layout.Region, {
62775 // center panel can't be hidden
62779 // center panel can't be hidden
62782 getMinWidth: function(){
62783 return this.minWidth;
62786 getMinHeight: function(){
62787 return this.minHeight;
62790 Roo.layout.West = function(mgr, config){
62791 Roo.layout.SplitRegion.call(this, mgr, config, "west", "w-resize");
62793 this.split.placement = Roo.SplitBar.LEFT;
62794 this.split.orientation = Roo.SplitBar.HORIZONTAL;
62795 this.split.el.addClass("x-layout-split-h");
62797 var size = config.initialSize || config.width;
62798 if(typeof size != "undefined"){
62799 this.el.setWidth(size);
62802 Roo.extend(Roo.layout.West, Roo.layout.SplitRegion, {
62803 orientation: Roo.SplitBar.HORIZONTAL,
62804 getBox : function(){
62805 if(this.collapsed){
62806 return this.collapsedEl.getBox();
62808 var box = this.el.getBox();
62810 box.width += this.split.el.getWidth();
62815 updateBox : function(box){
62816 if(this.split && !this.collapsed){
62817 var sw = this.split.el.getWidth();
62819 this.split.el.setLeft(box.x+box.width);
62820 this.split.el.setTop(box.y);
62821 this.split.el.setHeight(box.height);
62823 if(this.collapsed){
62824 this.updateBody(null, box.height);
62826 Roo.layout.Region.prototype.updateBox.call(this, box);
62829 Roo.layout.East = function(mgr, config){
62830 Roo.layout.SplitRegion.call(this, mgr, config, "east", "e-resize");
62832 this.split.placement = Roo.SplitBar.RIGHT;
62833 this.split.orientation = Roo.SplitBar.HORIZONTAL;
62834 this.split.el.addClass("x-layout-split-h");
62836 var size = config.initialSize || config.width;
62837 if(typeof size != "undefined"){
62838 this.el.setWidth(size);
62841 Roo.extend(Roo.layout.East, Roo.layout.SplitRegion, {
62842 orientation: Roo.SplitBar.HORIZONTAL,
62843 getBox : function(){
62844 if(this.collapsed){
62845 return this.collapsedEl.getBox();
62847 var box = this.el.getBox();
62849 var sw = this.split.el.getWidth();
62856 updateBox : function(box){
62857 if(this.split && !this.collapsed){
62858 var sw = this.split.el.getWidth();
62860 this.split.el.setLeft(box.x);
62861 this.split.el.setTop(box.y);
62862 this.split.el.setHeight(box.height);
62865 if(this.collapsed){
62866 this.updateBody(null, box.height);
62868 Roo.layout.Region.prototype.updateBox.call(this, box);
62870 });Roo.layout.South = function(mgr, config){
62871 Roo.layout.SplitRegion.call(this, mgr, config, "south", "s-resize");
62873 this.split.placement = Roo.SplitBar.BOTTOM;
62874 this.split.orientation = Roo.SplitBar.VERTICAL;
62875 this.split.el.addClass("x-layout-split-v");
62877 var size = config.initialSize || config.height;
62878 if(typeof size != "undefined"){
62879 this.el.setHeight(size);
62882 Roo.extend(Roo.layout.South, Roo.layout.SplitRegion, {
62883 orientation: Roo.SplitBar.VERTICAL,
62884 getBox : function(){
62885 if(this.collapsed){
62886 return this.collapsedEl.getBox();
62888 var box = this.el.getBox();
62890 var sh = this.split.el.getHeight();
62897 updateBox : function(box){
62898 if(this.split && !this.collapsed){
62899 var sh = this.split.el.getHeight();
62902 this.split.el.setLeft(box.x);
62903 this.split.el.setTop(box.y-sh);
62904 this.split.el.setWidth(box.width);
62906 if(this.collapsed){
62907 this.updateBody(box.width, null);
62909 Roo.layout.Region.prototype.updateBox.call(this, box);
62914 Roo.layout.North = function(mgr, config){
62915 Roo.layout.Region.call(this, mgr, config, "north", "n-resize");
62917 this.split.placement = Roo.SplitBar.TOP;
62918 this.split.orientation = Roo.SplitBar.VERTICAL;
62919 this.split.el.addClass("x-layout-split-v");
62921 var size = config.initialSize || config.height;
62922 if(typeof size != "undefined"){
62923 this.el.setHeight(size);
62926 Roo.extend(Roo.layout.North, Roo.layout.SplitRegion, {
62927 orientation: Roo.SplitBar.VERTICAL,
62928 getBox : function(){
62929 if(this.collapsed){
62930 return this.collapsedEl.getBox();
62932 var box = this.el.getBox();
62934 box.height += this.split.el.getHeight();
62939 updateBox : function(box){
62940 if(this.split && !this.collapsed){
62941 box.height -= this.split.el.getHeight();
62942 this.split.el.setLeft(box.x);
62943 this.split.el.setTop(box.y+box.height);
62944 this.split.el.setWidth(box.width);
62946 if(this.collapsed){
62947 this.updateBody(box.width, null);
62949 Roo.layout.Region.prototype.updateBox.call(this, box);
62953 * Ext JS Library 1.1.1
62954 * Copyright(c) 2006-2007, Ext JS, LLC.
62956 * Originally Released Under LGPL - original licence link has changed is not relivant.
62959 * <script type="text/javascript">
62964 * Private internal class for reading and applying state
62966 Roo.layout.StateManager = function(layout){
62967 // default empty state
62976 Roo.layout.StateManager.prototype = {
62977 init : function(layout, provider){
62978 this.provider = provider;
62979 var state = provider.get(layout.id+"-layout-state");
62981 var wasUpdating = layout.isUpdating();
62983 layout.beginUpdate();
62985 for(var key in state){
62986 if(typeof state[key] != "function"){
62987 var rstate = state[key];
62988 var r = layout.getRegion(key);
62991 r.resizeTo(rstate.size);
62993 if(rstate.collapsed == true){
62996 r.expand(null, true);
63002 layout.endUpdate();
63004 this.state = state;
63006 this.layout = layout;
63007 layout.on("regionresized", this.onRegionResized, this);
63008 layout.on("regioncollapsed", this.onRegionCollapsed, this);
63009 layout.on("regionexpanded", this.onRegionExpanded, this);
63012 storeState : function(){
63013 this.provider.set(this.layout.id+"-layout-state", this.state);
63016 onRegionResized : function(region, newSize){
63017 this.state[region.getPosition()].size = newSize;
63021 onRegionCollapsed : function(region){
63022 this.state[region.getPosition()].collapsed = true;
63026 onRegionExpanded : function(region){
63027 this.state[region.getPosition()].collapsed = false;
63032 * Ext JS Library 1.1.1
63033 * Copyright(c) 2006-2007, Ext JS, LLC.
63035 * Originally Released Under LGPL - original licence link has changed is not relivant.
63038 * <script type="text/javascript">
63041 * @class Roo.panel.Content
63042 * @extends Roo.util.Observable
63043 * @children Roo.form.Form Roo.JsonView Roo.View
63044 * @parent Roo.layout.Border Roo.LayoutDialog builder
63045 * A basic Content Panel element.
63046 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
63047 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
63048 * @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
63049 * @cfg {Boolean} closable True if the panel can be closed/removed
63050 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
63051 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
63052 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
63053 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
63054 * @cfg {String} title The title for this panel
63055 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
63056 * @cfg {String} url Calls {@link #setUrl} with this value
63057 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
63058 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
63059 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
63060 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
63061 * @cfg {String} style Extra style to add to the content panel
63062 * @cfg {Roo.menu.Menu} menu popup menu
63065 * Create a new Content Panel.
63066 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
63067 * @param {String/Object} config A string to set only the title or a config object
63068 * @param {String} content (optional) Set the HTML content for this panel
63069 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
63071 Roo.panel.Content = function(el, config, content){
63074 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
63078 if (config && config.parentLayout) {
63079 el = config.parentLayout.el.createChild();
63082 if(el.autoCreate){ // xtype is available if this is called from factory
63086 this.el = Roo.get(el);
63087 if(!this.el && config && config.autoCreate){
63088 if(typeof config.autoCreate == "object"){
63089 if(!config.autoCreate.id){
63090 config.autoCreate.id = config.id||el;
63092 this.el = Roo.DomHelper.append(document.body,
63093 config.autoCreate, true);
63095 this.el = Roo.DomHelper.append(document.body,
63096 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
63101 this.closable = false;
63102 this.loaded = false;
63103 this.active = false;
63104 if(typeof config == "string"){
63105 this.title = config;
63107 Roo.apply(this, config);
63110 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
63111 this.wrapEl = this.el.wrap();
63112 this.toolbar.container = this.el.insertSibling(false, 'before');
63113 this.toolbar = new Roo.Toolbar(this.toolbar);
63116 // xtype created footer. - not sure if will work as we normally have to render first..
63117 if (this.footer && !this.footer.el && this.footer.xtype) {
63118 if (!this.wrapEl) {
63119 this.wrapEl = this.el.wrap();
63122 this.footer.container = this.wrapEl.createChild();
63124 this.footer = Roo.factory(this.footer, Roo);
63129 this.resizeEl = Roo.get(this.resizeEl, true);
63131 this.resizeEl = this.el;
63133 // handle view.xtype
63141 * Fires when this panel is activated.
63142 * @param {Roo.panel.Content} this
63146 * @event deactivate
63147 * Fires when this panel is activated.
63148 * @param {Roo.panel.Content} this
63150 "deactivate" : true,
63154 * Fires when this panel is resized if fitToFrame is true.
63155 * @param {Roo.panel.Content} this
63156 * @param {Number} width The width after any component adjustments
63157 * @param {Number} height The height after any component adjustments
63163 * Fires when this tab is created
63164 * @param {Roo.panel.Content} this
63174 if(this.autoScroll){
63175 this.resizeEl.setStyle("overflow", "auto");
63177 // fix randome scrolling
63178 this.el.on('scroll', function() {
63179 Roo.log('fix random scolling');
63180 this.scrollTo('top',0);
63183 content = content || this.content;
63185 this.setContent(content);
63187 if(config && config.url){
63188 this.setUrl(this.url, this.params, this.loadOnce);
63193 Roo.panel.Content.superclass.constructor.call(this);
63195 if (this.view && typeof(this.view.xtype) != 'undefined') {
63196 this.view.el = this.el.appendChild(document.createElement("div"));
63197 this.view = Roo.factory(this.view);
63198 this.view.render && this.view.render(false, '');
63202 this.fireEvent('render', this);
63205 Roo.extend(Roo.panel.Content, Roo.util.Observable, {
63207 setRegion : function(region){
63208 this.region = region;
63210 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
63212 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
63217 * Returns the toolbar for this Panel if one was configured.
63218 * @return {Roo.Toolbar}
63220 getToolbar : function(){
63221 return this.toolbar;
63224 setActiveState : function(active){
63225 this.active = active;
63227 this.fireEvent("deactivate", this);
63229 this.fireEvent("activate", this);
63233 * Updates this panel's element
63234 * @param {String} content The new content
63235 * @param {Boolean} loadScripts (optional) true to look for and process scripts
63237 setContent : function(content, loadScripts){
63238 this.el.update(content, loadScripts);
63241 ignoreResize : function(w, h){
63242 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
63245 this.lastSize = {width: w, height: h};
63250 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
63251 * @return {Roo.UpdateManager} The UpdateManager
63253 getUpdateManager : function(){
63254 return this.el.getUpdateManager();
63257 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
63258 * @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:
63261 url: "your-url.php",
63262 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
63263 callback: yourFunction,
63264 scope: yourObject, //(optional scope)
63267 text: "Loading...",
63272 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
63273 * 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.
63274 * @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}
63275 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
63276 * @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.
63277 * @return {Roo.panel.Content} this
63280 var um = this.el.getUpdateManager();
63281 um.update.apply(um, arguments);
63287 * 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.
63288 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
63289 * @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)
63290 * @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)
63291 * @return {Roo.UpdateManager} The UpdateManager
63293 setUrl : function(url, params, loadOnce){
63294 if(this.refreshDelegate){
63295 this.removeListener("activate", this.refreshDelegate);
63297 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
63298 this.on("activate", this.refreshDelegate);
63299 return this.el.getUpdateManager();
63302 _handleRefresh : function(url, params, loadOnce){
63303 if(!loadOnce || !this.loaded){
63304 var updater = this.el.getUpdateManager();
63305 updater.update(url, params, this._setLoaded.createDelegate(this));
63309 _setLoaded : function(){
63310 this.loaded = true;
63314 * Returns this panel's id
63317 getId : function(){
63322 * Returns this panel's element - used by regiosn to add.
63323 * @return {Roo.Element}
63325 getEl : function(){
63326 return this.wrapEl || this.el;
63329 adjustForComponents : function(width, height)
63331 //Roo.log('adjustForComponents ');
63332 if(this.resizeEl != this.el){
63333 width -= this.el.getFrameWidth('lr');
63334 height -= this.el.getFrameWidth('tb');
63337 var te = this.toolbar.getEl();
63338 height -= te.getHeight();
63339 te.setWidth(width);
63342 var te = this.footer.getEl();
63343 //Roo.log("footer:" + te.getHeight());
63345 height -= te.getHeight();
63346 te.setWidth(width);
63350 if(this.adjustments){
63351 width += this.adjustments[0];
63352 height += this.adjustments[1];
63354 return {"width": width, "height": height};
63357 setSize : function(width, height){
63358 if(this.fitToFrame && !this.ignoreResize(width, height)){
63359 if(this.fitContainer && this.resizeEl != this.el){
63360 this.el.setSize(width, height);
63362 var size = this.adjustForComponents(width, height);
63363 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
63364 this.fireEvent('resize', this, size.width, size.height);
63369 * Returns this panel's title
63372 getTitle : function(){
63377 * Set this panel's title
63378 * @param {String} title
63380 setTitle : function(title){
63381 this.title = title;
63383 this.region.updatePanelTitle(this, title);
63388 * Returns true is this panel was configured to be closable
63389 * @return {Boolean}
63391 isClosable : function(){
63392 return this.closable;
63395 beforeSlide : function(){
63397 this.resizeEl.clip();
63400 afterSlide : function(){
63402 this.resizeEl.unclip();
63406 * Force a content refresh from the URL specified in the {@link #setUrl} method.
63407 * Will fail silently if the {@link #setUrl} method has not been called.
63408 * This does not activate the panel, just updates its content.
63410 refresh : function(){
63411 if(this.refreshDelegate){
63412 this.loaded = false;
63413 this.refreshDelegate();
63418 * Destroys this panel
63420 destroy : function(){
63421 this.el.removeAllListeners();
63422 var tempEl = document.createElement("span");
63423 tempEl.appendChild(this.el.dom);
63424 tempEl.innerHTML = "";
63430 * form - if the content panel contains a form - this is a reference to it.
63431 * @type {Roo.form.Form}
63435 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
63436 * This contains a reference to it.
63442 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
63452 * @param {Object} cfg Xtype definition of item to add.
63455 addxtype : function(cfg) {
63456 if(cfg.xtype.match(/^Cropbox$/)) {
63458 this.cropbox = new Roo.factory(cfg);
63460 this.cropbox.render(this.el);
63462 return this.cropbox;
63465 if (cfg.xtype.match(/^Form$/)) {
63468 //if (this.footer) {
63469 // el = this.footer.container.insertSibling(false, 'before');
63471 el = this.el.createChild();
63474 this.form = new Roo.form.Form(cfg);
63477 if ( this.form.allItems.length) {
63478 this.form.render(el.dom);
63482 // should only have one of theses..
63483 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
63484 // views.. should not be just added - used named prop 'view''
63486 cfg.el = this.el.appendChild(document.createElement("div"));
63489 var ret = new Roo.factory(cfg);
63491 ret.render && ret.render(false, ''); // render blank..
63511 * @class Roo.panel.Grid
63512 * @extends Roo.panel.Content
63513 * @parent Roo.layout.Border Roo.LayoutDialog builder
63515 * Create a new GridPanel.
63516 * @cfg {Roo.grid.Grid} grid The grid for this panel
63518 Roo.panel.Grid = function(grid, config){
63520 // universal ctor...
63521 if (typeof(grid.grid) != 'undefined') {
63523 grid = config.grid;
63525 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
63526 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
63528 this.wrapper.dom.appendChild(grid.getGridEl().dom);
63530 Roo.panel.Grid.superclass.constructor.call(this, this.wrapper, config);
63533 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
63535 // xtype created footer. - not sure if will work as we normally have to render first..
63536 if (this.footer && !this.footer.el && this.footer.xtype) {
63538 this.footer.container = this.grid.getView().getFooterPanel(true);
63539 this.footer.dataSource = this.grid.dataSource;
63540 this.footer = Roo.factory(this.footer, Roo);
63544 grid.monitorWindowResize = false; // turn off autosizing
63545 grid.autoHeight = false;
63546 grid.autoWidth = false;
63548 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
63551 Roo.extend(Roo.panel.Grid, Roo.panel.Content, {
63552 getId : function(){
63553 return this.grid.id;
63557 * Returns the grid for this panel
63558 * @return {Roo.grid.Grid}
63560 getGrid : function(){
63564 setSize : function(width, height){
63565 if(!this.ignoreResize(width, height)){
63566 var grid = this.grid;
63567 var size = this.adjustForComponents(width, height);
63568 grid.getGridEl().setSize(size.width, size.height);
63573 beforeSlide : function(){
63574 this.grid.getView().scroller.clip();
63577 afterSlide : function(){
63578 this.grid.getView().scroller.unclip();
63581 destroy : function(){
63582 this.grid.destroy();
63584 Roo.panel.Grid.superclass.destroy.call(this);
63590 * @class Roo.panel.NestedLayout
63591 * @extends Roo.panel.Content
63592 * @parent Roo.layout.Border Roo.LayoutDialog builder
63593 * @cfg {Roo.layout.Border} layout [required] The layout for this panel
63597 * Create a new NestedLayoutPanel.
63600 * @param {Roo.layout.Border} layout [required] The layout for this panel
63601 * @param {String/Object} config A string to set only the title or a config object
63603 Roo.panel.NestedLayout = function(layout, config)
63605 // construct with only one argument..
63606 /* FIXME - implement nicer consturctors
63607 if (layout.layout) {
63609 layout = config.layout;
63610 delete config.layout;
63612 if (layout.xtype && !layout.getEl) {
63613 // then layout needs constructing..
63614 layout = Roo.factory(layout, Roo);
63619 Roo.panel.NestedLayout.superclass.constructor.call(this, layout.getEl(), config);
63621 layout.monitorWindowResize = false; // turn off autosizing
63622 this.layout = layout;
63623 this.layout.getEl().addClass("x-layout-nested-layout");
63630 Roo.extend(Roo.panel.NestedLayout, Roo.panel.Content, {
63634 setSize : function(width, height){
63635 if(!this.ignoreResize(width, height)){
63636 var size = this.adjustForComponents(width, height);
63637 var el = this.layout.getEl();
63638 el.setSize(size.width, size.height);
63639 var touch = el.dom.offsetWidth;
63640 this.layout.layout();
63641 // ie requires a double layout on the first pass
63642 if(Roo.isIE && !this.initialized){
63643 this.initialized = true;
63644 this.layout.layout();
63649 // activate all subpanels if not currently active..
63651 setActiveState : function(active){
63652 this.active = active;
63654 this.fireEvent("deactivate", this);
63658 this.fireEvent("activate", this);
63659 // not sure if this should happen before or after..
63660 if (!this.layout) {
63661 return; // should not happen..
63664 for (var r in this.layout.regions) {
63665 reg = this.layout.getRegion(r);
63666 if (reg.getActivePanel()) {
63667 //reg.showPanel(reg.getActivePanel()); // force it to activate..
63668 reg.setActivePanel(reg.getActivePanel());
63671 if (!reg.panels.length) {
63674 reg.showPanel(reg.getPanel(0));
63683 * Returns the nested BorderLayout for this panel
63684 * @return {Roo.layout.Border}
63686 getLayout : function(){
63687 return this.layout;
63691 * Adds a xtype elements to the layout of the nested panel
63695 xtype : 'ContentPanel',
63702 xtype : 'panel.NestedLayout',
63708 items : [ ... list of content panels or nested layout panels.. ]
63712 * @param {Object} cfg Xtype definition of item to add.
63714 addxtype : function(cfg) {
63715 return this.layout.addxtype(cfg);
63720 Roo.panel.Scroll = function(el, config, content){
63721 config = config || {};
63722 config.fitToFrame = true;
63723 Roo.panel.Scroll.superclass.constructor.call(this, el, config, content);
63725 this.el.dom.style.overflow = "hidden";
63726 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
63727 this.el.removeClass("x-layout-inactive-content");
63728 this.el.on("mousewheel", this.onWheel, this);
63730 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
63731 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
63732 up.unselectable(); down.unselectable();
63733 up.on("click", this.scrollUp, this);
63734 down.on("click", this.scrollDown, this);
63735 up.addClassOnOver("x-scroller-btn-over");
63736 down.addClassOnOver("x-scroller-btn-over");
63737 up.addClassOnClick("x-scroller-btn-click");
63738 down.addClassOnClick("x-scroller-btn-click");
63739 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
63741 this.resizeEl = this.el;
63742 this.el = wrap; this.up = up; this.down = down;
63745 Roo.extend(Roo.panel.Scroll, Roo.panel.Content, {
63747 wheelIncrement : 5,
63748 scrollUp : function(){
63749 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
63752 scrollDown : function(){
63753 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
63756 afterScroll : function(){
63757 var el = this.resizeEl;
63758 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
63759 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
63760 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
63763 setSize : function(){
63764 Roo.panel.Scroll.superclass.setSize.apply(this, arguments);
63765 this.afterScroll();
63768 onWheel : function(e){
63769 var d = e.getWheelDelta();
63770 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
63771 this.afterScroll();
63775 setContent : function(content, loadScripts){
63776 this.resizeEl.update(content, loadScripts);
63784 * @class Roo.panel.Tree
63785 * @extends Roo.panel.Content
63786 * @parent Roo.layout.Border Roo.LayoutDialog builder
63787 * Treepanel component
63790 * Create a new TreePanel. - defaults to fit/scoll contents.
63791 * @param {String/Object} config A string to set only the panel's title, or a config object
63793 Roo.panel.Tree = function(config){
63794 var el = config.el;
63795 var tree = config.tree;
63796 delete config.tree;
63797 delete config.el; // hopefull!
63799 // wrapper for IE7 strict & safari scroll issue
63801 var treeEl = el.createChild();
63802 config.resizeEl = treeEl;
63806 Roo.panel.Tree.superclass.constructor.call(this, el, config);
63809 this.tree = new Roo.tree.TreePanel(treeEl , tree);
63810 //console.log(tree);
63811 this.on('activate', function()
63813 if (this.tree.rendered) {
63816 //console.log('render tree');
63817 this.tree.render();
63819 // this should not be needed.. - it's actually the 'el' that resizes?
63820 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
63822 //this.on('resize', function (cp, w, h) {
63823 // this.tree.innerCt.setWidth(w);
63824 // this.tree.innerCt.setHeight(h);
63825 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
63832 Roo.extend(Roo.panel.Tree, Roo.panel.Content, {
63836 * @cfg {Roo.tree.panel.Tree} tree [required] The tree TreePanel, with config etc.
63843 * Ext JS Library 1.1.1
63844 * Copyright(c) 2006-2007, Ext JS, LLC.
63846 * Originally Released Under LGPL - original licence link has changed is not relivant.
63849 * <script type="text/javascript">
63854 * @class Roo.layout.Reader
63855 * @extends Roo.layout.Border
63856 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
63857 * center region containing two nested regions (a top one for a list view and one for item preview below),
63858 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
63859 * The setup and configuration work exactly the same as it does for a {@link Roo.layout.Border} - this class simply
63860 * expedites the setup of the overall layout and regions for this common application style.
63863 var reader = new Roo.layout.Reader();
63864 var CP = Roo.panel.Content; // shortcut for adding
63866 reader.beginUpdate();
63867 reader.add("north", new CP("north", "North"));
63868 reader.add("west", new CP("west", {title: "West"}));
63869 reader.add("east", new CP("east", {title: "East"}));
63871 reader.regions.listView.add(new CP("listView", "List"));
63872 reader.regions.preview.add(new CP("preview", "Preview"));
63873 reader.endUpdate();
63876 * Create a new ReaderLayout
63877 * @param {Object} config Configuration options
63878 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
63879 * document.body if omitted)
63881 Roo.layout.Reader = function(config, renderTo){
63882 var c = config || {size:{}};
63883 Roo.layout.Reader.superclass.constructor.call(this, renderTo || document.body, {
63884 north: c.north !== false ? Roo.apply({
63888 }, c.north) : false,
63889 west: c.west !== false ? Roo.apply({
63897 margins:{left:5,right:0,bottom:5,top:5},
63898 cmargins:{left:5,right:5,bottom:5,top:5}
63899 }, c.west) : false,
63900 east: c.east !== false ? Roo.apply({
63908 margins:{left:0,right:5,bottom:5,top:5},
63909 cmargins:{left:5,right:5,bottom:5,top:5}
63910 }, c.east) : false,
63911 center: Roo.apply({
63912 tabPosition: 'top',
63916 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
63920 this.el.addClass('x-reader');
63922 this.beginUpdate();
63924 var inner = new Roo.layout.Border(Roo.get(document.body).createChild(), {
63925 south: c.preview !== false ? Roo.apply({
63932 cmargins:{top:5,left:0, right:0, bottom:0}
63933 }, c.preview) : false,
63934 center: Roo.apply({
63940 this.add('center', new Roo.panel.NestedLayout(inner,
63941 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
63945 this.regions.preview = inner.getRegion('south');
63946 this.regions.listView = inner.getRegion('center');
63949 Roo.extend(Roo.layout.Reader, Roo.layout.Border);/*
63951 * Ext JS Library 1.1.1
63952 * Copyright(c) 2006-2007, Ext JS, LLC.
63954 * Originally Released Under LGPL - original licence link has changed is not relivant.
63957 * <script type="text/javascript">
63961 * @class Roo.grid.Grid
63962 * @extends Roo.util.Observable
63963 * This class represents the primary interface of a component based grid control.
63964 * <br><br>Usage:<pre><code>
63965 var grid = new Roo.grid.Grid("my-container-id", {
63968 selModel: mySelectionModel,
63969 autoSizeColumns: true,
63970 monitorWindowResize: false,
63971 trackMouseOver: true
63976 * <b>Common Problems:</b><br/>
63977 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
63978 * element will correct this<br/>
63979 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
63980 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
63981 * are unpredictable.<br/>
63982 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
63983 * grid to calculate dimensions/offsets.<br/>
63985 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63986 * The container MUST have some type of size defined for the grid to fill. The container will be
63987 * automatically set to position relative if it isn't already.
63988 * @param {Object} config A config object that sets properties on this grid.
63990 Roo.grid.Grid = function(container, config){
63991 // initialize the container
63992 this.container = Roo.get(container);
63993 this.container.update("");
63994 this.container.setStyle("overflow", "hidden");
63995 this.container.addClass('x-grid-container');
63997 this.id = this.container.id;
63999 Roo.apply(this, config);
64000 // check and correct shorthanded configs
64002 this.dataSource = this.ds;
64006 this.colModel = this.cm;
64010 this.selModel = this.sm;
64014 if (this.selModel) {
64015 this.selModel = Roo.factory(this.selModel, Roo.grid);
64016 this.sm = this.selModel;
64017 this.sm.xmodule = this.xmodule || false;
64019 if (typeof(this.colModel.config) == 'undefined') {
64020 this.colModel = new Roo.grid.ColumnModel(this.colModel);
64021 this.cm = this.colModel;
64022 this.cm.xmodule = this.xmodule || false;
64024 if (this.dataSource) {
64025 this.dataSource= Roo.factory(this.dataSource, Roo.data);
64026 this.ds = this.dataSource;
64027 this.ds.xmodule = this.xmodule || false;
64034 this.container.setWidth(this.width);
64038 this.container.setHeight(this.height);
64045 * The raw click event for the entire grid.
64046 * @param {Roo.EventObject} e
64051 * The raw dblclick event for the entire grid.
64052 * @param {Roo.EventObject} e
64056 * @event contextmenu
64057 * The raw contextmenu event for the entire grid.
64058 * @param {Roo.EventObject} e
64060 "contextmenu" : true,
64063 * The raw mousedown event for the entire grid.
64064 * @param {Roo.EventObject} e
64066 "mousedown" : true,
64069 * The raw mouseup event for the entire grid.
64070 * @param {Roo.EventObject} e
64075 * The raw mouseover event for the entire grid.
64076 * @param {Roo.EventObject} e
64078 "mouseover" : true,
64081 * The raw mouseout event for the entire grid.
64082 * @param {Roo.EventObject} e
64087 * The raw keypress event for the entire grid.
64088 * @param {Roo.EventObject} e
64093 * The raw keydown event for the entire grid.
64094 * @param {Roo.EventObject} e
64102 * Fires when a cell is clicked
64103 * @param {Grid} this
64104 * @param {Number} rowIndex
64105 * @param {Number} columnIndex
64106 * @param {Roo.EventObject} e
64108 "cellclick" : true,
64110 * @event celldblclick
64111 * Fires when a cell is double clicked
64112 * @param {Grid} this
64113 * @param {Number} rowIndex
64114 * @param {Number} columnIndex
64115 * @param {Roo.EventObject} e
64117 "celldblclick" : true,
64120 * Fires when a row is clicked
64121 * @param {Grid} this
64122 * @param {Number} rowIndex
64123 * @param {Roo.EventObject} e
64127 * @event rowdblclick
64128 * Fires when a row is double clicked
64129 * @param {Grid} this
64130 * @param {Number} rowIndex
64131 * @param {Roo.EventObject} e
64133 "rowdblclick" : true,
64135 * @event headerclick
64136 * Fires when a header is clicked
64137 * @param {Grid} this
64138 * @param {Number} columnIndex
64139 * @param {Roo.EventObject} e
64141 "headerclick" : true,
64143 * @event headerdblclick
64144 * Fires when a header cell is double clicked
64145 * @param {Grid} this
64146 * @param {Number} columnIndex
64147 * @param {Roo.EventObject} e
64149 "headerdblclick" : true,
64151 * @event rowcontextmenu
64152 * Fires when a row is right clicked
64153 * @param {Grid} this
64154 * @param {Number} rowIndex
64155 * @param {Roo.EventObject} e
64157 "rowcontextmenu" : true,
64159 * @event cellcontextmenu
64160 * Fires when a cell is right clicked
64161 * @param {Grid} this
64162 * @param {Number} rowIndex
64163 * @param {Number} cellIndex
64164 * @param {Roo.EventObject} e
64166 "cellcontextmenu" : true,
64168 * @event headercontextmenu
64169 * Fires when a header is right clicked
64170 * @param {Grid} this
64171 * @param {Number} columnIndex
64172 * @param {Roo.EventObject} e
64174 "headercontextmenu" : true,
64176 * @event bodyscroll
64177 * Fires when the body element is scrolled
64178 * @param {Number} scrollLeft
64179 * @param {Number} scrollTop
64181 "bodyscroll" : true,
64183 * @event columnresize
64184 * Fires when the user resizes a column
64185 * @param {Number} columnIndex
64186 * @param {Number} newSize
64188 "columnresize" : true,
64190 * @event columnmove
64191 * Fires when the user moves a column
64192 * @param {Number} oldIndex
64193 * @param {Number} newIndex
64195 "columnmove" : true,
64198 * Fires when row(s) start being dragged
64199 * @param {Grid} this
64200 * @param {Roo.GridDD} dd The drag drop object
64201 * @param {event} e The raw browser event
64203 "startdrag" : true,
64206 * Fires when a drag operation is complete
64207 * @param {Grid} this
64208 * @param {Roo.GridDD} dd The drag drop object
64209 * @param {event} e The raw browser event
64214 * Fires when dragged row(s) are dropped on a valid DD target
64215 * @param {Grid} this
64216 * @param {Roo.GridDD} dd The drag drop object
64217 * @param {String} targetId The target drag drop object
64218 * @param {event} e The raw browser event
64223 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
64224 * @param {Grid} this
64225 * @param {Roo.GridDD} dd The drag drop object
64226 * @param {String} targetId The target drag drop object
64227 * @param {event} e The raw browser event
64232 * Fires when the dragged row(s) first cross another DD target while being dragged
64233 * @param {Grid} this
64234 * @param {Roo.GridDD} dd The drag drop object
64235 * @param {String} targetId The target drag drop object
64236 * @param {event} e The raw browser event
64238 "dragenter" : true,
64241 * Fires when the dragged row(s) leave another DD target while being dragged
64242 * @param {Grid} this
64243 * @param {Roo.GridDD} dd The drag drop object
64244 * @param {String} targetId The target drag drop object
64245 * @param {event} e The raw browser event
64250 * Fires when a row is rendered, so you can change add a style to it.
64251 * @param {GridView} gridview The grid view
64252 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
64258 * Fires when the grid is rendered
64259 * @param {Grid} grid
64264 Roo.grid.Grid.superclass.constructor.call(this);
64266 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
64269 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
64272 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
64275 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
64278 * @cfg {Roo.data.Store} ds The data store for the grid
64281 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
64285 * @cfg {Roo.PagingToolbar} footer the paging toolbar
64289 * @cfg {String} ddGroup - drag drop group.
64292 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
64296 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
64298 minColumnWidth : 25,
64301 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
64302 * <b>on initial render.</b> It is more efficient to explicitly size the columns
64303 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
64305 autoSizeColumns : false,
64308 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
64310 autoSizeHeaders : true,
64313 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
64315 monitorWindowResize : true,
64318 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
64319 * rows measured to get a columns size. Default is 0 (all rows).
64321 maxRowsToMeasure : 0,
64324 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
64326 trackMouseOver : true,
64329 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
64332 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
64336 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
64338 enableDragDrop : false,
64341 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
64343 enableColumnMove : true,
64346 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
64348 enableColumnHide : true,
64351 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
64353 enableRowHeightSync : false,
64356 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
64361 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
64363 autoHeight : false,
64366 * @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.
64368 autoExpandColumn : false,
64371 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
64374 autoExpandMin : 50,
64377 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
64379 autoExpandMax : 1000,
64382 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
64387 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
64391 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
64395 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
64397 sortColMenu : false,
64403 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
64404 * of a fixed width. Default is false.
64407 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
64412 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
64413 * %0 is replaced with the number of selected rows.
64415 ddText : "{0} selected row{1}",
64419 * Called once after all setup has been completed and the grid is ready to be rendered.
64420 * @return {Roo.grid.Grid} this
64422 render : function()
64424 var c = this.container;
64425 // try to detect autoHeight/width mode
64426 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
64427 this.autoHeight = true;
64429 var view = this.getView();
64432 c.on("click", this.onClick, this);
64433 c.on("dblclick", this.onDblClick, this);
64434 c.on("contextmenu", this.onContextMenu, this);
64435 c.on("keydown", this.onKeyDown, this);
64437 c.on("touchstart", this.onTouchStart, this);
64440 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
64442 this.getSelectionModel().init(this);
64447 this.loadMask = new Roo.LoadMask(this.container,
64448 Roo.apply({store:this.dataSource}, this.loadMask));
64452 if (this.toolbar && this.toolbar.xtype) {
64453 this.toolbar.container = this.getView().getHeaderPanel(true);
64454 this.toolbar = new Roo.Toolbar(this.toolbar);
64456 if (this.footer && this.footer.xtype) {
64457 this.footer.dataSource = this.getDataSource();
64458 this.footer.container = this.getView().getFooterPanel(true);
64459 this.footer = Roo.factory(this.footer, Roo);
64461 if (this.dropTarget && this.dropTarget.xtype) {
64462 delete this.dropTarget.xtype;
64463 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
64467 this.rendered = true;
64468 this.fireEvent('render', this);
64473 * Reconfigures the grid to use a different Store and Column Model.
64474 * The View will be bound to the new objects and refreshed.
64475 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
64476 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
64478 reconfigure : function(dataSource, colModel){
64480 this.loadMask.destroy();
64481 this.loadMask = new Roo.LoadMask(this.container,
64482 Roo.apply({store:dataSource}, this.loadMask));
64484 this.view.bind(dataSource, colModel);
64485 this.dataSource = dataSource;
64486 this.colModel = colModel;
64487 this.view.refresh(true);
64491 * Add's a column, default at the end..
64493 * @param {int} position to add (default end)
64494 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
64496 addColumns : function(pos, ar)
64499 for (var i =0;i< ar.length;i++) {
64501 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
64502 this.cm.lookup[cfg.id] = cfg;
64506 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
64507 pos = this.cm.config.length; //this.cm.config.push(cfg);
64509 pos = Math.max(0,pos);
64512 this.cm.config.splice.apply(this.cm.config, ar);
64516 this.view.generateRules(this.cm);
64517 this.view.refresh(true);
64525 onKeyDown : function(e){
64526 this.fireEvent("keydown", e);
64530 * Destroy this grid.
64531 * @param {Boolean} removeEl True to remove the element
64533 destroy : function(removeEl, keepListeners){
64535 this.loadMask.destroy();
64537 var c = this.container;
64538 c.removeAllListeners();
64539 this.view.destroy();
64540 this.colModel.purgeListeners();
64541 if(!keepListeners){
64542 this.purgeListeners();
64545 if(removeEl === true){
64551 processEvent : function(name, e){
64552 // does this fire select???
64553 //Roo.log('grid:processEvent ' + name);
64555 if (name != 'touchstart' ) {
64556 this.fireEvent(name, e);
64559 var t = e.getTarget();
64561 var header = v.findHeaderIndex(t);
64562 if(header !== false){
64563 var ename = name == 'touchstart' ? 'click' : name;
64565 this.fireEvent("header" + ename, this, header, e);
64567 var row = v.findRowIndex(t);
64568 var cell = v.findCellIndex(t);
64569 if (name == 'touchstart') {
64570 // first touch is always a click.
64571 // hopefull this happens after selection is updated.?
64574 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
64575 var cs = this.selModel.getSelectedCell();
64576 if (row == cs[0] && cell == cs[1]){
64580 if (typeof(this.selModel.getSelections) != 'undefined') {
64581 var cs = this.selModel.getSelections();
64582 var ds = this.dataSource;
64583 if (cs.length == 1 && ds.getAt(row) == cs[0]){
64594 this.fireEvent("row" + name, this, row, e);
64595 if(cell !== false){
64596 this.fireEvent("cell" + name, this, row, cell, e);
64603 onClick : function(e){
64604 this.processEvent("click", e);
64607 onTouchStart : function(e){
64608 this.processEvent("touchstart", e);
64612 onContextMenu : function(e, t){
64613 this.processEvent("contextmenu", e);
64617 onDblClick : function(e){
64618 this.processEvent("dblclick", e);
64622 walkCells : function(row, col, step, fn, scope){
64623 var cm = this.colModel, clen = cm.getColumnCount();
64624 var ds = this.dataSource, rlen = ds.getCount(), first = true;
64636 if(fn.call(scope || this, row, col, cm) === true){
64654 if(fn.call(scope || this, row, col, cm) === true){
64666 getSelections : function(){
64667 return this.selModel.getSelections();
64671 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
64672 * but if manual update is required this method will initiate it.
64674 autoSize : function(){
64676 this.view.layout();
64677 if(this.view.adjustForScroll){
64678 this.view.adjustForScroll();
64684 * Returns the grid's underlying element.
64685 * @return {Element} The element
64687 getGridEl : function(){
64688 return this.container;
64691 // private for compatibility, overridden by editor grid
64692 stopEditing : function(){},
64695 * Returns the grid's SelectionModel.
64696 * @return {SelectionModel}
64698 getSelectionModel : function(){
64699 if(!this.selModel){
64700 this.selModel = new Roo.grid.RowSelectionModel();
64702 return this.selModel;
64706 * Returns the grid's DataSource.
64707 * @return {DataSource}
64709 getDataSource : function(){
64710 return this.dataSource;
64714 * Returns the grid's ColumnModel.
64715 * @return {ColumnModel}
64717 getColumnModel : function(){
64718 return this.colModel;
64722 * Returns the grid's GridView object.
64723 * @return {GridView}
64725 getView : function(){
64727 this.view = new Roo.grid.GridView(this.viewConfig);
64728 this.relayEvents(this.view, [
64729 "beforerowremoved", "beforerowsinserted",
64730 "beforerefresh", "rowremoved",
64731 "rowsinserted", "rowupdated" ,"refresh"
64737 * Called to get grid's drag proxy text, by default returns this.ddText.
64738 * Override this to put something different in the dragged text.
64741 getDragDropText : function(){
64742 var count = this.selModel.getCount();
64743 return String.format(this.ddText, count, count == 1 ? '' : 's');
64748 * Ext JS Library 1.1.1
64749 * Copyright(c) 2006-2007, Ext JS, LLC.
64751 * Originally Released Under LGPL - original licence link has changed is not relivant.
64754 * <script type="text/javascript">
64757 * @class Roo.grid.AbstractGridView
64758 * @extends Roo.util.Observable
64760 * Abstract base class for grid Views
64763 Roo.grid.AbstractGridView = function(){
64767 "beforerowremoved" : true,
64768 "beforerowsinserted" : true,
64769 "beforerefresh" : true,
64770 "rowremoved" : true,
64771 "rowsinserted" : true,
64772 "rowupdated" : true,
64775 Roo.grid.AbstractGridView.superclass.constructor.call(this);
64778 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
64779 rowClass : "x-grid-row",
64780 cellClass : "x-grid-cell",
64781 tdClass : "x-grid-td",
64782 hdClass : "x-grid-hd",
64783 splitClass : "x-grid-hd-split",
64785 init: function(grid){
64787 var cid = this.grid.getGridEl().id;
64788 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
64789 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
64790 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
64791 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
64794 getColumnRenderers : function(){
64795 var renderers = [];
64796 var cm = this.grid.colModel;
64797 var colCount = cm.getColumnCount();
64798 for(var i = 0; i < colCount; i++){
64799 renderers[i] = cm.getRenderer(i);
64804 getColumnIds : function(){
64806 var cm = this.grid.colModel;
64807 var colCount = cm.getColumnCount();
64808 for(var i = 0; i < colCount; i++){
64809 ids[i] = cm.getColumnId(i);
64814 getDataIndexes : function(){
64815 if(!this.indexMap){
64816 this.indexMap = this.buildIndexMap();
64818 return this.indexMap.colToData;
64821 getColumnIndexByDataIndex : function(dataIndex){
64822 if(!this.indexMap){
64823 this.indexMap = this.buildIndexMap();
64825 return this.indexMap.dataToCol[dataIndex];
64829 * Set a css style for a column dynamically.
64830 * @param {Number} colIndex The index of the column
64831 * @param {String} name The css property name
64832 * @param {String} value The css value
64834 setCSSStyle : function(colIndex, name, value){
64835 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
64836 Roo.util.CSS.updateRule(selector, name, value);
64839 generateRules : function(cm){
64840 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
64841 Roo.util.CSS.removeStyleSheet(rulesId);
64842 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
64843 var cid = cm.getColumnId(i);
64844 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
64845 this.tdSelector, cid, " {\n}\n",
64846 this.hdSelector, cid, " {\n}\n",
64847 this.splitSelector, cid, " {\n}\n");
64849 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
64853 * Ext JS Library 1.1.1
64854 * Copyright(c) 2006-2007, Ext JS, LLC.
64856 * Originally Released Under LGPL - original licence link has changed is not relivant.
64859 * <script type="text/javascript">
64863 // This is a support class used internally by the Grid components
64864 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
64866 this.view = grid.getView();
64867 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
64868 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
64870 this.setHandleElId(Roo.id(hd));
64871 this.setOuterHandleElId(Roo.id(hd2));
64873 this.scroll = false;
64875 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
64877 getDragData : function(e){
64878 var t = Roo.lib.Event.getTarget(e);
64879 var h = this.view.findHeaderCell(t);
64881 return {ddel: h.firstChild, header:h};
64886 onInitDrag : function(e){
64887 this.view.headersDisabled = true;
64888 var clone = this.dragData.ddel.cloneNode(true);
64889 clone.id = Roo.id();
64890 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
64891 this.proxy.update(clone);
64895 afterValidDrop : function(){
64897 setTimeout(function(){
64898 v.headersDisabled = false;
64902 afterInvalidDrop : function(){
64904 setTimeout(function(){
64905 v.headersDisabled = false;
64911 * Ext JS Library 1.1.1
64912 * Copyright(c) 2006-2007, Ext JS, LLC.
64914 * Originally Released Under LGPL - original licence link has changed is not relivant.
64917 * <script type="text/javascript">
64920 // This is a support class used internally by the Grid components
64921 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
64923 this.view = grid.getView();
64924 // split the proxies so they don't interfere with mouse events
64925 this.proxyTop = Roo.DomHelper.append(document.body, {
64926 cls:"col-move-top", html:" "
64928 this.proxyBottom = Roo.DomHelper.append(document.body, {
64929 cls:"col-move-bottom", html:" "
64931 this.proxyTop.hide = this.proxyBottom.hide = function(){
64932 this.setLeftTop(-100,-100);
64933 this.setStyle("visibility", "hidden");
64935 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
64936 // temporarily disabled
64937 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
64938 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
64940 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
64941 proxyOffsets : [-4, -9],
64942 fly: Roo.Element.fly,
64944 getTargetFromEvent : function(e){
64945 var t = Roo.lib.Event.getTarget(e);
64946 var cindex = this.view.findCellIndex(t);
64947 if(cindex !== false){
64948 return this.view.getHeaderCell(cindex);
64953 nextVisible : function(h){
64954 var v = this.view, cm = this.grid.colModel;
64957 if(!cm.isHidden(v.getCellIndex(h))){
64965 prevVisible : function(h){
64966 var v = this.view, cm = this.grid.colModel;
64969 if(!cm.isHidden(v.getCellIndex(h))){
64977 positionIndicator : function(h, n, e){
64978 var x = Roo.lib.Event.getPageX(e);
64979 var r = Roo.lib.Dom.getRegion(n.firstChild);
64980 var px, pt, py = r.top + this.proxyOffsets[1];
64981 if((r.right - x) <= (r.right-r.left)/2){
64982 px = r.right+this.view.borderWidth;
64988 var oldIndex = this.view.getCellIndex(h);
64989 var newIndex = this.view.getCellIndex(n);
64991 if(this.grid.colModel.isFixed(newIndex)){
64995 var locked = this.grid.colModel.isLocked(newIndex);
65000 if(oldIndex < newIndex){
65003 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
65006 px += this.proxyOffsets[0];
65007 this.proxyTop.setLeftTop(px, py);
65008 this.proxyTop.show();
65009 if(!this.bottomOffset){
65010 this.bottomOffset = this.view.mainHd.getHeight();
65012 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
65013 this.proxyBottom.show();
65017 onNodeEnter : function(n, dd, e, data){
65018 if(data.header != n){
65019 this.positionIndicator(data.header, n, e);
65023 onNodeOver : function(n, dd, e, data){
65024 var result = false;
65025 if(data.header != n){
65026 result = this.positionIndicator(data.header, n, e);
65029 this.proxyTop.hide();
65030 this.proxyBottom.hide();
65032 return result ? this.dropAllowed : this.dropNotAllowed;
65035 onNodeOut : function(n, dd, e, data){
65036 this.proxyTop.hide();
65037 this.proxyBottom.hide();
65040 onNodeDrop : function(n, dd, e, data){
65041 var h = data.header;
65043 var cm = this.grid.colModel;
65044 var x = Roo.lib.Event.getPageX(e);
65045 var r = Roo.lib.Dom.getRegion(n.firstChild);
65046 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
65047 var oldIndex = this.view.getCellIndex(h);
65048 var newIndex = this.view.getCellIndex(n);
65049 var locked = cm.isLocked(newIndex);
65053 if(oldIndex < newIndex){
65056 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
65059 cm.setLocked(oldIndex, locked, true);
65060 cm.moveColumn(oldIndex, newIndex);
65061 this.grid.fireEvent("columnmove", oldIndex, newIndex);
65069 * Ext JS Library 1.1.1
65070 * Copyright(c) 2006-2007, Ext JS, LLC.
65072 * Originally Released Under LGPL - original licence link has changed is not relivant.
65075 * <script type="text/javascript">
65079 * @class Roo.grid.GridView
65080 * @extends Roo.util.Observable
65083 * @param {Object} config
65085 Roo.grid.GridView = function(config){
65086 Roo.grid.GridView.superclass.constructor.call(this);
65089 Roo.apply(this, config);
65092 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
65094 unselectable : 'unselectable="on"',
65095 unselectableCls : 'x-unselectable',
65098 rowClass : "x-grid-row",
65100 cellClass : "x-grid-col",
65102 tdClass : "x-grid-td",
65104 hdClass : "x-grid-hd",
65106 splitClass : "x-grid-split",
65108 sortClasses : ["sort-asc", "sort-desc"],
65110 enableMoveAnim : false,
65114 dh : Roo.DomHelper,
65116 fly : Roo.Element.fly,
65118 css : Roo.util.CSS,
65124 scrollIncrement : 22,
65126 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
65128 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
65130 bind : function(ds, cm){
65132 this.ds.un("load", this.onLoad, this);
65133 this.ds.un("datachanged", this.onDataChange, this);
65134 this.ds.un("add", this.onAdd, this);
65135 this.ds.un("remove", this.onRemove, this);
65136 this.ds.un("update", this.onUpdate, this);
65137 this.ds.un("clear", this.onClear, this);
65140 ds.on("load", this.onLoad, this);
65141 ds.on("datachanged", this.onDataChange, this);
65142 ds.on("add", this.onAdd, this);
65143 ds.on("remove", this.onRemove, this);
65144 ds.on("update", this.onUpdate, this);
65145 ds.on("clear", this.onClear, this);
65150 this.cm.un("widthchange", this.onColWidthChange, this);
65151 this.cm.un("headerchange", this.onHeaderChange, this);
65152 this.cm.un("hiddenchange", this.onHiddenChange, this);
65153 this.cm.un("columnmoved", this.onColumnMove, this);
65154 this.cm.un("columnlockchange", this.onColumnLock, this);
65157 this.generateRules(cm);
65158 cm.on("widthchange", this.onColWidthChange, this);
65159 cm.on("headerchange", this.onHeaderChange, this);
65160 cm.on("hiddenchange", this.onHiddenChange, this);
65161 cm.on("columnmoved", this.onColumnMove, this);
65162 cm.on("columnlockchange", this.onColumnLock, this);
65167 init: function(grid){
65168 Roo.grid.GridView.superclass.init.call(this, grid);
65170 this.bind(grid.dataSource, grid.colModel);
65172 grid.on("headerclick", this.handleHeaderClick, this);
65174 if(grid.trackMouseOver){
65175 grid.on("mouseover", this.onRowOver, this);
65176 grid.on("mouseout", this.onRowOut, this);
65178 grid.cancelTextSelection = function(){};
65179 this.gridId = grid.id;
65181 var tpls = this.templates || {};
65184 tpls.master = new Roo.Template(
65185 '<div class="x-grid" hidefocus="true">',
65186 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
65187 '<div class="x-grid-topbar"></div>',
65188 '<div class="x-grid-scroller"><div></div></div>',
65189 '<div class="x-grid-locked">',
65190 '<div class="x-grid-header">{lockedHeader}</div>',
65191 '<div class="x-grid-body">{lockedBody}</div>',
65193 '<div class="x-grid-viewport">',
65194 '<div class="x-grid-header">{header}</div>',
65195 '<div class="x-grid-body">{body}</div>',
65197 '<div class="x-grid-bottombar"></div>',
65199 '<div class="x-grid-resize-proxy"> </div>',
65202 tpls.master.disableformats = true;
65206 tpls.header = new Roo.Template(
65207 '<table border="0" cellspacing="0" cellpadding="0">',
65208 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
65211 tpls.header.disableformats = true;
65213 tpls.header.compile();
65216 tpls.hcell = new Roo.Template(
65217 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
65218 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
65221 tpls.hcell.disableFormats = true;
65223 tpls.hcell.compile();
65226 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
65227 this.unselectableCls + '" ' + this.unselectable +'> </div>');
65228 tpls.hsplit.disableFormats = true;
65230 tpls.hsplit.compile();
65233 tpls.body = new Roo.Template(
65234 '<table border="0" cellspacing="0" cellpadding="0">',
65235 "<tbody>{rows}</tbody>",
65238 tpls.body.disableFormats = true;
65240 tpls.body.compile();
65243 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
65244 tpls.row.disableFormats = true;
65246 tpls.row.compile();
65249 tpls.cell = new Roo.Template(
65250 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
65251 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
65252 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
65255 tpls.cell.disableFormats = true;
65257 tpls.cell.compile();
65259 this.templates = tpls;
65262 // remap these for backwards compat
65263 onColWidthChange : function(){
65264 this.updateColumns.apply(this, arguments);
65266 onHeaderChange : function(){
65267 this.updateHeaders.apply(this, arguments);
65269 onHiddenChange : function(){
65270 this.handleHiddenChange.apply(this, arguments);
65272 onColumnMove : function(){
65273 this.handleColumnMove.apply(this, arguments);
65275 onColumnLock : function(){
65276 this.handleLockChange.apply(this, arguments);
65279 onDataChange : function(){
65281 this.updateHeaderSortState();
65284 onClear : function(){
65288 onUpdate : function(ds, record){
65289 this.refreshRow(record);
65292 refreshRow : function(record){
65293 var ds = this.ds, index;
65294 if(typeof record == 'number'){
65296 record = ds.getAt(index);
65298 index = ds.indexOf(record);
65300 this.insertRows(ds, index, index, true);
65301 this.onRemove(ds, record, index+1, true);
65302 this.syncRowHeights(index, index);
65304 this.fireEvent("rowupdated", this, index, record);
65307 onAdd : function(ds, records, index){
65308 this.insertRows(ds, index, index + (records.length-1));
65311 onRemove : function(ds, record, index, isUpdate){
65312 if(isUpdate !== true){
65313 this.fireEvent("beforerowremoved", this, index, record);
65315 var bt = this.getBodyTable(), lt = this.getLockedTable();
65316 if(bt.rows[index]){
65317 bt.firstChild.removeChild(bt.rows[index]);
65319 if(lt.rows[index]){
65320 lt.firstChild.removeChild(lt.rows[index]);
65322 if(isUpdate !== true){
65323 this.stripeRows(index);
65324 this.syncRowHeights(index, index);
65326 this.fireEvent("rowremoved", this, index, record);
65330 onLoad : function(){
65331 this.scrollToTop();
65335 * Scrolls the grid to the top
65337 scrollToTop : function(){
65339 this.scroller.dom.scrollTop = 0;
65345 * Gets a panel in the header of the grid that can be used for toolbars etc.
65346 * After modifying the contents of this panel a call to grid.autoSize() may be
65347 * required to register any changes in size.
65348 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
65349 * @return Roo.Element
65351 getHeaderPanel : function(doShow){
65353 this.headerPanel.show();
65355 return this.headerPanel;
65359 * Gets a panel in the footer of the grid that can be used for toolbars etc.
65360 * After modifying the contents of this panel a call to grid.autoSize() may be
65361 * required to register any changes in size.
65362 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
65363 * @return Roo.Element
65365 getFooterPanel : function(doShow){
65367 this.footerPanel.show();
65369 return this.footerPanel;
65372 initElements : function(){
65373 var E = Roo.Element;
65374 var el = this.grid.getGridEl().dom.firstChild;
65375 var cs = el.childNodes;
65377 this.el = new E(el);
65379 this.focusEl = new E(el.firstChild);
65380 this.focusEl.swallowEvent("click", true);
65382 this.headerPanel = new E(cs[1]);
65383 this.headerPanel.enableDisplayMode("block");
65385 this.scroller = new E(cs[2]);
65386 this.scrollSizer = new E(this.scroller.dom.firstChild);
65388 this.lockedWrap = new E(cs[3]);
65389 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
65390 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
65392 this.mainWrap = new E(cs[4]);
65393 this.mainHd = new E(this.mainWrap.dom.firstChild);
65394 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
65396 this.footerPanel = new E(cs[5]);
65397 this.footerPanel.enableDisplayMode("block");
65399 this.resizeProxy = new E(cs[6]);
65401 this.headerSelector = String.format(
65402 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
65403 this.lockedHd.id, this.mainHd.id
65406 this.splitterSelector = String.format(
65407 '#{0} div.x-grid-split, #{1} div.x-grid-split',
65408 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
65411 idToCssName : function(s)
65413 return s.replace(/[^a-z0-9]+/ig, '-');
65416 getHeaderCell : function(index){
65417 return Roo.DomQuery.select(this.headerSelector)[index];
65420 getHeaderCellMeasure : function(index){
65421 return this.getHeaderCell(index).firstChild;
65424 getHeaderCellText : function(index){
65425 return this.getHeaderCell(index).firstChild.firstChild;
65428 getLockedTable : function(){
65429 return this.lockedBody.dom.firstChild;
65432 getBodyTable : function(){
65433 return this.mainBody.dom.firstChild;
65436 getLockedRow : function(index){
65437 return this.getLockedTable().rows[index];
65440 getRow : function(index){
65441 return this.getBodyTable().rows[index];
65444 getRowComposite : function(index){
65446 this.rowEl = new Roo.CompositeElementLite();
65448 var els = [], lrow, mrow;
65449 if(lrow = this.getLockedRow(index)){
65452 if(mrow = this.getRow(index)){
65455 this.rowEl.elements = els;
65459 * Gets the 'td' of the cell
65461 * @param {Integer} rowIndex row to select
65462 * @param {Integer} colIndex column to select
65466 getCell : function(rowIndex, colIndex){
65467 var locked = this.cm.getLockedCount();
65469 if(colIndex < locked){
65470 source = this.lockedBody.dom.firstChild;
65472 source = this.mainBody.dom.firstChild;
65473 colIndex -= locked;
65475 return source.rows[rowIndex].childNodes[colIndex];
65478 getCellText : function(rowIndex, colIndex){
65479 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
65482 getCellBox : function(cell){
65483 var b = this.fly(cell).getBox();
65484 if(Roo.isOpera){ // opera fails to report the Y
65485 b.y = cell.offsetTop + this.mainBody.getY();
65490 getCellIndex : function(cell){
65491 var id = String(cell.className).match(this.cellRE);
65493 return parseInt(id[1], 10);
65498 findHeaderIndex : function(n){
65499 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
65500 return r ? this.getCellIndex(r) : false;
65503 findHeaderCell : function(n){
65504 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
65505 return r ? r : false;
65508 findRowIndex : function(n){
65512 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
65513 return r ? r.rowIndex : false;
65516 findCellIndex : function(node){
65517 var stop = this.el.dom;
65518 while(node && node != stop){
65519 if(this.findRE.test(node.className)){
65520 return this.getCellIndex(node);
65522 node = node.parentNode;
65527 getColumnId : function(index){
65528 return this.cm.getColumnId(index);
65531 getSplitters : function()
65533 if(this.splitterSelector){
65534 return Roo.DomQuery.select(this.splitterSelector);
65540 getSplitter : function(index){
65541 return this.getSplitters()[index];
65544 onRowOver : function(e, t){
65546 if((row = this.findRowIndex(t)) !== false){
65547 this.getRowComposite(row).addClass("x-grid-row-over");
65551 onRowOut : function(e, t){
65553 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
65554 this.getRowComposite(row).removeClass("x-grid-row-over");
65558 renderHeaders : function(){
65560 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
65561 var cb = [], lb = [], sb = [], lsb = [], p = {};
65562 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65563 p.cellId = "x-grid-hd-0-" + i;
65564 p.splitId = "x-grid-csplit-0-" + i;
65565 p.id = cm.getColumnId(i);
65566 p.value = cm.getColumnHeader(i) || "";
65567 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
65568 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
65569 if(!cm.isLocked(i)){
65570 cb[cb.length] = ct.apply(p);
65571 sb[sb.length] = st.apply(p);
65573 lb[lb.length] = ct.apply(p);
65574 lsb[lsb.length] = st.apply(p);
65577 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
65578 ht.apply({cells: cb.join(""), splits:sb.join("")})];
65581 updateHeaders : function(){
65582 var html = this.renderHeaders();
65583 this.lockedHd.update(html[0]);
65584 this.mainHd.update(html[1]);
65588 * Focuses the specified row.
65589 * @param {Number} row The row index
65591 focusRow : function(row)
65593 //Roo.log('GridView.focusRow');
65594 var x = this.scroller.dom.scrollLeft;
65595 this.focusCell(row, 0, false);
65596 this.scroller.dom.scrollLeft = x;
65600 * Focuses the specified cell.
65601 * @param {Number} row The row index
65602 * @param {Number} col The column index
65603 * @param {Boolean} hscroll false to disable horizontal scrolling
65605 focusCell : function(row, col, hscroll)
65607 //Roo.log('GridView.focusCell');
65608 var el = this.ensureVisible(row, col, hscroll);
65609 this.focusEl.alignTo(el, "tl-tl");
65611 this.focusEl.focus();
65613 this.focusEl.focus.defer(1, this.focusEl);
65618 * Scrolls the specified cell into view
65619 * @param {Number} row The row index
65620 * @param {Number} col The column index
65621 * @param {Boolean} hscroll false to disable horizontal scrolling
65623 ensureVisible : function(row, col, hscroll)
65625 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
65626 //return null; //disable for testing.
65627 if(typeof row != "number"){
65628 row = row.rowIndex;
65630 if(row < 0 && row >= this.ds.getCount()){
65633 col = (col !== undefined ? col : 0);
65634 var cm = this.grid.colModel;
65635 while(cm.isHidden(col)){
65639 var el = this.getCell(row, col);
65643 var c = this.scroller.dom;
65645 var ctop = parseInt(el.offsetTop, 10);
65646 var cleft = parseInt(el.offsetLeft, 10);
65647 var cbot = ctop + el.offsetHeight;
65648 var cright = cleft + el.offsetWidth;
65650 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
65651 var stop = parseInt(c.scrollTop, 10);
65652 var sleft = parseInt(c.scrollLeft, 10);
65653 var sbot = stop + ch;
65654 var sright = sleft + c.clientWidth;
65656 Roo.log('GridView.ensureVisible:' +
65658 ' c.clientHeight:' + c.clientHeight +
65659 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
65667 c.scrollTop = ctop;
65668 //Roo.log("set scrolltop to ctop DISABLE?");
65669 }else if(cbot > sbot){
65670 //Roo.log("set scrolltop to cbot-ch");
65671 c.scrollTop = cbot-ch;
65674 if(hscroll !== false){
65676 c.scrollLeft = cleft;
65677 }else if(cright > sright){
65678 c.scrollLeft = cright-c.clientWidth;
65685 updateColumns : function(){
65686 this.grid.stopEditing();
65687 var cm = this.grid.colModel, colIds = this.getColumnIds();
65688 //var totalWidth = cm.getTotalWidth();
65690 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65691 //if(cm.isHidden(i)) continue;
65692 var w = cm.getColumnWidth(i);
65693 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
65694 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
65696 this.updateSplitters();
65699 generateRules : function(cm){
65700 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
65701 Roo.util.CSS.removeStyleSheet(rulesId);
65702 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65703 var cid = cm.getColumnId(i);
65705 if(cm.config[i].align){
65706 align = 'text-align:'+cm.config[i].align+';';
65709 if(cm.isHidden(i)){
65710 hidden = 'display:none;';
65712 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
65714 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
65715 this.hdSelector, cid, " {\n", align, width, "}\n",
65716 this.tdSelector, cid, " {\n",hidden,"\n}\n",
65717 this.splitSelector, cid, " {\n", hidden , "\n}\n");
65719 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
65722 updateSplitters : function(){
65723 var cm = this.cm, s = this.getSplitters();
65724 if(s){ // splitters not created yet
65725 var pos = 0, locked = true;
65726 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
65727 if(cm.isHidden(i)) {
65730 var w = cm.getColumnWidth(i); // make sure it's a number
65731 if(!cm.isLocked(i) && locked){
65736 s[i].style.left = (pos-this.splitOffset) + "px";
65741 handleHiddenChange : function(colModel, colIndex, hidden){
65743 this.hideColumn(colIndex);
65745 this.unhideColumn(colIndex);
65749 hideColumn : function(colIndex){
65750 var cid = this.getColumnId(colIndex);
65751 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
65752 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
65754 this.updateHeaders();
65756 this.updateSplitters();
65760 unhideColumn : function(colIndex){
65761 var cid = this.getColumnId(colIndex);
65762 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
65763 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
65766 this.updateHeaders();
65768 this.updateSplitters();
65772 insertRows : function(dm, firstRow, lastRow, isUpdate){
65773 if(firstRow == 0 && lastRow == dm.getCount()-1){
65777 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
65779 var s = this.getScrollState();
65780 var markup = this.renderRows(firstRow, lastRow);
65781 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
65782 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
65783 this.restoreScroll(s);
65785 this.fireEvent("rowsinserted", this, firstRow, lastRow);
65786 this.syncRowHeights(firstRow, lastRow);
65787 this.stripeRows(firstRow);
65793 bufferRows : function(markup, target, index){
65794 var before = null, trows = target.rows, tbody = target.tBodies[0];
65795 if(index < trows.length){
65796 before = trows[index];
65798 var b = document.createElement("div");
65799 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
65800 var rows = b.firstChild.rows;
65801 for(var i = 0, len = rows.length; i < len; i++){
65803 tbody.insertBefore(rows[0], before);
65805 tbody.appendChild(rows[0]);
65812 deleteRows : function(dm, firstRow, lastRow){
65813 if(dm.getRowCount()<1){
65814 this.fireEvent("beforerefresh", this);
65815 this.mainBody.update("");
65816 this.lockedBody.update("");
65817 this.fireEvent("refresh", this);
65819 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
65820 var bt = this.getBodyTable();
65821 var tbody = bt.firstChild;
65822 var rows = bt.rows;
65823 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
65824 tbody.removeChild(rows[firstRow]);
65826 this.stripeRows(firstRow);
65827 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
65831 updateRows : function(dataSource, firstRow, lastRow){
65832 var s = this.getScrollState();
65834 this.restoreScroll(s);
65837 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
65841 this.updateHeaderSortState();
65844 getScrollState : function(){
65846 var sb = this.scroller.dom;
65847 return {left: sb.scrollLeft, top: sb.scrollTop};
65850 stripeRows : function(startRow){
65851 if(!this.grid.stripeRows || this.ds.getCount() < 1){
65854 startRow = startRow || 0;
65855 var rows = this.getBodyTable().rows;
65856 var lrows = this.getLockedTable().rows;
65857 var cls = ' x-grid-row-alt ';
65858 for(var i = startRow, len = rows.length; i < len; i++){
65859 var row = rows[i], lrow = lrows[i];
65860 var isAlt = ((i+1) % 2 == 0);
65861 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
65862 if(isAlt == hasAlt){
65866 row.className += " x-grid-row-alt";
65868 row.className = row.className.replace("x-grid-row-alt", "");
65871 lrow.className = row.className;
65876 restoreScroll : function(state){
65877 //Roo.log('GridView.restoreScroll');
65878 var sb = this.scroller.dom;
65879 sb.scrollLeft = state.left;
65880 sb.scrollTop = state.top;
65884 syncScroll : function(){
65885 //Roo.log('GridView.syncScroll');
65886 var sb = this.scroller.dom;
65887 var sh = this.mainHd.dom;
65888 var bs = this.mainBody.dom;
65889 var lv = this.lockedBody.dom;
65890 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
65891 lv.scrollTop = bs.scrollTop = sb.scrollTop;
65894 handleScroll : function(e){
65896 var sb = this.scroller.dom;
65897 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
65901 handleWheel : function(e){
65902 var d = e.getWheelDelta();
65903 this.scroller.dom.scrollTop -= d*22;
65904 // set this here to prevent jumpy scrolling on large tables
65905 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
65909 renderRows : function(startRow, endRow){
65910 // pull in all the crap needed to render rows
65911 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
65912 var colCount = cm.getColumnCount();
65914 if(ds.getCount() < 1){
65918 // build a map for all the columns
65920 for(var i = 0; i < colCount; i++){
65921 var name = cm.getDataIndex(i);
65923 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
65924 renderer : cm.getRenderer(i),
65925 id : cm.getColumnId(i),
65926 locked : cm.isLocked(i),
65927 has_editor : cm.isCellEditable(i)
65931 startRow = startRow || 0;
65932 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
65934 // records to render
65935 var rs = ds.getRange(startRow, endRow);
65937 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
65940 // As much as I hate to duplicate code, this was branched because FireFox really hates
65941 // [].join("") on strings. The performance difference was substantial enough to
65942 // branch this function
65943 doRender : Roo.isGecko ?
65944 function(cs, rs, ds, startRow, colCount, stripe){
65945 var ts = this.templates, ct = ts.cell, rt = ts.row;
65947 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
65949 var hasListener = this.grid.hasListener('rowclass');
65951 for(var j = 0, len = rs.length; j < len; j++){
65952 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
65953 for(var i = 0; i < colCount; i++){
65955 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
65957 p.css = p.attr = "";
65958 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
65959 if(p.value == undefined || p.value === "") {
65960 p.value = " ";
65963 p.css += ' x-grid-editable-cell';
65965 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
65966 p.css += ' x-grid-dirty-cell';
65968 var markup = ct.apply(p);
65976 if(stripe && ((rowIndex+1) % 2 == 0)){
65977 alt.push("x-grid-row-alt")
65980 alt.push( " x-grid-dirty-row");
65983 if(this.getRowClass){
65984 alt.push(this.getRowClass(r, rowIndex));
65990 rowIndex : rowIndex,
65993 this.grid.fireEvent('rowclass', this, rowcfg);
65994 alt.push(rowcfg.rowClass);
65996 rp.alt = alt.join(" ");
65997 lbuf+= rt.apply(rp);
65999 buf+= rt.apply(rp);
66001 return [lbuf, buf];
66003 function(cs, rs, ds, startRow, colCount, stripe){
66004 var ts = this.templates, ct = ts.cell, rt = ts.row;
66006 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
66007 var hasListener = this.grid.hasListener('rowclass');
66010 for(var j = 0, len = rs.length; j < len; j++){
66011 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
66012 for(var i = 0; i < colCount; i++){
66014 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
66016 p.css = p.attr = "";
66017 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
66018 if(p.value == undefined || p.value === "") {
66019 p.value = " ";
66023 p.css += ' x-grid-editable-cell';
66025 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
66026 p.css += ' x-grid-dirty-cell'
66029 var markup = ct.apply(p);
66031 cb[cb.length] = markup;
66033 lcb[lcb.length] = markup;
66037 if(stripe && ((rowIndex+1) % 2 == 0)){
66038 alt.push( "x-grid-row-alt");
66041 alt.push(" x-grid-dirty-row");
66044 if(this.getRowClass){
66045 alt.push( this.getRowClass(r, rowIndex));
66051 rowIndex : rowIndex,
66054 this.grid.fireEvent('rowclass', this, rowcfg);
66055 alt.push(rowcfg.rowClass);
66058 rp.alt = alt.join(" ");
66059 rp.cells = lcb.join("");
66060 lbuf[lbuf.length] = rt.apply(rp);
66061 rp.cells = cb.join("");
66062 buf[buf.length] = rt.apply(rp);
66064 return [lbuf.join(""), buf.join("")];
66067 renderBody : function(){
66068 var markup = this.renderRows();
66069 var bt = this.templates.body;
66070 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
66074 * Refreshes the grid
66075 * @param {Boolean} headersToo
66077 refresh : function(headersToo){
66078 this.fireEvent("beforerefresh", this);
66079 this.grid.stopEditing();
66080 var result = this.renderBody();
66081 this.lockedBody.update(result[0]);
66082 this.mainBody.update(result[1]);
66083 if(headersToo === true){
66084 this.updateHeaders();
66085 this.updateColumns();
66086 this.updateSplitters();
66087 this.updateHeaderSortState();
66089 this.syncRowHeights();
66091 this.fireEvent("refresh", this);
66094 handleColumnMove : function(cm, oldIndex, newIndex){
66095 this.indexMap = null;
66096 var s = this.getScrollState();
66097 this.refresh(true);
66098 this.restoreScroll(s);
66099 this.afterMove(newIndex);
66102 afterMove : function(colIndex){
66103 if(this.enableMoveAnim && Roo.enableFx){
66104 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
66106 // if multisort - fix sortOrder, and reload..
66107 if (this.grid.dataSource.multiSort) {
66108 // the we can call sort again..
66109 var dm = this.grid.dataSource;
66110 var cm = this.grid.colModel;
66112 for(var i = 0; i < cm.config.length; i++ ) {
66114 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
66115 continue; // dont' bother, it's not in sort list or being set.
66118 so.push(cm.config[i].dataIndex);
66121 dm.load(dm.lastOptions);
66128 updateCell : function(dm, rowIndex, dataIndex){
66129 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
66130 if(typeof colIndex == "undefined"){ // not present in grid
66133 var cm = this.grid.colModel;
66134 var cell = this.getCell(rowIndex, colIndex);
66135 var cellText = this.getCellText(rowIndex, colIndex);
66138 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
66139 id : cm.getColumnId(colIndex),
66140 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
66142 var renderer = cm.getRenderer(colIndex);
66143 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
66144 if(typeof val == "undefined" || val === "") {
66147 cellText.innerHTML = val;
66148 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
66149 this.syncRowHeights(rowIndex, rowIndex);
66152 calcColumnWidth : function(colIndex, maxRowsToMeasure){
66154 if(this.grid.autoSizeHeaders){
66155 var h = this.getHeaderCellMeasure(colIndex);
66156 maxWidth = Math.max(maxWidth, h.scrollWidth);
66159 if(this.cm.isLocked(colIndex)){
66160 tb = this.getLockedTable();
66163 tb = this.getBodyTable();
66164 index = colIndex - this.cm.getLockedCount();
66167 var rows = tb.rows;
66168 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
66169 for(var i = 0; i < stopIndex; i++){
66170 var cell = rows[i].childNodes[index].firstChild;
66171 maxWidth = Math.max(maxWidth, cell.scrollWidth);
66174 return maxWidth + /*margin for error in IE*/ 5;
66177 * Autofit a column to its content.
66178 * @param {Number} colIndex
66179 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
66181 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
66182 if(this.cm.isHidden(colIndex)){
66183 return; // can't calc a hidden column
66186 var cid = this.cm.getColumnId(colIndex);
66187 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
66188 if(this.grid.autoSizeHeaders){
66189 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
66192 var newWidth = this.calcColumnWidth(colIndex);
66193 this.cm.setColumnWidth(colIndex,
66194 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
66195 if(!suppressEvent){
66196 this.grid.fireEvent("columnresize", colIndex, newWidth);
66201 * Autofits all columns to their content and then expands to fit any extra space in the grid
66203 autoSizeColumns : function(){
66204 var cm = this.grid.colModel;
66205 var colCount = cm.getColumnCount();
66206 for(var i = 0; i < colCount; i++){
66207 this.autoSizeColumn(i, true, true);
66209 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
66212 this.updateColumns();
66218 * Autofits all columns to the grid's width proportionate with their current size
66219 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
66221 fitColumns : function(reserveScrollSpace){
66222 var cm = this.grid.colModel;
66223 var colCount = cm.getColumnCount();
66227 for (i = 0; i < colCount; i++){
66228 if(!cm.isHidden(i) && !cm.isFixed(i)){
66229 w = cm.getColumnWidth(i);
66235 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
66236 if(reserveScrollSpace){
66239 var frac = (avail - cm.getTotalWidth())/width;
66240 while (cols.length){
66243 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
66245 this.updateColumns();
66249 onRowSelect : function(rowIndex){
66250 var row = this.getRowComposite(rowIndex);
66251 row.addClass("x-grid-row-selected");
66254 onRowDeselect : function(rowIndex){
66255 var row = this.getRowComposite(rowIndex);
66256 row.removeClass("x-grid-row-selected");
66259 onCellSelect : function(row, col){
66260 var cell = this.getCell(row, col);
66262 Roo.fly(cell).addClass("x-grid-cell-selected");
66266 onCellDeselect : function(row, col){
66267 var cell = this.getCell(row, col);
66269 Roo.fly(cell).removeClass("x-grid-cell-selected");
66273 updateHeaderSortState : function(){
66275 // sort state can be single { field: xxx, direction : yyy}
66276 // or { xxx=>ASC , yyy : DESC ..... }
66279 if (!this.ds.multiSort) {
66280 var state = this.ds.getSortState();
66284 mstate[state.field] = state.direction;
66285 // FIXME... - this is not used here.. but might be elsewhere..
66286 this.sortState = state;
66289 mstate = this.ds.sortToggle;
66291 //remove existing sort classes..
66293 var sc = this.sortClasses;
66294 var hds = this.el.select(this.headerSelector).removeClass(sc);
66296 for(var f in mstate) {
66298 var sortColumn = this.cm.findColumnIndex(f);
66300 if(sortColumn != -1){
66301 var sortDir = mstate[f];
66302 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
66311 handleHeaderClick : function(g, index,e){
66313 Roo.log("header click");
66316 // touch events on header are handled by context
66317 this.handleHdCtx(g,index,e);
66322 if(this.headersDisabled){
66325 var dm = g.dataSource, cm = g.colModel;
66326 if(!cm.isSortable(index)){
66331 if (dm.multiSort) {
66332 // update the sortOrder
66334 for(var i = 0; i < cm.config.length; i++ ) {
66336 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
66337 continue; // dont' bother, it's not in sort list or being set.
66340 so.push(cm.config[i].dataIndex);
66346 dm.sort(cm.getDataIndex(index));
66350 destroy : function(){
66352 this.colMenu.removeAll();
66353 Roo.menu.MenuMgr.unregister(this.colMenu);
66354 this.colMenu.getEl().remove();
66355 delete this.colMenu;
66358 this.hmenu.removeAll();
66359 Roo.menu.MenuMgr.unregister(this.hmenu);
66360 this.hmenu.getEl().remove();
66363 if(this.grid.enableColumnMove){
66364 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
66366 for(var dd in dds){
66367 if(!dds[dd].config.isTarget && dds[dd].dragElId){
66368 var elid = dds[dd].dragElId;
66370 Roo.get(elid).remove();
66371 } else if(dds[dd].config.isTarget){
66372 dds[dd].proxyTop.remove();
66373 dds[dd].proxyBottom.remove();
66376 if(Roo.dd.DDM.locationCache[dd]){
66377 delete Roo.dd.DDM.locationCache[dd];
66380 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
66383 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
66384 this.bind(null, null);
66385 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
66388 handleLockChange : function(){
66389 this.refresh(true);
66392 onDenyColumnLock : function(){
66396 onDenyColumnHide : function(){
66400 handleHdMenuClick : function(item){
66401 var index = this.hdCtxIndex;
66402 var cm = this.cm, ds = this.ds;
66405 ds.sort(cm.getDataIndex(index), "ASC");
66408 ds.sort(cm.getDataIndex(index), "DESC");
66411 var lc = cm.getLockedCount();
66412 if(cm.getColumnCount(true) <= lc+1){
66413 this.onDenyColumnLock();
66417 cm.setLocked(index, true, true);
66418 cm.moveColumn(index, lc);
66419 this.grid.fireEvent("columnmove", index, lc);
66421 cm.setLocked(index, true);
66425 var lc = cm.getLockedCount();
66426 if((lc-1) != index){
66427 cm.setLocked(index, false, true);
66428 cm.moveColumn(index, lc-1);
66429 this.grid.fireEvent("columnmove", index, lc-1);
66431 cm.setLocked(index, false);
66434 case 'wider': // used to expand cols on touch..
66436 var cw = cm.getColumnWidth(index);
66437 cw += (item.id == 'wider' ? 1 : -1) * 50;
66438 cw = Math.max(0, cw);
66439 cw = Math.min(cw,4000);
66440 cm.setColumnWidth(index, cw);
66444 index = cm.getIndexById(item.id.substr(4));
66446 if(item.checked && cm.getColumnCount(true) <= 1){
66447 this.onDenyColumnHide();
66450 cm.setHidden(index, item.checked);
66456 beforeColMenuShow : function(){
66457 var cm = this.cm, colCount = cm.getColumnCount();
66458 this.colMenu.removeAll();
66461 for(var i = 0; i < colCount; i++){
66463 id: "col-"+cm.getColumnId(i),
66464 text: cm.getColumnHeader(i),
66465 checked: !cm.isHidden(i),
66470 if (this.grid.sortColMenu) {
66471 items.sort(function(a,b) {
66472 if (a.text == b.text) {
66475 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
66479 for(var i = 0; i < colCount; i++){
66480 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
66484 handleHdCtx : function(g, index, e){
66486 var hd = this.getHeaderCell(index);
66487 this.hdCtxIndex = index;
66488 var ms = this.hmenu.items, cm = this.cm;
66489 ms.get("asc").setDisabled(!cm.isSortable(index));
66490 ms.get("desc").setDisabled(!cm.isSortable(index));
66491 if(this.grid.enableColLock !== false){
66492 ms.get("lock").setDisabled(cm.isLocked(index));
66493 ms.get("unlock").setDisabled(!cm.isLocked(index));
66495 this.hmenu.show(hd, "tl-bl");
66498 handleHdOver : function(e){
66499 var hd = this.findHeaderCell(e.getTarget());
66500 if(hd && !this.headersDisabled){
66501 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
66502 this.fly(hd).addClass("x-grid-hd-over");
66507 handleHdOut : function(e){
66508 var hd = this.findHeaderCell(e.getTarget());
66510 this.fly(hd).removeClass("x-grid-hd-over");
66514 handleSplitDblClick : function(e, t){
66515 var i = this.getCellIndex(t);
66516 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
66517 this.autoSizeColumn(i, true);
66522 render : function(){
66525 var colCount = cm.getColumnCount();
66527 if(this.grid.monitorWindowResize === true){
66528 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
66530 var header = this.renderHeaders();
66531 var body = this.templates.body.apply({rows:""});
66532 var html = this.templates.master.apply({
66535 lockedHeader: header[0],
66539 //this.updateColumns();
66541 this.grid.getGridEl().dom.innerHTML = html;
66543 this.initElements();
66545 // a kludge to fix the random scolling effect in webkit
66546 this.el.on("scroll", function() {
66547 this.el.dom.scrollTop=0; // hopefully not recursive..
66550 this.scroller.on("scroll", this.handleScroll, this);
66551 this.lockedBody.on("mousewheel", this.handleWheel, this);
66552 this.mainBody.on("mousewheel", this.handleWheel, this);
66554 this.mainHd.on("mouseover", this.handleHdOver, this);
66555 this.mainHd.on("mouseout", this.handleHdOut, this);
66556 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
66557 {delegate: "."+this.splitClass});
66559 this.lockedHd.on("mouseover", this.handleHdOver, this);
66560 this.lockedHd.on("mouseout", this.handleHdOut, this);
66561 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
66562 {delegate: "."+this.splitClass});
66564 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
66565 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66568 this.updateSplitters();
66570 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
66571 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66572 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
66575 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
66576 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
66578 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
66579 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
66581 if(this.grid.enableColLock !== false){
66582 this.hmenu.add('-',
66583 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
66584 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
66588 this.hmenu.add('-',
66589 {id:"wider", text: this.columnsWiderText},
66590 {id:"narrow", text: this.columnsNarrowText }
66596 if(this.grid.enableColumnHide !== false){
66598 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
66599 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
66600 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
66602 this.hmenu.add('-',
66603 {id:"columns", text: this.columnsText, menu: this.colMenu}
66606 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
66608 this.grid.on("headercontextmenu", this.handleHdCtx, this);
66611 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
66612 this.dd = new Roo.grid.GridDragZone(this.grid, {
66613 ddGroup : this.grid.ddGroup || 'GridDD'
66619 for(var i = 0; i < colCount; i++){
66620 if(cm.isHidden(i)){
66621 this.hideColumn(i);
66623 if(cm.config[i].align){
66624 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
66625 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
66629 this.updateHeaderSortState();
66631 this.beforeInitialResize();
66634 // two part rendering gives faster view to the user
66635 this.renderPhase2.defer(1, this);
66638 renderPhase2 : function(){
66639 // render the rows now
66641 if(this.grid.autoSizeColumns){
66642 this.autoSizeColumns();
66646 beforeInitialResize : function(){
66650 onColumnSplitterMoved : function(i, w){
66651 this.userResized = true;
66652 var cm = this.grid.colModel;
66653 cm.setColumnWidth(i, w, true);
66654 var cid = cm.getColumnId(i);
66655 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
66656 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
66657 this.updateSplitters();
66659 this.grid.fireEvent("columnresize", i, w);
66662 syncRowHeights : function(startIndex, endIndex){
66663 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
66664 startIndex = startIndex || 0;
66665 var mrows = this.getBodyTable().rows;
66666 var lrows = this.getLockedTable().rows;
66667 var len = mrows.length-1;
66668 endIndex = Math.min(endIndex || len, len);
66669 for(var i = startIndex; i <= endIndex; i++){
66670 var m = mrows[i], l = lrows[i];
66671 var h = Math.max(m.offsetHeight, l.offsetHeight);
66672 m.style.height = l.style.height = h + "px";
66677 layout : function(initialRender, is2ndPass)
66680 var auto = g.autoHeight;
66681 var scrollOffset = 16;
66682 var c = g.getGridEl(), cm = this.cm,
66683 expandCol = g.autoExpandColumn,
66685 //c.beginMeasure();
66687 if(!c.dom.offsetWidth){ // display:none?
66689 this.lockedWrap.show();
66690 this.mainWrap.show();
66695 var hasLock = this.cm.isLocked(0);
66697 var tbh = this.headerPanel.getHeight();
66698 var bbh = this.footerPanel.getHeight();
66701 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
66702 var newHeight = ch + c.getBorderWidth("tb");
66704 newHeight = Math.min(g.maxHeight, newHeight);
66706 c.setHeight(newHeight);
66710 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
66713 var s = this.scroller;
66715 var csize = c.getSize(true);
66717 this.el.setSize(csize.width, csize.height);
66719 this.headerPanel.setWidth(csize.width);
66720 this.footerPanel.setWidth(csize.width);
66722 var hdHeight = this.mainHd.getHeight();
66723 var vw = csize.width;
66724 var vh = csize.height - (tbh + bbh);
66728 var bt = this.getBodyTable();
66730 if(cm.getLockedCount() == cm.config.length){
66731 bt = this.getLockedTable();
66734 var ltWidth = hasLock ?
66735 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
66737 var scrollHeight = bt.offsetHeight;
66738 var scrollWidth = ltWidth + bt.offsetWidth;
66739 var vscroll = false, hscroll = false;
66741 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
66743 var lw = this.lockedWrap, mw = this.mainWrap;
66744 var lb = this.lockedBody, mb = this.mainBody;
66746 setTimeout(function(){
66747 var t = s.dom.offsetTop;
66748 var w = s.dom.clientWidth,
66749 h = s.dom.clientHeight;
66752 lw.setSize(ltWidth, h);
66754 mw.setLeftTop(ltWidth, t);
66755 mw.setSize(w-ltWidth, h);
66757 lb.setHeight(h-hdHeight);
66758 mb.setHeight(h-hdHeight);
66760 if(is2ndPass !== true && !gv.userResized && expandCol){
66761 // high speed resize without full column calculation
66763 var ci = cm.getIndexById(expandCol);
66765 ci = cm.findColumnIndex(expandCol);
66767 ci = Math.max(0, ci); // make sure it's got at least the first col.
66768 var expandId = cm.getColumnId(ci);
66769 var tw = cm.getTotalWidth(false);
66770 var currentWidth = cm.getColumnWidth(ci);
66771 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
66772 if(currentWidth != cw){
66773 cm.setColumnWidth(ci, cw, true);
66774 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
66775 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
66776 gv.updateSplitters();
66777 gv.layout(false, true);
66789 onWindowResize : function(){
66790 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
66796 appendFooter : function(parentEl){
66800 sortAscText : "Sort Ascending",
66801 sortDescText : "Sort Descending",
66802 lockText : "Lock Column",
66803 unlockText : "Unlock Column",
66804 columnsText : "Columns",
66806 columnsWiderText : "Wider",
66807 columnsNarrowText : "Thinner"
66811 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
66812 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
66813 this.proxy.el.addClass('x-grid3-col-dd');
66816 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
66817 handleMouseDown : function(e){
66821 callHandleMouseDown : function(e){
66822 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
66827 * Ext JS Library 1.1.1
66828 * Copyright(c) 2006-2007, Ext JS, LLC.
66830 * Originally Released Under LGPL - original licence link has changed is not relivant.
66833 * <script type="text/javascript">
66836 * @extends Roo.dd.DDProxy
66837 * @class Roo.grid.SplitDragZone
66838 * Support for Column Header resizing
66840 * @param {Object} config
66843 // This is a support class used internally by the Grid components
66844 Roo.grid.SplitDragZone = function(grid, hd, hd2){
66846 this.view = grid.getView();
66847 this.proxy = this.view.resizeProxy;
66848 Roo.grid.SplitDragZone.superclass.constructor.call(
66851 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
66853 dragElId : Roo.id(this.proxy.dom),
66858 this.setHandleElId(Roo.id(hd));
66859 if (hd2 !== false) {
66860 this.setOuterHandleElId(Roo.id(hd2));
66863 this.scroll = false;
66865 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
66866 fly: Roo.Element.fly,
66868 b4StartDrag : function(x, y){
66869 this.view.headersDisabled = true;
66870 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
66871 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
66873 this.proxy.setHeight(h);
66875 // for old system colWidth really stored the actual width?
66876 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
66877 // which in reality did not work.. - it worked only for fixed sizes
66878 // for resizable we need to use actual sizes.
66879 var w = this.cm.getColumnWidth(this.cellIndex);
66880 if (!this.view.mainWrap) {
66882 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
66887 // this was w-this.grid.minColumnWidth;
66888 // doesnt really make sense? - w = thie curren width or the rendered one?
66889 var minw = Math.max(w-this.grid.minColumnWidth, 0);
66890 this.resetConstraints();
66891 this.setXConstraint(minw, 1000);
66892 this.setYConstraint(0, 0);
66893 this.minX = x - minw;
66894 this.maxX = x + 1000;
66896 if (!this.view.mainWrap) { // this is Bootstrap code..
66897 this.getDragEl().style.display='block';
66900 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
66904 handleMouseDown : function(e){
66905 ev = Roo.EventObject.setEvent(e);
66906 var t = this.fly(ev.getTarget());
66907 if(t.hasClass("x-grid-split")){
66908 this.cellIndex = this.view.getCellIndex(t.dom);
66909 this.split = t.dom;
66910 this.cm = this.grid.colModel;
66911 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
66912 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
66917 endDrag : function(e){
66918 this.view.headersDisabled = false;
66919 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
66920 var diff = endX - this.startPos;
66922 var w = this.cm.getColumnWidth(this.cellIndex);
66923 if (!this.view.mainWrap) {
66926 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
66929 autoOffset : function(){
66930 this.setDelta(0,0);
66934 * Ext JS Library 1.1.1
66935 * Copyright(c) 2006-2007, Ext JS, LLC.
66937 * Originally Released Under LGPL - original licence link has changed is not relivant.
66940 * <script type="text/javascript">
66944 // This is a support class used internally by the Grid components
66945 Roo.grid.GridDragZone = function(grid, config){
66946 this.view = grid.getView();
66947 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
66948 if(this.view.lockedBody){
66949 this.setHandleElId(Roo.id(this.view.mainBody.dom));
66950 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
66952 this.scroll = false;
66954 this.ddel = document.createElement('div');
66955 this.ddel.className = 'x-grid-dd-wrap';
66958 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
66959 ddGroup : "GridDD",
66961 getDragData : function(e){
66962 var t = Roo.lib.Event.getTarget(e);
66963 var rowIndex = this.view.findRowIndex(t);
66964 var sm = this.grid.selModel;
66966 //Roo.log(rowIndex);
66968 if (sm.getSelectedCell) {
66969 // cell selection..
66970 if (!sm.getSelectedCell()) {
66973 if (rowIndex != sm.getSelectedCell()[0]) {
66978 if (sm.getSelections && sm.getSelections().length < 1) {
66983 // before it used to all dragging of unseleted... - now we dont do that.
66984 if(rowIndex !== false){
66989 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
66991 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
66994 if (e.hasModifier()){
66995 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
66998 Roo.log("getDragData");
67003 rowIndex: rowIndex,
67004 selections: sm.getSelections ? sm.getSelections() : (
67005 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
67012 onInitDrag : function(e){
67013 var data = this.dragData;
67014 this.ddel.innerHTML = this.grid.getDragDropText();
67015 this.proxy.update(this.ddel);
67016 // fire start drag?
67019 afterRepair : function(){
67020 this.dragging = false;
67023 getRepairXY : function(e, data){
67027 onEndDrag : function(data, e){
67031 onValidDrop : function(dd, e, id){
67036 beforeInvalidDrop : function(e, id){
67041 * Ext JS Library 1.1.1
67042 * Copyright(c) 2006-2007, Ext JS, LLC.
67044 * Originally Released Under LGPL - original licence link has changed is not relivant.
67047 * <script type="text/javascript">
67052 * @class Roo.grid.ColumnModel
67053 * @extends Roo.util.Observable
67054 * This is the default implementation of a ColumnModel used by the Grid. It defines
67055 * the columns in the grid.
67058 var colModel = new Roo.grid.ColumnModel([
67059 {header: "Ticker", width: 60, sortable: true, locked: true},
67060 {header: "Company Name", width: 150, sortable: true},
67061 {header: "Market Cap.", width: 100, sortable: true},
67062 {header: "$ Sales", width: 100, sortable: true, renderer: money},
67063 {header: "Employees", width: 100, sortable: true, resizable: false}
67068 * The config options listed for this class are options which may appear in each
67069 * individual column definition.
67070 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
67072 * @param {Object} config An Array of column config objects. See this class's
67073 * config objects for details.
67075 Roo.grid.ColumnModel = function(config){
67077 * The config passed into the constructor
67079 this.config = []; //config;
67082 // if no id, create one
67083 // if the column does not have a dataIndex mapping,
67084 // map it to the order it is in the config
67085 for(var i = 0, len = config.length; i < len; i++){
67086 this.addColumn(config[i]);
67091 * The width of columns which have no width specified (defaults to 100)
67094 this.defaultWidth = 100;
67097 * Default sortable of columns which have no sortable specified (defaults to false)
67100 this.defaultSortable = false;
67104 * @event widthchange
67105 * Fires when the width of a column changes.
67106 * @param {ColumnModel} this
67107 * @param {Number} columnIndex The column index
67108 * @param {Number} newWidth The new width
67110 "widthchange": true,
67112 * @event headerchange
67113 * Fires when the text of a header changes.
67114 * @param {ColumnModel} this
67115 * @param {Number} columnIndex The column index
67116 * @param {Number} newText The new header text
67118 "headerchange": true,
67120 * @event hiddenchange
67121 * Fires when a column is hidden or "unhidden".
67122 * @param {ColumnModel} this
67123 * @param {Number} columnIndex The column index
67124 * @param {Boolean} hidden true if hidden, false otherwise
67126 "hiddenchange": true,
67128 * @event columnmoved
67129 * Fires when a column is moved.
67130 * @param {ColumnModel} this
67131 * @param {Number} oldIndex
67132 * @param {Number} newIndex
67134 "columnmoved" : true,
67136 * @event columlockchange
67137 * Fires when a column's locked state is changed
67138 * @param {ColumnModel} this
67139 * @param {Number} colIndex
67140 * @param {Boolean} locked true if locked
67142 "columnlockchange" : true
67144 Roo.grid.ColumnModel.superclass.constructor.call(this);
67146 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
67148 * @cfg {String} header [required] The header text to display in the Grid view.
67151 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
67154 * @cfg {String} smHeader Header at Bootsrap Small width
67157 * @cfg {String} mdHeader Header at Bootsrap Medium width
67160 * @cfg {String} lgHeader Header at Bootsrap Large width
67163 * @cfg {String} xlHeader Header at Bootsrap extra Large width
67166 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
67167 * {@link Roo.data.Record} definition from which to draw the column's value. If not
67168 * specified, the column's index is used as an index into the Record's data Array.
67171 * @cfg {Number} width The initial width in pixels of the column. Using this
67172 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
67175 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
67176 * Defaults to the value of the {@link #defaultSortable} property.
67177 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
67180 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
67183 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
67186 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
67189 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
67192 * @cfg {Function} renderer A function used to generate HTML markup for a cell
67193 * given the cell's data value. See {@link #setRenderer}. If not specified, the
67194 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
67195 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
67198 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
67201 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
67204 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
67207 * @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)
67210 * @cfg {String} tooltip mouse over tooltip text
67213 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
67216 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
67219 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
67222 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
67225 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
67228 * Returns the id of the column at the specified index.
67229 * @param {Number} index The column index
67230 * @return {String} the id
67232 getColumnId : function(index){
67233 return this.config[index].id;
67237 * Returns the column for a specified id.
67238 * @param {String} id The column id
67239 * @return {Object} the column
67241 getColumnById : function(id){
67242 return this.lookup[id];
67247 * Returns the column Object for a specified dataIndex.
67248 * @param {String} dataIndex The column dataIndex
67249 * @return {Object|Boolean} the column or false if not found
67251 getColumnByDataIndex: function(dataIndex){
67252 var index = this.findColumnIndex(dataIndex);
67253 return index > -1 ? this.config[index] : false;
67257 * Returns the index for a specified column id.
67258 * @param {String} id The column id
67259 * @return {Number} the index, or -1 if not found
67261 getIndexById : function(id){
67262 for(var i = 0, len = this.config.length; i < len; i++){
67263 if(this.config[i].id == id){
67271 * Returns the index for a specified column dataIndex.
67272 * @param {String} dataIndex The column dataIndex
67273 * @return {Number} the index, or -1 if not found
67276 findColumnIndex : function(dataIndex){
67277 for(var i = 0, len = this.config.length; i < len; i++){
67278 if(this.config[i].dataIndex == dataIndex){
67286 moveColumn : function(oldIndex, newIndex){
67287 var c = this.config[oldIndex];
67288 this.config.splice(oldIndex, 1);
67289 this.config.splice(newIndex, 0, c);
67290 this.dataMap = null;
67291 this.fireEvent("columnmoved", this, oldIndex, newIndex);
67294 isLocked : function(colIndex){
67295 return this.config[colIndex].locked === true;
67298 setLocked : function(colIndex, value, suppressEvent){
67299 if(this.isLocked(colIndex) == value){
67302 this.config[colIndex].locked = value;
67303 if(!suppressEvent){
67304 this.fireEvent("columnlockchange", this, colIndex, value);
67308 getTotalLockedWidth : function(){
67309 var totalWidth = 0;
67310 for(var i = 0; i < this.config.length; i++){
67311 if(this.isLocked(i) && !this.isHidden(i)){
67312 this.totalWidth += this.getColumnWidth(i);
67318 getLockedCount : function(){
67319 for(var i = 0, len = this.config.length; i < len; i++){
67320 if(!this.isLocked(i)){
67325 return this.config.length;
67329 * Returns the number of columns.
67332 getColumnCount : function(visibleOnly){
67333 if(visibleOnly === true){
67335 for(var i = 0, len = this.config.length; i < len; i++){
67336 if(!this.isHidden(i)){
67342 return this.config.length;
67346 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
67347 * @param {Function} fn
67348 * @param {Object} scope (optional)
67349 * @return {Array} result
67351 getColumnsBy : function(fn, scope){
67353 for(var i = 0, len = this.config.length; i < len; i++){
67354 var c = this.config[i];
67355 if(fn.call(scope||this, c, i) === true){
67363 * Returns true if the specified column is sortable.
67364 * @param {Number} col The column index
67365 * @return {Boolean}
67367 isSortable : function(col){
67368 if(typeof this.config[col].sortable == "undefined"){
67369 return this.defaultSortable;
67371 return this.config[col].sortable;
67375 * Returns the rendering (formatting) function defined for the column.
67376 * @param {Number} col The column index.
67377 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
67379 getRenderer : function(col){
67380 if(!this.config[col].renderer){
67381 return Roo.grid.ColumnModel.defaultRenderer;
67383 return this.config[col].renderer;
67387 * Sets the rendering (formatting) function for a column.
67388 * @param {Number} col The column index
67389 * @param {Function} fn The function to use to process the cell's raw data
67390 * to return HTML markup for the grid view. The render function is called with
67391 * the following parameters:<ul>
67392 * <li>Data value.</li>
67393 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
67394 * <li>css A CSS style string to apply to the table cell.</li>
67395 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
67396 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
67397 * <li>Row index</li>
67398 * <li>Column index</li>
67399 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
67401 setRenderer : function(col, fn){
67402 this.config[col].renderer = fn;
67406 * Returns the width for the specified column.
67407 * @param {Number} col The column index
67408 * @param (optional) {String} gridSize bootstrap width size.
67411 getColumnWidth : function(col, gridSize)
67413 var cfg = this.config[col];
67415 if (typeof(gridSize) == 'undefined') {
67416 return cfg.width * 1 || this.defaultWidth;
67418 if (gridSize === false) { // if we set it..
67419 return cfg.width || false;
67421 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
67423 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
67424 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
67427 return cfg[ sizes[i] ];
67434 * Sets the width for a column.
67435 * @param {Number} col The column index
67436 * @param {Number} width The new width
67438 setColumnWidth : function(col, width, suppressEvent){
67439 this.config[col].width = width;
67440 this.totalWidth = null;
67441 if(!suppressEvent){
67442 this.fireEvent("widthchange", this, col, width);
67447 * Returns the total width of all columns.
67448 * @param {Boolean} includeHidden True to include hidden column widths
67451 getTotalWidth : function(includeHidden){
67452 if(!this.totalWidth){
67453 this.totalWidth = 0;
67454 for(var i = 0, len = this.config.length; i < len; i++){
67455 if(includeHidden || !this.isHidden(i)){
67456 this.totalWidth += this.getColumnWidth(i);
67460 return this.totalWidth;
67464 * Returns the header for the specified column.
67465 * @param {Number} col The column index
67468 getColumnHeader : function(col){
67469 return this.config[col].header;
67473 * Sets the header for a column.
67474 * @param {Number} col The column index
67475 * @param {String} header The new header
67477 setColumnHeader : function(col, header){
67478 this.config[col].header = header;
67479 this.fireEvent("headerchange", this, col, header);
67483 * Returns the tooltip for the specified column.
67484 * @param {Number} col The column index
67487 getColumnTooltip : function(col){
67488 return this.config[col].tooltip;
67491 * Sets the tooltip for a column.
67492 * @param {Number} col The column index
67493 * @param {String} tooltip The new tooltip
67495 setColumnTooltip : function(col, tooltip){
67496 this.config[col].tooltip = tooltip;
67500 * Returns the dataIndex for the specified column.
67501 * @param {Number} col The column index
67504 getDataIndex : function(col){
67505 return this.config[col].dataIndex;
67509 * Sets the dataIndex for a column.
67510 * @param {Number} col The column index
67511 * @param {Number} dataIndex The new dataIndex
67513 setDataIndex : function(col, dataIndex){
67514 this.config[col].dataIndex = dataIndex;
67520 * Returns true if the cell is editable.
67521 * @param {Number} colIndex The column index
67522 * @param {Number} rowIndex The row index - this is nto actually used..?
67523 * @return {Boolean}
67525 isCellEditable : function(colIndex, rowIndex){
67526 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
67530 * Returns the editor defined for the cell/column.
67531 * return false or null to disable editing.
67532 * @param {Number} colIndex The column index
67533 * @param {Number} rowIndex The row index
67536 getCellEditor : function(colIndex, rowIndex){
67537 return this.config[colIndex].editor;
67541 * Sets if a column is editable.
67542 * @param {Number} col The column index
67543 * @param {Boolean} editable True if the column is editable
67545 setEditable : function(col, editable){
67546 this.config[col].editable = editable;
67551 * Returns true if the column is hidden.
67552 * @param {Number} colIndex The column index
67553 * @return {Boolean}
67555 isHidden : function(colIndex){
67556 return this.config[colIndex].hidden;
67561 * Returns true if the column width cannot be changed
67563 isFixed : function(colIndex){
67564 return this.config[colIndex].fixed;
67568 * Returns true if the column can be resized
67569 * @return {Boolean}
67571 isResizable : function(colIndex){
67572 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
67575 * Sets if a column is hidden.
67576 * @param {Number} colIndex The column index
67577 * @param {Boolean} hidden True if the column is hidden
67579 setHidden : function(colIndex, hidden){
67580 this.config[colIndex].hidden = hidden;
67581 this.totalWidth = null;
67582 this.fireEvent("hiddenchange", this, colIndex, hidden);
67586 * Sets the editor for a column.
67587 * @param {Number} col The column index
67588 * @param {Object} editor The editor object
67590 setEditor : function(col, editor){
67591 this.config[col].editor = editor;
67594 * Add a column (experimental...) - defaults to adding to the end..
67595 * @param {Object} config
67597 addColumn : function(c)
67600 var i = this.config.length;
67601 this.config[i] = c;
67603 if(typeof c.dataIndex == "undefined"){
67606 if(typeof c.renderer == "string"){
67607 c.renderer = Roo.util.Format[c.renderer];
67609 if(typeof c.id == "undefined"){
67612 if(c.editor && c.editor.xtype){
67613 c.editor = Roo.factory(c.editor, Roo.grid);
67615 if(c.editor && c.editor.isFormField){
67616 c.editor = new Roo.grid.GridEditor(c.editor);
67618 this.lookup[c.id] = c;
67623 Roo.grid.ColumnModel.defaultRenderer = function(value)
67625 if(typeof value == "object") {
67628 if(typeof value == "string" && value.length < 1){
67632 return String.format("{0}", value);
67635 // Alias for backwards compatibility
67636 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
67639 * Ext JS Library 1.1.1
67640 * Copyright(c) 2006-2007, Ext JS, LLC.
67642 * Originally Released Under LGPL - original licence link has changed is not relivant.
67645 * <script type="text/javascript">
67649 * @class Roo.grid.AbstractSelectionModel
67650 * @extends Roo.util.Observable
67652 * Abstract base class for grid SelectionModels. It provides the interface that should be
67653 * implemented by descendant classes. This class should not be directly instantiated.
67656 Roo.grid.AbstractSelectionModel = function(){
67657 this.locked = false;
67658 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
67661 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
67662 /** @ignore Called by the grid automatically. Do not call directly. */
67663 init : function(grid){
67669 * Locks the selections.
67672 this.locked = true;
67676 * Unlocks the selections.
67678 unlock : function(){
67679 this.locked = false;
67683 * Returns true if the selections are locked.
67684 * @return {Boolean}
67686 isLocked : function(){
67687 return this.locked;
67691 * Ext JS Library 1.1.1
67692 * Copyright(c) 2006-2007, Ext JS, LLC.
67694 * Originally Released Under LGPL - original licence link has changed is not relivant.
67697 * <script type="text/javascript">
67700 * @extends Roo.grid.AbstractSelectionModel
67701 * @class Roo.grid.RowSelectionModel
67702 * The default SelectionModel used by {@link Roo.grid.Grid}.
67703 * It supports multiple selections and keyboard selection/navigation.
67705 * @param {Object} config
67707 Roo.grid.RowSelectionModel = function(config){
67708 Roo.apply(this, config);
67709 this.selections = new Roo.util.MixedCollection(false, function(o){
67714 this.lastActive = false;
67718 * @event selectionchange
67719 * Fires when the selection changes
67720 * @param {SelectionModel} this
67722 "selectionchange" : true,
67724 * @event afterselectionchange
67725 * Fires after the selection changes (eg. by key press or clicking)
67726 * @param {SelectionModel} this
67728 "afterselectionchange" : true,
67730 * @event beforerowselect
67731 * Fires when a row is selected being selected, return false to cancel.
67732 * @param {SelectionModel} this
67733 * @param {Number} rowIndex The selected index
67734 * @param {Boolean} keepExisting False if other selections will be cleared
67736 "beforerowselect" : true,
67739 * Fires when a row is selected.
67740 * @param {SelectionModel} this
67741 * @param {Number} rowIndex The selected index
67742 * @param {Roo.data.Record} r The record
67744 "rowselect" : true,
67746 * @event rowdeselect
67747 * Fires when a row is deselected.
67748 * @param {SelectionModel} this
67749 * @param {Number} rowIndex The selected index
67751 "rowdeselect" : true
67753 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
67754 this.locked = false;
67757 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
67759 * @cfg {Boolean} singleSelect
67760 * True to allow selection of only one row at a time (defaults to false)
67762 singleSelect : false,
67765 initEvents : function(){
67767 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
67768 this.grid.on("mousedown", this.handleMouseDown, this);
67769 }else{ // allow click to work like normal
67770 this.grid.on("rowclick", this.handleDragableRowClick, this);
67772 // bootstrap does not have a view..
67773 var view = this.grid.view ? this.grid.view : this.grid;
67774 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
67775 "up" : function(e){
67777 this.selectPrevious(e.shiftKey);
67778 }else if(this.last !== false && this.lastActive !== false){
67779 var last = this.last;
67780 this.selectRange(this.last, this.lastActive-1);
67781 view.focusRow(this.lastActive);
67782 if(last !== false){
67786 this.selectFirstRow();
67788 this.fireEvent("afterselectionchange", this);
67790 "down" : function(e){
67792 this.selectNext(e.shiftKey);
67793 }else if(this.last !== false && this.lastActive !== false){
67794 var last = this.last;
67795 this.selectRange(this.last, this.lastActive+1);
67796 view.focusRow(this.lastActive);
67797 if(last !== false){
67801 this.selectFirstRow();
67803 this.fireEvent("afterselectionchange", this);
67809 view.on("refresh", this.onRefresh, this);
67810 view.on("rowupdated", this.onRowUpdated, this);
67811 view.on("rowremoved", this.onRemove, this);
67815 onRefresh : function(){
67816 var ds = this.grid.ds, i, v = this.grid.view;
67817 var s = this.selections;
67818 s.each(function(r){
67819 if((i = ds.indexOfId(r.id)) != -1){
67821 s.add(ds.getAt(i)); // updating the selection relate data
67829 onRemove : function(v, index, r){
67830 this.selections.remove(r);
67834 onRowUpdated : function(v, index, r){
67835 if(this.isSelected(r)){
67836 v.onRowSelect(index);
67842 * @param {Array} records The records to select
67843 * @param {Boolean} keepExisting (optional) True to keep existing selections
67845 selectRecords : function(records, keepExisting){
67847 this.clearSelections();
67849 var ds = this.grid.ds;
67850 for(var i = 0, len = records.length; i < len; i++){
67851 this.selectRow(ds.indexOf(records[i]), true);
67856 * Gets the number of selected rows.
67859 getCount : function(){
67860 return this.selections.length;
67864 * Selects the first row in the grid.
67866 selectFirstRow : function(){
67871 * Select the last row.
67872 * @param {Boolean} keepExisting (optional) True to keep existing selections
67874 selectLastRow : function(keepExisting){
67875 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
67879 * Selects the row immediately following the last selected row.
67880 * @param {Boolean} keepExisting (optional) True to keep existing selections
67882 selectNext : function(keepExisting){
67883 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
67884 this.selectRow(this.last+1, keepExisting);
67885 var view = this.grid.view ? this.grid.view : this.grid;
67886 view.focusRow(this.last);
67891 * Selects the row that precedes the last selected row.
67892 * @param {Boolean} keepExisting (optional) True to keep existing selections
67894 selectPrevious : function(keepExisting){
67896 this.selectRow(this.last-1, keepExisting);
67897 var view = this.grid.view ? this.grid.view : this.grid;
67898 view.focusRow(this.last);
67903 * Returns the selected records
67904 * @return {Array} Array of selected records
67906 getSelections : function(){
67907 return [].concat(this.selections.items);
67911 * Returns the first selected record.
67914 getSelected : function(){
67915 return this.selections.itemAt(0);
67920 * Clears all selections.
67922 clearSelections : function(fast){
67927 var ds = this.grid.ds;
67928 var s = this.selections;
67929 s.each(function(r){
67930 this.deselectRow(ds.indexOfId(r.id));
67934 this.selections.clear();
67941 * Selects all rows.
67943 selectAll : function(){
67947 this.selections.clear();
67948 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
67949 this.selectRow(i, true);
67954 * Returns True if there is a selection.
67955 * @return {Boolean}
67957 hasSelection : function(){
67958 return this.selections.length > 0;
67962 * Returns True if the specified row is selected.
67963 * @param {Number/Record} record The record or index of the record to check
67964 * @return {Boolean}
67966 isSelected : function(index){
67967 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
67968 return (r && this.selections.key(r.id) ? true : false);
67972 * Returns True if the specified record id is selected.
67973 * @param {String} id The id of record to check
67974 * @return {Boolean}
67976 isIdSelected : function(id){
67977 return (this.selections.key(id) ? true : false);
67981 handleMouseDown : function(e, t)
67983 var view = this.grid.view ? this.grid.view : this.grid;
67985 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
67988 if(e.shiftKey && this.last !== false){
67989 var last = this.last;
67990 this.selectRange(last, rowIndex, e.ctrlKey);
67991 this.last = last; // reset the last
67992 view.focusRow(rowIndex);
67994 var isSelected = this.isSelected(rowIndex);
67995 if(e.button !== 0 && isSelected){
67996 view.focusRow(rowIndex);
67997 }else if(e.ctrlKey && isSelected){
67998 this.deselectRow(rowIndex);
67999 }else if(!isSelected){
68000 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
68001 view.focusRow(rowIndex);
68004 this.fireEvent("afterselectionchange", this);
68007 handleDragableRowClick : function(grid, rowIndex, e)
68009 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
68010 this.selectRow(rowIndex, false);
68011 var view = this.grid.view ? this.grid.view : this.grid;
68012 view.focusRow(rowIndex);
68013 this.fireEvent("afterselectionchange", this);
68018 * Selects multiple rows.
68019 * @param {Array} rows Array of the indexes of the row to select
68020 * @param {Boolean} keepExisting (optional) True to keep existing selections
68022 selectRows : function(rows, keepExisting){
68024 this.clearSelections();
68026 for(var i = 0, len = rows.length; i < len; i++){
68027 this.selectRow(rows[i], true);
68032 * Selects a range of rows. All rows in between startRow and endRow are also selected.
68033 * @param {Number} startRow The index of the first row in the range
68034 * @param {Number} endRow The index of the last row in the range
68035 * @param {Boolean} keepExisting (optional) True to retain existing selections
68037 selectRange : function(startRow, endRow, keepExisting){
68042 this.clearSelections();
68044 if(startRow <= endRow){
68045 for(var i = startRow; i <= endRow; i++){
68046 this.selectRow(i, true);
68049 for(var i = startRow; i >= endRow; i--){
68050 this.selectRow(i, true);
68056 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
68057 * @param {Number} startRow The index of the first row in the range
68058 * @param {Number} endRow The index of the last row in the range
68060 deselectRange : function(startRow, endRow, preventViewNotify){
68064 for(var i = startRow; i <= endRow; i++){
68065 this.deselectRow(i, preventViewNotify);
68071 * @param {Number} row The index of the row to select
68072 * @param {Boolean} keepExisting (optional) True to keep existing selections
68074 selectRow : function(index, keepExisting, preventViewNotify){
68075 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
68078 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
68079 if(!keepExisting || this.singleSelect){
68080 this.clearSelections();
68082 var r = this.grid.ds.getAt(index);
68083 this.selections.add(r);
68084 this.last = this.lastActive = index;
68085 if(!preventViewNotify){
68086 var view = this.grid.view ? this.grid.view : this.grid;
68087 view.onRowSelect(index);
68089 this.fireEvent("rowselect", this, index, r);
68090 this.fireEvent("selectionchange", this);
68096 * @param {Number} row The index of the row to deselect
68098 deselectRow : function(index, preventViewNotify){
68102 if(this.last == index){
68105 if(this.lastActive == index){
68106 this.lastActive = false;
68108 var r = this.grid.ds.getAt(index);
68109 this.selections.remove(r);
68110 if(!preventViewNotify){
68111 var view = this.grid.view ? this.grid.view : this.grid;
68112 view.onRowDeselect(index);
68114 this.fireEvent("rowdeselect", this, index);
68115 this.fireEvent("selectionchange", this);
68119 restoreLast : function(){
68121 this.last = this._last;
68126 acceptsNav : function(row, col, cm){
68127 return !cm.isHidden(col) && cm.isCellEditable(col, row);
68131 onEditorKey : function(field, e){
68132 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
68137 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
68139 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68141 }else if(k == e.ENTER && !e.ctrlKey){
68145 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
68147 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
68149 }else if(k == e.ESC){
68153 g.startEditing(newCell[0], newCell[1]);
68158 * Ext JS Library 1.1.1
68159 * Copyright(c) 2006-2007, Ext JS, LLC.
68161 * Originally Released Under LGPL - original licence link has changed is not relivant.
68164 * <script type="text/javascript">
68167 * @class Roo.grid.CellSelectionModel
68168 * @extends Roo.grid.AbstractSelectionModel
68169 * This class provides the basic implementation for cell selection in a grid.
68171 * @param {Object} config The object containing the configuration of this model.
68172 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
68174 Roo.grid.CellSelectionModel = function(config){
68175 Roo.apply(this, config);
68177 this.selection = null;
68181 * @event beforerowselect
68182 * Fires before a cell is selected.
68183 * @param {SelectionModel} this
68184 * @param {Number} rowIndex The selected row index
68185 * @param {Number} colIndex The selected cell index
68187 "beforecellselect" : true,
68189 * @event cellselect
68190 * Fires when a cell is selected.
68191 * @param {SelectionModel} this
68192 * @param {Number} rowIndex The selected row index
68193 * @param {Number} colIndex The selected cell index
68195 "cellselect" : true,
68197 * @event selectionchange
68198 * Fires when the active selection changes.
68199 * @param {SelectionModel} this
68200 * @param {Object} selection null for no selection or an object (o) with two properties
68202 <li>o.record: the record object for the row the selection is in</li>
68203 <li>o.cell: An array of [rowIndex, columnIndex]</li>
68206 "selectionchange" : true,
68209 * Fires when the tab (or enter) was pressed on the last editable cell
68210 * You can use this to trigger add new row.
68211 * @param {SelectionModel} this
68215 * @event beforeeditnext
68216 * Fires before the next editable sell is made active
68217 * You can use this to skip to another cell or fire the tabend
68218 * if you set cell to false
68219 * @param {Object} eventdata object : { cell : [ row, col ] }
68221 "beforeeditnext" : true
68223 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
68226 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
68228 enter_is_tab: false,
68231 initEvents : function(){
68232 this.grid.on("mousedown", this.handleMouseDown, this);
68233 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
68234 var view = this.grid.view;
68235 view.on("refresh", this.onViewChange, this);
68236 view.on("rowupdated", this.onRowUpdated, this);
68237 view.on("beforerowremoved", this.clearSelections, this);
68238 view.on("beforerowsinserted", this.clearSelections, this);
68239 if(this.grid.isEditor){
68240 this.grid.on("beforeedit", this.beforeEdit, this);
68245 beforeEdit : function(e){
68246 this.select(e.row, e.column, false, true, e.record);
68250 onRowUpdated : function(v, index, r){
68251 if(this.selection && this.selection.record == r){
68252 v.onCellSelect(index, this.selection.cell[1]);
68257 onViewChange : function(){
68258 this.clearSelections(true);
68262 * Returns the currently selected cell,.
68263 * @return {Array} The selected cell (row, column) or null if none selected.
68265 getSelectedCell : function(){
68266 return this.selection ? this.selection.cell : null;
68270 * Clears all selections.
68271 * @param {Boolean} true to prevent the gridview from being notified about the change.
68273 clearSelections : function(preventNotify){
68274 var s = this.selection;
68276 if(preventNotify !== true){
68277 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
68279 this.selection = null;
68280 this.fireEvent("selectionchange", this, null);
68285 * Returns true if there is a selection.
68286 * @return {Boolean}
68288 hasSelection : function(){
68289 return this.selection ? true : false;
68293 handleMouseDown : function(e, t){
68294 var v = this.grid.getView();
68295 if(this.isLocked()){
68298 var row = v.findRowIndex(t);
68299 var cell = v.findCellIndex(t);
68300 if(row !== false && cell !== false){
68301 this.select(row, cell);
68307 * @param {Number} rowIndex
68308 * @param {Number} collIndex
68310 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
68311 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
68312 this.clearSelections();
68313 r = r || this.grid.dataSource.getAt(rowIndex);
68316 cell : [rowIndex, colIndex]
68318 if(!preventViewNotify){
68319 var v = this.grid.getView();
68320 v.onCellSelect(rowIndex, colIndex);
68321 if(preventFocus !== true){
68322 v.focusCell(rowIndex, colIndex);
68325 this.fireEvent("cellselect", this, rowIndex, colIndex);
68326 this.fireEvent("selectionchange", this, this.selection);
68331 isSelectable : function(rowIndex, colIndex, cm){
68332 return !cm.isHidden(colIndex);
68336 handleKeyDown : function(e){
68337 //Roo.log('Cell Sel Model handleKeyDown');
68338 if(!e.isNavKeyPress()){
68341 var g = this.grid, s = this.selection;
68344 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
68346 this.select(cell[0], cell[1]);
68351 var walk = function(row, col, step){
68352 return g.walkCells(row, col, step, sm.isSelectable, sm);
68354 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
68361 // handled by onEditorKey
68362 if (g.isEditor && g.editing) {
68366 newCell = walk(r, c-1, -1);
68368 newCell = walk(r, c+1, 1);
68373 newCell = walk(r+1, c, 1);
68377 newCell = walk(r-1, c, -1);
68381 newCell = walk(r, c+1, 1);
68385 newCell = walk(r, c-1, -1);
68390 if(g.isEditor && !g.editing){
68391 g.startEditing(r, c);
68400 this.select(newCell[0], newCell[1]);
68406 acceptsNav : function(row, col, cm){
68407 return !cm.isHidden(col) && cm.isCellEditable(col, row);
68411 * @param {Number} field (not used) - as it's normally used as a listener
68412 * @param {Number} e - event - fake it by using
68414 * var e = Roo.EventObjectImpl.prototype;
68415 * e.keyCode = e.TAB
68419 onEditorKey : function(field, e){
68421 var k = e.getKey(),
68424 ed = g.activeEditor,
68426 ///Roo.log('onEditorKey' + k);
68429 if (this.enter_is_tab && k == e.ENTER) {
68435 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
68437 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68443 } else if(k == e.ENTER && !e.ctrlKey){
68446 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68448 } else if(k == e.ESC){
68453 var ecall = { cell : newCell, forward : forward };
68454 this.fireEvent('beforeeditnext', ecall );
68455 newCell = ecall.cell;
68456 forward = ecall.forward;
68460 //Roo.log('next cell after edit');
68461 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
68462 } else if (forward) {
68463 // tabbed past last
68464 this.fireEvent.defer(100, this, ['tabend',this]);
68469 * Ext JS Library 1.1.1
68470 * Copyright(c) 2006-2007, Ext JS, LLC.
68472 * Originally Released Under LGPL - original licence link has changed is not relivant.
68475 * <script type="text/javascript">
68479 * @class Roo.grid.EditorGrid
68480 * @extends Roo.grid.Grid
68481 * Class for creating and editable grid.
68482 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
68483 * The container MUST have some type of size defined for the grid to fill. The container will be
68484 * automatically set to position relative if it isn't already.
68485 * @param {Object} dataSource The data model to bind to
68486 * @param {Object} colModel The column model with info about this grid's columns
68488 Roo.grid.EditorGrid = function(container, config){
68489 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
68490 this.getGridEl().addClass("xedit-grid");
68492 if(!this.selModel){
68493 this.selModel = new Roo.grid.CellSelectionModel();
68496 this.activeEditor = null;
68500 * @event beforeedit
68501 * Fires before cell editing is triggered. The edit event object has the following properties <br />
68502 * <ul style="padding:5px;padding-left:16px;">
68503 * <li>grid - This grid</li>
68504 * <li>record - The record being edited</li>
68505 * <li>field - The field name being edited</li>
68506 * <li>value - The value for the field being edited.</li>
68507 * <li>row - The grid row index</li>
68508 * <li>column - The grid column index</li>
68509 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
68511 * @param {Object} e An edit event (see above for description)
68513 "beforeedit" : true,
68516 * Fires after a cell is edited. <br />
68517 * <ul style="padding:5px;padding-left:16px;">
68518 * <li>grid - This grid</li>
68519 * <li>record - The record being edited</li>
68520 * <li>field - The field name being edited</li>
68521 * <li>value - The value being set</li>
68522 * <li>originalValue - The original value for the field, before the edit.</li>
68523 * <li>row - The grid row index</li>
68524 * <li>column - The grid column index</li>
68526 * @param {Object} e An edit event (see above for description)
68528 "afteredit" : true,
68530 * @event validateedit
68531 * Fires after a cell is edited, but before the value is set in the record.
68532 * You can use this to modify the value being set in the field, Return false
68533 * to cancel the change. The edit event object has the following properties <br />
68534 * <ul style="padding:5px;padding-left:16px;">
68535 * <li>editor - This editor</li>
68536 * <li>grid - This grid</li>
68537 * <li>record - The record being edited</li>
68538 * <li>field - The field name being edited</li>
68539 * <li>value - The value being set</li>
68540 * <li>originalValue - The original value for the field, before the edit.</li>
68541 * <li>row - The grid row index</li>
68542 * <li>column - The grid column index</li>
68543 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
68545 * @param {Object} e An edit event (see above for description)
68547 "validateedit" : true
68549 this.on("bodyscroll", this.stopEditing, this);
68550 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
68553 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
68555 * @cfg {Number} clicksToEdit
68556 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
68563 trackMouseOver: false, // causes very odd FF errors
68565 onCellDblClick : function(g, row, col){
68566 this.startEditing(row, col);
68569 onEditComplete : function(ed, value, startValue){
68570 this.editing = false;
68571 this.activeEditor = null;
68572 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
68574 var field = this.colModel.getDataIndex(ed.col);
68579 originalValue: startValue,
68586 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
68589 if(String(value) !== String(startValue)){
68591 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
68592 r.set(field, e.value);
68593 // if we are dealing with a combo box..
68594 // then we also set the 'name' colum to be the displayField
68595 if (ed.field.displayField && ed.field.name) {
68596 r.set(ed.field.name, ed.field.el.dom.value);
68599 delete e.cancel; //?? why!!!
68600 this.fireEvent("afteredit", e);
68603 this.fireEvent("afteredit", e); // always fire it!
68605 this.view.focusCell(ed.row, ed.col);
68609 * Starts editing the specified for the specified row/column
68610 * @param {Number} rowIndex
68611 * @param {Number} colIndex
68613 startEditing : function(row, col){
68614 this.stopEditing();
68615 if(this.colModel.isCellEditable(col, row)){
68616 this.view.ensureVisible(row, col, true);
68618 var r = this.dataSource.getAt(row);
68619 var field = this.colModel.getDataIndex(col);
68620 var cell = Roo.get(this.view.getCell(row,col));
68625 value: r.data[field],
68630 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
68631 this.editing = true;
68632 var ed = this.colModel.getCellEditor(col, row);
68638 ed.render(ed.parentEl || document.body);
68644 (function(){ // complex but required for focus issues in safari, ie and opera
68648 ed.on("complete", this.onEditComplete, this, {single: true});
68649 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
68650 this.activeEditor = ed;
68651 var v = r.data[field];
68652 ed.startEdit(this.view.getCell(row, col), v);
68653 // combo's with 'displayField and name set
68654 if (ed.field.displayField && ed.field.name) {
68655 ed.field.el.dom.value = r.data[ed.field.name];
68659 }).defer(50, this);
68665 * Stops any active editing
68667 stopEditing : function(){
68668 if(this.activeEditor){
68669 this.activeEditor.completeEdit();
68671 this.activeEditor = null;
68675 * Called to get grid's drag proxy text, by default returns this.ddText.
68678 getDragDropText : function(){
68679 var count = this.selModel.getSelectedCell() ? 1 : 0;
68680 return String.format(this.ddText, count, count == 1 ? '' : 's');
68685 * Ext JS Library 1.1.1
68686 * Copyright(c) 2006-2007, Ext JS, LLC.
68688 * Originally Released Under LGPL - original licence link has changed is not relivant.
68691 * <script type="text/javascript">
68694 // private - not really -- you end up using it !
68695 // This is a support class used internally by the Grid components
68698 * @class Roo.grid.GridEditor
68699 * @extends Roo.Editor
68700 * Class for creating and editable grid elements.
68701 * @param {Object} config any settings (must include field)
68703 Roo.grid.GridEditor = function(field, config){
68704 if (!config && field.field) {
68706 field = Roo.factory(config.field, Roo.form);
68708 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
68709 field.monitorTab = false;
68712 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
68715 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
68718 alignment: "tl-tl",
68721 cls: "x-small-editor x-grid-editor",
68726 * Ext JS Library 1.1.1
68727 * Copyright(c) 2006-2007, Ext JS, LLC.
68729 * Originally Released Under LGPL - original licence link has changed is not relivant.
68732 * <script type="text/javascript">
68737 Roo.grid.PropertyRecord = Roo.data.Record.create([
68738 {name:'name',type:'string'}, 'value'
68742 Roo.grid.PropertyStore = function(grid, source){
68744 this.store = new Roo.data.Store({
68745 recordType : Roo.grid.PropertyRecord
68747 this.store.on('update', this.onUpdate, this);
68749 this.setSource(source);
68751 Roo.grid.PropertyStore.superclass.constructor.call(this);
68756 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
68757 setSource : function(o){
68759 this.store.removeAll();
68762 if(this.isEditableValue(o[k])){
68763 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
68766 this.store.loadRecords({records: data}, {}, true);
68769 onUpdate : function(ds, record, type){
68770 if(type == Roo.data.Record.EDIT){
68771 var v = record.data['value'];
68772 var oldValue = record.modified['value'];
68773 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
68774 this.source[record.id] = v;
68776 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
68783 getProperty : function(row){
68784 return this.store.getAt(row);
68787 isEditableValue: function(val){
68788 if(val && val instanceof Date){
68790 }else if(typeof val == 'object' || typeof val == 'function'){
68796 setValue : function(prop, value){
68797 this.source[prop] = value;
68798 this.store.getById(prop).set('value', value);
68801 getSource : function(){
68802 return this.source;
68806 Roo.grid.PropertyColumnModel = function(grid, store){
68809 g.PropertyColumnModel.superclass.constructor.call(this, [
68810 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
68811 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
68813 this.store = store;
68814 this.bselect = Roo.DomHelper.append(document.body, {
68815 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
68816 {tag: 'option', value: 'true', html: 'true'},
68817 {tag: 'option', value: 'false', html: 'false'}
68820 Roo.id(this.bselect);
68823 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
68824 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
68825 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
68826 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
68827 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
68829 this.renderCellDelegate = this.renderCell.createDelegate(this);
68830 this.renderPropDelegate = this.renderProp.createDelegate(this);
68833 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
68837 valueText : 'Value',
68839 dateFormat : 'm/j/Y',
68842 renderDate : function(dateVal){
68843 return dateVal.dateFormat(this.dateFormat);
68846 renderBool : function(bVal){
68847 return bVal ? 'true' : 'false';
68850 isCellEditable : function(colIndex, rowIndex){
68851 return colIndex == 1;
68854 getRenderer : function(col){
68856 this.renderCellDelegate : this.renderPropDelegate;
68859 renderProp : function(v){
68860 return this.getPropertyName(v);
68863 renderCell : function(val){
68865 if(val instanceof Date){
68866 rv = this.renderDate(val);
68867 }else if(typeof val == 'boolean'){
68868 rv = this.renderBool(val);
68870 return Roo.util.Format.htmlEncode(rv);
68873 getPropertyName : function(name){
68874 var pn = this.grid.propertyNames;
68875 return pn && pn[name] ? pn[name] : name;
68878 getCellEditor : function(colIndex, rowIndex){
68879 var p = this.store.getProperty(rowIndex);
68880 var n = p.data['name'], val = p.data['value'];
68882 if(typeof(this.grid.customEditors[n]) == 'string'){
68883 return this.editors[this.grid.customEditors[n]];
68885 if(typeof(this.grid.customEditors[n]) != 'undefined'){
68886 return this.grid.customEditors[n];
68888 if(val instanceof Date){
68889 return this.editors['date'];
68890 }else if(typeof val == 'number'){
68891 return this.editors['number'];
68892 }else if(typeof val == 'boolean'){
68893 return this.editors['boolean'];
68895 return this.editors['string'];
68901 * @class Roo.grid.PropertyGrid
68902 * @extends Roo.grid.EditorGrid
68903 * This class represents the interface of a component based property grid control.
68904 * <br><br>Usage:<pre><code>
68905 var grid = new Roo.grid.PropertyGrid("my-container-id", {
68913 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
68914 * The container MUST have some type of size defined for the grid to fill. The container will be
68915 * automatically set to position relative if it isn't already.
68916 * @param {Object} config A config object that sets properties on this grid.
68918 Roo.grid.PropertyGrid = function(container, config){
68919 config = config || {};
68920 var store = new Roo.grid.PropertyStore(this);
68921 this.store = store;
68922 var cm = new Roo.grid.PropertyColumnModel(this, store);
68923 store.store.sort('name', 'ASC');
68924 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
68927 enableColLock:false,
68928 enableColumnMove:false,
68930 trackMouseOver: false,
68933 this.getGridEl().addClass('x-props-grid');
68934 this.lastEditRow = null;
68935 this.on('columnresize', this.onColumnResize, this);
68938 * @event beforepropertychange
68939 * Fires before a property changes (return false to stop?)
68940 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
68941 * @param {String} id Record Id
68942 * @param {String} newval New Value
68943 * @param {String} oldval Old Value
68945 "beforepropertychange": true,
68947 * @event propertychange
68948 * Fires after a property changes
68949 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
68950 * @param {String} id Record Id
68951 * @param {String} newval New Value
68952 * @param {String} oldval Old Value
68954 "propertychange": true
68956 this.customEditors = this.customEditors || {};
68958 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
68961 * @cfg {Object} customEditors map of colnames=> custom editors.
68962 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
68963 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
68964 * false disables editing of the field.
68968 * @cfg {Object} propertyNames map of property Names to their displayed value
68971 render : function(){
68972 Roo.grid.PropertyGrid.superclass.render.call(this);
68973 this.autoSize.defer(100, this);
68976 autoSize : function(){
68977 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
68979 this.view.fitColumns();
68983 onColumnResize : function(){
68984 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
68988 * Sets the data for the Grid
68989 * accepts a Key => Value object of all the elements avaiable.
68990 * @param {Object} data to appear in grid.
68992 setSource : function(source){
68993 this.store.setSource(source);
68997 * Gets all the data from the grid.
68998 * @return {Object} data data stored in grid
69000 getSource : function(){
69001 return this.store.getSource();
69010 * @class Roo.grid.Calendar
69011 * @extends Roo.grid.Grid
69012 * This class extends the Grid to provide a calendar widget
69013 * <br><br>Usage:<pre><code>
69014 var grid = new Roo.grid.Calendar("my-container-id", {
69017 selModel: mySelectionModel,
69018 autoSizeColumns: true,
69019 monitorWindowResize: false,
69020 trackMouseOver: true
69021 eventstore : real data store..
69027 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
69028 * The container MUST have some type of size defined for the grid to fill. The container will be
69029 * automatically set to position relative if it isn't already.
69030 * @param {Object} config A config object that sets properties on this grid.
69032 Roo.grid.Calendar = function(container, config){
69033 // initialize the container
69034 this.container = Roo.get(container);
69035 this.container.update("");
69036 this.container.setStyle("overflow", "hidden");
69037 this.container.addClass('x-grid-container');
69039 this.id = this.container.id;
69041 Roo.apply(this, config);
69042 // check and correct shorthanded configs
69046 for (var r = 0;r < 6;r++) {
69049 for (var c =0;c < 7;c++) {
69053 if (this.eventStore) {
69054 this.eventStore= Roo.factory(this.eventStore, Roo.data);
69055 this.eventStore.on('load',this.onLoad, this);
69056 this.eventStore.on('beforeload',this.clearEvents, this);
69060 this.dataSource = new Roo.data.Store({
69061 proxy: new Roo.data.MemoryProxy(rows),
69062 reader: new Roo.data.ArrayReader({}, [
69063 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
69066 this.dataSource.load();
69067 this.ds = this.dataSource;
69068 this.ds.xmodule = this.xmodule || false;
69071 var cellRender = function(v,x,r)
69073 return String.format(
69074 '<div class="fc-day fc-widget-content"><div>' +
69075 '<div class="fc-event-container"></div>' +
69076 '<div class="fc-day-number">{0}</div>'+
69078 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
69079 '</div></div>', v);
69084 this.colModel = new Roo.grid.ColumnModel( [
69086 xtype: 'ColumnModel',
69088 dataIndex : 'weekday0',
69090 renderer : cellRender
69093 xtype: 'ColumnModel',
69095 dataIndex : 'weekday1',
69097 renderer : cellRender
69100 xtype: 'ColumnModel',
69102 dataIndex : 'weekday2',
69103 header : 'Tuesday',
69104 renderer : cellRender
69107 xtype: 'ColumnModel',
69109 dataIndex : 'weekday3',
69110 header : 'Wednesday',
69111 renderer : cellRender
69114 xtype: 'ColumnModel',
69116 dataIndex : 'weekday4',
69117 header : 'Thursday',
69118 renderer : cellRender
69121 xtype: 'ColumnModel',
69123 dataIndex : 'weekday5',
69125 renderer : cellRender
69128 xtype: 'ColumnModel',
69130 dataIndex : 'weekday6',
69131 header : 'Saturday',
69132 renderer : cellRender
69135 this.cm = this.colModel;
69136 this.cm.xmodule = this.xmodule || false;
69140 //this.selModel = new Roo.grid.CellSelectionModel();
69141 //this.sm = this.selModel;
69142 //this.selModel.init(this);
69146 this.container.setWidth(this.width);
69150 this.container.setHeight(this.height);
69157 * The raw click event for the entire grid.
69158 * @param {Roo.EventObject} e
69163 * The raw dblclick event for the entire grid.
69164 * @param {Roo.EventObject} e
69168 * @event contextmenu
69169 * The raw contextmenu event for the entire grid.
69170 * @param {Roo.EventObject} e
69172 "contextmenu" : true,
69175 * The raw mousedown event for the entire grid.
69176 * @param {Roo.EventObject} e
69178 "mousedown" : true,
69181 * The raw mouseup event for the entire grid.
69182 * @param {Roo.EventObject} e
69187 * The raw mouseover event for the entire grid.
69188 * @param {Roo.EventObject} e
69190 "mouseover" : true,
69193 * The raw mouseout event for the entire grid.
69194 * @param {Roo.EventObject} e
69199 * The raw keypress event for the entire grid.
69200 * @param {Roo.EventObject} e
69205 * The raw keydown event for the entire grid.
69206 * @param {Roo.EventObject} e
69214 * Fires when a cell is clicked
69215 * @param {Grid} this
69216 * @param {Number} rowIndex
69217 * @param {Number} columnIndex
69218 * @param {Roo.EventObject} e
69220 "cellclick" : true,
69222 * @event celldblclick
69223 * Fires when a cell is double clicked
69224 * @param {Grid} this
69225 * @param {Number} rowIndex
69226 * @param {Number} columnIndex
69227 * @param {Roo.EventObject} e
69229 "celldblclick" : true,
69232 * Fires when a row is clicked
69233 * @param {Grid} this
69234 * @param {Number} rowIndex
69235 * @param {Roo.EventObject} e
69239 * @event rowdblclick
69240 * Fires when a row is double clicked
69241 * @param {Grid} this
69242 * @param {Number} rowIndex
69243 * @param {Roo.EventObject} e
69245 "rowdblclick" : true,
69247 * @event headerclick
69248 * Fires when a header is clicked
69249 * @param {Grid} this
69250 * @param {Number} columnIndex
69251 * @param {Roo.EventObject} e
69253 "headerclick" : true,
69255 * @event headerdblclick
69256 * Fires when a header cell is double clicked
69257 * @param {Grid} this
69258 * @param {Number} columnIndex
69259 * @param {Roo.EventObject} e
69261 "headerdblclick" : true,
69263 * @event rowcontextmenu
69264 * Fires when a row is right clicked
69265 * @param {Grid} this
69266 * @param {Number} rowIndex
69267 * @param {Roo.EventObject} e
69269 "rowcontextmenu" : true,
69271 * @event cellcontextmenu
69272 * Fires when a cell is right clicked
69273 * @param {Grid} this
69274 * @param {Number} rowIndex
69275 * @param {Number} cellIndex
69276 * @param {Roo.EventObject} e
69278 "cellcontextmenu" : true,
69280 * @event headercontextmenu
69281 * Fires when a header is right clicked
69282 * @param {Grid} this
69283 * @param {Number} columnIndex
69284 * @param {Roo.EventObject} e
69286 "headercontextmenu" : true,
69288 * @event bodyscroll
69289 * Fires when the body element is scrolled
69290 * @param {Number} scrollLeft
69291 * @param {Number} scrollTop
69293 "bodyscroll" : true,
69295 * @event columnresize
69296 * Fires when the user resizes a column
69297 * @param {Number} columnIndex
69298 * @param {Number} newSize
69300 "columnresize" : true,
69302 * @event columnmove
69303 * Fires when the user moves a column
69304 * @param {Number} oldIndex
69305 * @param {Number} newIndex
69307 "columnmove" : true,
69310 * Fires when row(s) start being dragged
69311 * @param {Grid} this
69312 * @param {Roo.GridDD} dd The drag drop object
69313 * @param {event} e The raw browser event
69315 "startdrag" : true,
69318 * Fires when a drag operation is complete
69319 * @param {Grid} this
69320 * @param {Roo.GridDD} dd The drag drop object
69321 * @param {event} e The raw browser event
69326 * Fires when dragged row(s) are dropped on a valid DD target
69327 * @param {Grid} this
69328 * @param {Roo.GridDD} dd The drag drop object
69329 * @param {String} targetId The target drag drop object
69330 * @param {event} e The raw browser event
69335 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
69336 * @param {Grid} this
69337 * @param {Roo.GridDD} dd The drag drop object
69338 * @param {String} targetId The target drag drop object
69339 * @param {event} e The raw browser event
69344 * Fires when the dragged row(s) first cross another DD target while being dragged
69345 * @param {Grid} this
69346 * @param {Roo.GridDD} dd The drag drop object
69347 * @param {String} targetId The target drag drop object
69348 * @param {event} e The raw browser event
69350 "dragenter" : true,
69353 * Fires when the dragged row(s) leave another DD target while being dragged
69354 * @param {Grid} this
69355 * @param {Roo.GridDD} dd The drag drop object
69356 * @param {String} targetId The target drag drop object
69357 * @param {event} e The raw browser event
69362 * Fires when a row is rendered, so you can change add a style to it.
69363 * @param {GridView} gridview The grid view
69364 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
69370 * Fires when the grid is rendered
69371 * @param {Grid} grid
69376 * Fires when a date is selected
69377 * @param {DatePicker} this
69378 * @param {Date} date The selected date
69382 * @event monthchange
69383 * Fires when the displayed month changes
69384 * @param {DatePicker} this
69385 * @param {Date} date The selected month
69387 'monthchange': true,
69389 * @event evententer
69390 * Fires when mouse over an event
69391 * @param {Calendar} this
69392 * @param {event} Event
69394 'evententer': true,
69396 * @event eventleave
69397 * Fires when the mouse leaves an
69398 * @param {Calendar} this
69401 'eventleave': true,
69403 * @event eventclick
69404 * Fires when the mouse click an
69405 * @param {Calendar} this
69408 'eventclick': true,
69410 * @event eventrender
69411 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
69412 * @param {Calendar} this
69413 * @param {data} data to be modified
69415 'eventrender': true
69419 Roo.grid.Grid.superclass.constructor.call(this);
69420 this.on('render', function() {
69421 this.view.el.addClass('x-grid-cal');
69423 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
69427 if (!Roo.grid.Calendar.style) {
69428 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
69431 '.x-grid-cal .x-grid-col' : {
69432 height: 'auto !important',
69433 'vertical-align': 'top'
69435 '.x-grid-cal .fc-event-hori' : {
69446 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
69448 * @cfg {Store} eventStore The store that loads events.
69453 activeDate : false,
69456 monitorWindowResize : false,
69459 resizeColumns : function() {
69460 var col = (this.view.el.getWidth() / 7) - 3;
69461 // loop through cols, and setWidth
69462 for(var i =0 ; i < 7 ; i++){
69463 this.cm.setColumnWidth(i, col);
69466 setDate :function(date) {
69468 Roo.log('setDate?');
69470 this.resizeColumns();
69471 var vd = this.activeDate;
69472 this.activeDate = date;
69473 // if(vd && this.el){
69474 // var t = date.getTime();
69475 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
69476 // Roo.log('using add remove');
69478 // this.fireEvent('monthchange', this, date);
69480 // this.cells.removeClass("fc-state-highlight");
69481 // this.cells.each(function(c){
69482 // if(c.dateValue == t){
69483 // c.addClass("fc-state-highlight");
69484 // setTimeout(function(){
69485 // try{c.dom.firstChild.focus();}catch(e){}
69495 var days = date.getDaysInMonth();
69497 var firstOfMonth = date.getFirstDateOfMonth();
69498 var startingPos = firstOfMonth.getDay()-this.startDay;
69500 if(startingPos < this.startDay){
69504 var pm = date.add(Date.MONTH, -1);
69505 var prevStart = pm.getDaysInMonth()-startingPos;
69509 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
69511 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
69512 //this.cells.addClassOnOver('fc-state-hover');
69514 var cells = this.cells.elements;
69515 var textEls = this.textNodes;
69517 //Roo.each(cells, function(cell){
69518 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
69521 days += startingPos;
69523 // convert everything to numbers so it's fast
69524 var day = 86400000;
69525 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
69528 //Roo.log(prevStart);
69530 var today = new Date().clearTime().getTime();
69531 var sel = date.clearTime().getTime();
69532 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
69533 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
69534 var ddMatch = this.disabledDatesRE;
69535 var ddText = this.disabledDatesText;
69536 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
69537 var ddaysText = this.disabledDaysText;
69538 var format = this.format;
69540 var setCellClass = function(cal, cell){
69542 //Roo.log('set Cell Class');
69544 var t = d.getTime();
69549 cell.dateValue = t;
69551 cell.className += " fc-today";
69552 cell.className += " fc-state-highlight";
69553 cell.title = cal.todayText;
69556 // disable highlight in other month..
69557 cell.className += " fc-state-highlight";
69562 //cell.className = " fc-state-disabled";
69563 cell.title = cal.minText;
69567 //cell.className = " fc-state-disabled";
69568 cell.title = cal.maxText;
69572 if(ddays.indexOf(d.getDay()) != -1){
69573 // cell.title = ddaysText;
69574 // cell.className = " fc-state-disabled";
69577 if(ddMatch && format){
69578 var fvalue = d.dateFormat(format);
69579 if(ddMatch.test(fvalue)){
69580 cell.title = ddText.replace("%0", fvalue);
69581 cell.className = " fc-state-disabled";
69585 if (!cell.initialClassName) {
69586 cell.initialClassName = cell.dom.className;
69589 cell.dom.className = cell.initialClassName + ' ' + cell.className;
69594 for(; i < startingPos; i++) {
69595 cells[i].dayName = (++prevStart);
69596 Roo.log(textEls[i]);
69597 d.setDate(d.getDate()+1);
69599 //cells[i].className = "fc-past fc-other-month";
69600 setCellClass(this, cells[i]);
69605 for(; i < days; i++){
69606 intDay = i - startingPos + 1;
69607 cells[i].dayName = (intDay);
69608 d.setDate(d.getDate()+1);
69610 cells[i].className = ''; // "x-date-active";
69611 setCellClass(this, cells[i]);
69615 for(; i < 42; i++) {
69616 //textEls[i].innerHTML = (++extraDays);
69618 d.setDate(d.getDate()+1);
69619 cells[i].dayName = (++extraDays);
69620 cells[i].className = "fc-future fc-other-month";
69621 setCellClass(this, cells[i]);
69624 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
69626 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
69628 // this will cause all the cells to mis
69631 for (var r = 0;r < 6;r++) {
69632 for (var c =0;c < 7;c++) {
69633 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
69637 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
69638 for(i=0;i<cells.length;i++) {
69640 this.cells.elements[i].dayName = cells[i].dayName ;
69641 this.cells.elements[i].className = cells[i].className;
69642 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
69643 this.cells.elements[i].title = cells[i].title ;
69644 this.cells.elements[i].dateValue = cells[i].dateValue ;
69650 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
69651 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
69653 ////if(totalRows != 6){
69654 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
69655 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
69658 this.fireEvent('monthchange', this, date);
69663 * Returns the grid's SelectionModel.
69664 * @return {SelectionModel}
69666 getSelectionModel : function(){
69667 if(!this.selModel){
69668 this.selModel = new Roo.grid.CellSelectionModel();
69670 return this.selModel;
69674 this.eventStore.load()
69680 findCell : function(dt) {
69681 dt = dt.clearTime().getTime();
69683 this.cells.each(function(c){
69684 //Roo.log("check " +c.dateValue + '?=' + dt);
69685 if(c.dateValue == dt){
69695 findCells : function(rec) {
69696 var s = rec.data.start_dt.clone().clearTime().getTime();
69698 var e= rec.data.end_dt.clone().clearTime().getTime();
69701 this.cells.each(function(c){
69702 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
69704 if(c.dateValue > e){
69707 if(c.dateValue < s){
69716 findBestRow: function(cells)
69720 for (var i =0 ; i < cells.length;i++) {
69721 ret = Math.max(cells[i].rows || 0,ret);
69728 addItem : function(rec)
69730 // look for vertical location slot in
69731 var cells = this.findCells(rec);
69733 rec.row = this.findBestRow(cells);
69735 // work out the location.
69739 for(var i =0; i < cells.length; i++) {
69747 if (crow.start.getY() == cells[i].getY()) {
69749 crow.end = cells[i];
69765 for (var i = 0; i < cells.length;i++) {
69766 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
69773 clearEvents: function() {
69775 if (!this.eventStore.getCount()) {
69778 // reset number of rows in cells.
69779 Roo.each(this.cells.elements, function(c){
69783 this.eventStore.each(function(e) {
69784 this.clearEvent(e);
69789 clearEvent : function(ev)
69792 Roo.each(ev.els, function(el) {
69793 el.un('mouseenter' ,this.onEventEnter, this);
69794 el.un('mouseleave' ,this.onEventLeave, this);
69802 renderEvent : function(ev,ctr) {
69804 ctr = this.view.el.select('.fc-event-container',true).first();
69808 this.clearEvent(ev);
69814 var cells = ev.cells;
69815 var rows = ev.rows;
69816 this.fireEvent('eventrender', this, ev);
69818 for(var i =0; i < rows.length; i++) {
69822 cls += ' fc-event-start';
69824 if ((i+1) == rows.length) {
69825 cls += ' fc-event-end';
69828 //Roo.log(ev.data);
69829 // how many rows should it span..
69830 var cg = this.eventTmpl.append(ctr,Roo.apply({
69833 }, ev.data) , true);
69836 cg.on('mouseenter' ,this.onEventEnter, this, ev);
69837 cg.on('mouseleave' ,this.onEventLeave, this, ev);
69838 cg.on('click', this.onEventClick, this, ev);
69842 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
69843 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
69846 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
69847 cg.setWidth(ebox.right - sbox.x -2);
69851 renderEvents: function()
69853 // first make sure there is enough space..
69855 if (!this.eventTmpl) {
69856 this.eventTmpl = new Roo.Template(
69857 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
69858 '<div class="fc-event-inner">' +
69859 '<span class="fc-event-time">{time}</span>' +
69860 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
69862 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
69870 this.cells.each(function(c) {
69871 //Roo.log(c.select('.fc-day-content div',true).first());
69872 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
69875 var ctr = this.view.el.select('.fc-event-container',true).first();
69878 this.eventStore.each(function(ev){
69880 this.renderEvent(ev);
69884 this.view.layout();
69888 onEventEnter: function (e, el,event,d) {
69889 this.fireEvent('evententer', this, el, event);
69892 onEventLeave: function (e, el,event,d) {
69893 this.fireEvent('eventleave', this, el, event);
69896 onEventClick: function (e, el,event,d) {
69897 this.fireEvent('eventclick', this, el, event);
69900 onMonthChange: function () {
69904 onLoad: function () {
69906 //Roo.log('calendar onload');
69908 if(this.eventStore.getCount() > 0){
69912 this.eventStore.each(function(d){
69917 if (typeof(add.end_dt) == 'undefined') {
69918 Roo.log("Missing End time in calendar data: ");
69922 if (typeof(add.start_dt) == 'undefined') {
69923 Roo.log("Missing Start time in calendar data: ");
69927 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
69928 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
69929 add.id = add.id || d.id;
69930 add.title = add.title || '??';
69938 this.renderEvents();
69948 render : function ()
69952 if (!this.view.el.hasClass('course-timesheet')) {
69953 this.view.el.addClass('course-timesheet');
69955 if (this.tsStyle) {
69960 Roo.log(_this.grid.view.el.getWidth());
69963 this.tsStyle = Roo.util.CSS.createStyleSheet({
69964 '.course-timesheet .x-grid-row' : {
69967 '.x-grid-row td' : {
69968 'vertical-align' : 0
69970 '.course-edit-link' : {
69972 'text-overflow' : 'ellipsis',
69973 'overflow' : 'hidden',
69974 'white-space' : 'nowrap',
69975 'cursor' : 'pointer'
69980 '.de-act-sup-link' : {
69981 'color' : 'purple',
69982 'text-decoration' : 'line-through'
69986 'text-decoration' : 'line-through'
69988 '.course-timesheet .course-highlight' : {
69989 'border-top-style': 'dashed !important',
69990 'border-bottom-bottom': 'dashed !important'
69992 '.course-timesheet .course-item' : {
69993 'font-family' : 'tahoma, arial, helvetica',
69994 'font-size' : '11px',
69995 'overflow' : 'hidden',
69996 'padding-left' : '10px',
69997 'padding-right' : '10px',
69998 'padding-top' : '10px'
70006 monitorWindowResize : false,
70007 cellrenderer : function(v,x,r)
70012 xtype: 'CellSelectionModel',
70019 beforeload : function (_self, options)
70021 options.params = options.params || {};
70022 options.params._month = _this.monthField.getValue();
70023 options.params.limit = 9999;
70024 options.params['sort'] = 'when_dt';
70025 options.params['dir'] = 'ASC';
70026 this.proxy.loadResponse = this.loadResponse;
70028 //this.addColumns();
70030 load : function (_self, records, options)
70032 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
70033 // if you click on the translation.. you can edit it...
70034 var el = Roo.get(this);
70035 var id = el.dom.getAttribute('data-id');
70036 var d = el.dom.getAttribute('data-date');
70037 var t = el.dom.getAttribute('data-time');
70038 //var id = this.child('span').dom.textContent;
70041 Pman.Dialog.CourseCalendar.show({
70045 productitem_active : id ? 1 : 0
70047 _this.grid.ds.load({});
70052 _this.panel.fireEvent('resize', [ '', '' ]);
70055 loadResponse : function(o, success, response){
70056 // this is overridden on before load..
70058 Roo.log("our code?");
70059 //Roo.log(success);
70060 //Roo.log(response)
70061 delete this.activeRequest;
70063 this.fireEvent("loadexception", this, o, response);
70064 o.request.callback.call(o.request.scope, null, o.request.arg, false);
70069 result = o.reader.read(response);
70071 Roo.log("load exception?");
70072 this.fireEvent("loadexception", this, o, response, e);
70073 o.request.callback.call(o.request.scope, null, o.request.arg, false);
70076 Roo.log("ready...");
70077 // loop through result.records;
70078 // and set this.tdate[date] = [] << array of records..
70080 Roo.each(result.records, function(r){
70082 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
70083 _this.tdata[r.data.when_dt.format('j')] = [];
70085 _this.tdata[r.data.when_dt.format('j')].push(r.data);
70088 //Roo.log(_this.tdata);
70090 result.records = [];
70091 result.totalRecords = 6;
70093 // let's generate some duumy records for the rows.
70094 //var st = _this.dateField.getValue();
70096 // work out monday..
70097 //st = st.add(Date.DAY, -1 * st.format('w'));
70099 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70101 var firstOfMonth = date.getFirstDayOfMonth();
70102 var days = date.getDaysInMonth();
70104 var firstAdded = false;
70105 for (var i = 0; i < result.totalRecords ; i++) {
70106 //var d= st.add(Date.DAY, i);
70109 for(var w = 0 ; w < 7 ; w++){
70110 if(!firstAdded && firstOfMonth != w){
70117 var dd = (d > 0 && d < 10) ? "0"+d : d;
70118 row['weekday'+w] = String.format(
70119 '<span style="font-size: 16px;"><b>{0}</b></span>'+
70120 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
70122 date.format('Y-m-')+dd
70125 if(typeof(_this.tdata[d]) != 'undefined'){
70126 Roo.each(_this.tdata[d], function(r){
70130 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
70131 if(r.parent_id*1>0){
70132 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
70135 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
70136 deactive = 'de-act-link';
70139 row['weekday'+w] += String.format(
70140 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
70142 r.product_id_name, //1
70143 r.when_dt.format('h:ia'), //2
70153 // only do this if something added..
70155 result.records.push(_this.grid.dataSource.reader.newRow(row));
70159 // push it twice. (second one with an hour..
70163 this.fireEvent("load", this, o, o.request.arg);
70164 o.request.callback.call(o.request.scope, result, o.request.arg, true);
70166 sortInfo : {field: 'when_dt', direction : 'ASC' },
70168 xtype: 'HttpProxy',
70171 url : baseURL + '/Roo/Shop_course.php'
70174 xtype: 'JsonReader',
70191 'name': 'parent_id',
70195 'name': 'product_id',
70199 'name': 'productitem_id',
70217 click : function (_self, e)
70219 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70220 sd.setMonth(sd.getMonth()-1);
70221 _this.monthField.setValue(sd.format('Y-m-d'));
70222 _this.grid.ds.load({});
70228 xtype: 'Separator',
70232 xtype: 'MonthField',
70235 render : function (_self)
70237 _this.monthField = _self;
70238 // _this.monthField.set today
70240 select : function (combo, date)
70242 _this.grid.ds.load({});
70245 value : (function() { return new Date(); })()
70248 xtype: 'Separator',
70254 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
70264 click : function (_self, e)
70266 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
70267 sd.setMonth(sd.getMonth()+1);
70268 _this.monthField.setValue(sd.format('Y-m-d'));
70269 _this.grid.ds.load({});
70282 * Ext JS Library 1.1.1
70283 * Copyright(c) 2006-2007, Ext JS, LLC.
70285 * Originally Released Under LGPL - original licence link has changed is not relivant.
70288 * <script type="text/javascript">
70292 * @class Roo.LoadMask
70293 * A simple utility class for generically masking elements while loading data. If the element being masked has
70294 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
70295 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
70296 * element's UpdateManager load indicator and will be destroyed after the initial load.
70298 * Create a new LoadMask
70299 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
70300 * @param {Object} config The config object
70302 Roo.LoadMask = function(el, config){
70303 this.el = Roo.get(el);
70304 Roo.apply(this, config);
70306 this.store.on('beforeload', this.onBeforeLoad, this);
70307 this.store.on('load', this.onLoad, this);
70308 this.store.on('loadexception', this.onLoadException, this);
70309 this.removeMask = false;
70311 var um = this.el.getUpdateManager();
70312 um.showLoadIndicator = false; // disable the default indicator
70313 um.on('beforeupdate', this.onBeforeLoad, this);
70314 um.on('update', this.onLoad, this);
70315 um.on('failure', this.onLoad, this);
70316 this.removeMask = true;
70320 Roo.LoadMask.prototype = {
70322 * @cfg {Boolean} removeMask
70323 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
70324 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
70326 removeMask : false,
70328 * @cfg {String} msg
70329 * The text to display in a centered loading message box (defaults to 'Loading...')
70331 msg : 'Loading...',
70333 * @cfg {String} msgCls
70334 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
70336 msgCls : 'x-mask-loading',
70339 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
70345 * Disables the mask to prevent it from being displayed
70347 disable : function(){
70348 this.disabled = true;
70352 * Enables the mask so that it can be displayed
70354 enable : function(){
70355 this.disabled = false;
70358 onLoadException : function()
70360 Roo.log(arguments);
70362 if (typeof(arguments[3]) != 'undefined') {
70363 Roo.MessageBox.alert("Error loading",arguments[3]);
70367 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
70368 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
70375 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
70378 onLoad : function()
70380 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
70384 onBeforeLoad : function(){
70385 if(!this.disabled){
70386 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
70391 destroy : function(){
70393 this.store.un('beforeload', this.onBeforeLoad, this);
70394 this.store.un('load', this.onLoad, this);
70395 this.store.un('loadexception', this.onLoadException, this);
70397 var um = this.el.getUpdateManager();
70398 um.un('beforeupdate', this.onBeforeLoad, this);
70399 um.un('update', this.onLoad, this);
70400 um.un('failure', this.onLoad, this);
70405 * Ext JS Library 1.1.1
70406 * Copyright(c) 2006-2007, Ext JS, LLC.
70408 * Originally Released Under LGPL - original licence link has changed is not relivant.
70411 * <script type="text/javascript">
70416 * @class Roo.XTemplate
70417 * @extends Roo.Template
70418 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
70420 var t = new Roo.XTemplate(
70421 '<select name="{name}">',
70422 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
70426 // then append, applying the master template values
70429 * Supported features:
70434 {a_variable} - output encoded.
70435 {a_variable.format:("Y-m-d")} - call a method on the variable
70436 {a_variable:raw} - unencoded output
70437 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
70438 {a_variable:this.method_on_template(...)} - call a method on the template object.
70443 <tpl for="a_variable or condition.."></tpl>
70444 <tpl if="a_variable or condition"></tpl>
70445 <tpl exec="some javascript"></tpl>
70446 <tpl name="named_template"></tpl> (experimental)
70448 <tpl for="."></tpl> - just iterate the property..
70449 <tpl for=".."></tpl> - iterates with the parent (probably the template)
70453 Roo.XTemplate = function()
70455 Roo.XTemplate.superclass.constructor.apply(this, arguments);
70462 Roo.extend(Roo.XTemplate, Roo.Template, {
70465 * The various sub templates
70470 * basic tag replacing syntax
70473 * // you can fake an object call by doing this
70477 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
70480 * compile the template
70482 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
70485 compile: function()
70489 s = ['<tpl>', s, '</tpl>'].join('');
70491 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
70492 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
70493 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
70494 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
70495 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
70500 while(true == !!(m = s.match(re))){
70501 var forMatch = m[0].match(nameRe),
70502 ifMatch = m[0].match(ifRe),
70503 execMatch = m[0].match(execRe),
70504 namedMatch = m[0].match(namedRe),
70509 name = forMatch && forMatch[1] ? forMatch[1] : '';
70512 // if - puts fn into test..
70513 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
70515 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
70520 // exec - calls a function... returns empty if true is returned.
70521 exp = execMatch && execMatch[1] ? execMatch[1] : null;
70523 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
70531 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
70532 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
70533 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
70536 var uid = namedMatch ? namedMatch[1] : id;
70540 id: namedMatch ? namedMatch[1] : id,
70547 s = s.replace(m[0], '');
70549 s = s.replace(m[0], '{xtpl'+ id + '}');
70554 for(var i = tpls.length-1; i >= 0; --i){
70555 this.compileTpl(tpls[i]);
70556 this.tpls[tpls[i].id] = tpls[i];
70558 this.master = tpls[tpls.length-1];
70562 * same as applyTemplate, except it's done to one of the subTemplates
70563 * when using named templates, you can do:
70565 * var str = pl.applySubTemplate('your-name', values);
70568 * @param {Number} id of the template
70569 * @param {Object} values to apply to template
70570 * @param {Object} parent (normaly the instance of this object)
70572 applySubTemplate : function(id, values, parent)
70576 var t = this.tpls[id];
70580 if(t.test && !t.test.call(this, values, parent)){
70584 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
70585 Roo.log(e.toString());
70591 if(t.exec && t.exec.call(this, values, parent)){
70595 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
70596 Roo.log(e.toString());
70601 var vs = t.target ? t.target.call(this, values, parent) : values;
70602 parent = t.target ? values : parent;
70603 if(t.target && vs instanceof Array){
70605 for(var i = 0, len = vs.length; i < len; i++){
70606 buf[buf.length] = t.compiled.call(this, vs[i], parent);
70608 return buf.join('');
70610 return t.compiled.call(this, vs, parent);
70612 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
70613 Roo.log(e.toString());
70614 Roo.log(t.compiled);
70619 compileTpl : function(tpl)
70621 var fm = Roo.util.Format;
70622 var useF = this.disableFormats !== true;
70623 var sep = Roo.isGecko ? "+" : ",";
70624 var undef = function(str) {
70625 Roo.log("Property not found :" + str);
70629 var fn = function(m, name, format, args)
70631 //Roo.log(arguments);
70632 args = args ? args.replace(/\\'/g,"'") : args;
70633 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
70634 if (typeof(format) == 'undefined') {
70635 format= 'htmlEncode';
70637 if (format == 'raw' ) {
70641 if(name.substr(0, 4) == 'xtpl'){
70642 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
70645 // build an array of options to determine if value is undefined..
70647 // basically get 'xxxx.yyyy' then do
70648 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
70649 // (function () { Roo.log("Property not found"); return ''; })() :
70654 Roo.each(name.split('.'), function(st) {
70655 lookfor += (lookfor.length ? '.': '') + st;
70656 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
70659 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
70662 if(format && useF){
70664 args = args ? ',' + args : "";
70666 if(format.substr(0, 5) != "this."){
70667 format = "fm." + format + '(';
70669 format = 'this.call("'+ format.substr(5) + '", ';
70673 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
70677 // called with xxyx.yuu:(test,test)
70679 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
70681 // raw.. - :raw modifier..
70682 return "'"+ sep + udef_st + name + ")"+sep+"'";
70686 // branched to use + in gecko and [].join() in others
70688 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
70689 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
70692 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
70693 body.push(tpl.body.replace(/(\r\n|\n)/g,
70694 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
70695 body.push("'].join('');};};");
70696 body = body.join('');
70699 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
70701 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
70707 applyTemplate : function(values){
70708 return this.master.compiled.call(this, values, {});
70709 //var s = this.subs;
70712 apply : function(){
70713 return this.applyTemplate.apply(this, arguments);
70718 Roo.XTemplate.from = function(el){
70719 el = Roo.getDom(el);
70720 return new Roo.XTemplate(el.value || el.innerHTML);
70721 };// old names for panel elements
70722 Roo.GridPanel = Roo.panel.Grid;
70723 Roo.CalendarPanel = Roo.panel.Calendar;
70724 Roo.ContentPanel = Roo.panel.Content;
70725 Roo.NestedLayoutPanel = Roo.panel.NestedLayout;
70726 Roo.TabPanel = Roo.panel.Tab;
70727 Roo.TabPanelItem = Roo.panel.TabItem;
70728 Roo.TreePanel = Roo.panel.Tree;
70732 Roo.ScrollPanel = Roo.panel.Scroll;
70734 Roo.BorderLayout = Roo.layout.Border;
70735 Roo.BasicLayoutRegion = Roo.layout.BasicRegion;
70736 Roo.LayoutRegion = Roo.layout.Region;
70737 Roo.SplitLayoutRegion = Roo.layout.SplitRegion;
70738 Roo.LayoutManager = Roo.layout.Manager;
70741 Roo.NorthLayoutRegion = Roo.layout.North;
70742 Roo.EastLayoutRegion = Roo.layout.East;
70743 Roo.WestLayoutRegion = Roo.layout.West;
70744 Roo.SouthLayoutRegion = Roo.layout.South;
70745 Roo.CenterLayoutRegion = Roo.layout.Center;
70748 Roo.LayoutStateManager = Roo.layout.StateManager;
70749 Roo.ReaderLayout = Roo.layout.Reader;