4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
17 window["undefined"] = window["undefined"];
21 * Roo core utilities and functions.
26 * Copies all the properties of config to obj.
27 * @param {Object} obj The receiver of the properties
28 * @param {Object} config The source of the properties
29 * @param {Object} defaults A different object that will also be applied for default values
30 * @return {Object} returns obj
35 Roo.apply = function(o, c, defaults){
37 // no "this" reference for friendly out of scope calls
38 Roo.apply(o, defaults);
40 if(o && c && typeof c == 'object'){
51 var ua = navigator.userAgent.toLowerCase();
53 var isStrict = document.compatMode == "CSS1Compat",
54 isOpera = ua.indexOf("opera") > -1,
55 isSafari = (/webkit|khtml/).test(ua),
56 isFirefox = ua.indexOf("firefox") > -1,
57 isIE = ua.indexOf("msie") > -1,
58 isIE7 = ua.indexOf("msie 7") > -1,
59 isIE11 = /trident.*rv\:11\./.test(ua),
60 isEdge = ua.indexOf("edge") > -1,
61 isGecko = !isSafari && ua.indexOf("gecko") > -1,
62 isBorderBox = isIE && !isStrict,
63 isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64 isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65 isLinux = (ua.indexOf("linux") != -1),
66 isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67 isIOS = /iphone|ipad/.test(ua),
68 isAndroid = /android/.test(ua),
69 isTouch = (function() {
71 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72 window.addEventListener('touchstart', function __set_has_touch__ () {
74 window.removeEventListener('touchstart', __set_has_touch__);
76 return false; // no touch on chrome!?
78 document.createEvent("TouchEvent");
85 // remove css image flicker
88 document.execCommand("BackgroundImageCache", false, true);
94 * True if the browser is in strict mode
99 * True if the page is running over SSL
104 * True when the document is fully initialized and ready for action
109 * Turn on debugging output (currently only the factory uses this)
116 * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
119 enableGarbageCollector : true,
122 * True to automatically purge event listeners after uncaching an element (defaults to false).
123 * Note: this only happens if enableGarbageCollector is true.
126 enableListenerCollection:false,
129 * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130 * the IE insecure content warning (defaults to javascript:false).
133 SSL_SECURE_URL : "javascript:false",
136 * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137 * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
140 BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
142 emptyFn : function(){},
145 * Copies all the properties of config to obj if they don't already exist.
146 * @param {Object} obj The receiver of the properties
147 * @param {Object} config The source of the properties
148 * @return {Object} returns obj
150 applyIf : function(o, c){
153 if(typeof o[p] == "undefined"){ o[p] = c[p]; }
160 * Applies event listeners to elements by selectors when the document is ready.
161 * The event name is specified with an @ suffix.
164 // add a listener for click on all anchors in element with id foo
165 '#foo a@click' : function(e, t){
169 // add the same listener to multiple selectors (separated by comma BEFORE the @)
170 '#foo a, #bar span.some-class@mouseover' : function(){
175 * @param {Object} obj The list of behaviors to apply
177 addBehaviors : function(o){
179 Roo.onReady(function(){
184 var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
186 var parts = b.split('@');
187 if(parts[1]){ // for Object prototype breakers
190 cache[s] = Roo.select(s);
192 cache[s].on(parts[1], o[b]);
199 * Generates unique ids. If the element already has an id, it is unchanged
200 * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201 * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202 * @return {String} The generated Id.
204 id : function(el, prefix){
205 prefix = prefix || "roo-gen";
207 var id = prefix + (++idSeed);
208 return el ? (el.id ? el.id : (el.id = id)) : id;
213 * Extends one class with another class and optionally overrides members with the passed literal. This class
214 * also adds the function "override()" to the class that can be used to override
215 * members on an instance.
216 * @param {Object} subclass The class inheriting the functionality
217 * @param {Object} superclass The class being extended
218 * @param {Object} overrides (optional) A literal with members
223 var io = function(o){
228 return function(sb, sp, overrides){
229 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
232 sb = function(){sp.apply(this, arguments);};
234 var F = function(){}, sbp, spp = sp.prototype;
236 sbp = sb.prototype = new F();
240 if(spp.constructor == Object.prototype.constructor){
245 sb.override = function(o){
249 Roo.override(sb, overrides);
255 * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
257 Roo.override(MyClass, {
258 newMethod1: function(){
261 newMethod2: function(foo){
266 * @param {Object} origclass The class to override
267 * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
268 * containing one or more methods.
271 override : function(origclass, overrides){
273 var p = origclass.prototype;
274 for(var method in overrides){
275 p[method] = overrides[method];
280 * Creates namespaces to be used for scoping variables and classes so that they are not global. Usage:
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
286 * @param {String} namespace1
287 * @param {String} namespace2
288 * @param {String} etc
291 namespace : function(){
292 var a=arguments, o=null, i, j, d, rt;
293 for (i=0; i<a.length; ++i) {
297 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298 for (j=1; j<d.length; ++j) {
299 o[d[j]]=o[d[j]] || {};
305 * Creates namespaces to be used for scoping variables and classes so that they are not global. Usage:
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
310 * @param {String} classname
311 * @param {String} namespace (optional)
315 factory : function(c, ns)
317 // no xtype, no ns or c.xns - or forced off by c.xns
318 if (!c.xtype || (!ns && !c.xns) || (c.xns === false)) { // not enough info...
321 ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322 if (c.constructor == ns[c.xtype]) {// already created...
326 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327 var ret = new ns[c.xtype](c);
331 c.xns = false; // prevent recursion..
335 * Logs to console if it can.
337 * @param {String|Object} string
342 if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
349 * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2". Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
353 urlEncode : function(o){
359 var ov = o[key], k = Roo.encodeURIComponent(key);
360 var type = typeof ov;
361 if(type == 'undefined'){
363 }else if(type != "function" && type != "object"){
364 buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365 }else if(ov instanceof Array){
367 for(var i = 0, len = ov.length; i < len; i++) {
368 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
379 * Safe version of encodeURIComponent
380 * @param {String} data
384 encodeURIComponent : function (data)
387 return encodeURIComponent(data);
388 } catch(e) {} // should be an uri encode error.
390 if (data == '' || data == null){
393 // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394 function nibble_to_hex(nibble){
395 var chars = '0123456789ABCDEF';
396 return chars.charAt(nibble);
398 data = data.toString();
400 for(var i=0; i<data.length; i++){
401 var c = data.charCodeAt(i);
402 var bs = new Array();
405 bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406 bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407 bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408 bs[3] = 0x80 | (c & 0x3F);
409 }else if (c > 0x800){
411 bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412 bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413 bs[2] = 0x80 | (c & 0x3F);
416 bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417 bs[1] = 0x80 | (c & 0x3F);
422 for(var j=0; j<bs.length; j++){
424 var hex = nibble_to_hex((b & 0xF0) >>> 4)
425 + nibble_to_hex(b &0x0F);
434 * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435 * @param {String} string
436 * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437 * @return {Object} A literal with members
439 urlDecode : function(string, overwrite){
440 if(!string || !string.length){
444 var pairs = string.split('&');
445 var pair, name, value;
446 for(var i = 0, len = pairs.length; i < len; i++){
447 pair = pairs[i].split('=');
448 name = decodeURIComponent(pair[0]);
449 value = decodeURIComponent(pair[1]);
450 if(overwrite !== true){
451 if(typeof obj[name] == "undefined"){
453 }else if(typeof obj[name] == "string"){
454 obj[name] = [obj[name]];
455 obj[name].push(value);
457 obj[name].push(value);
467 * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468 * passed array is not really an array, your function is called once with it.
469 * The supplied function is called with (Object item, Number index, Array allItems).
470 * @param {Array/NodeList/Mixed} array
471 * @param {Function} fn
472 * @param {Object} scope
474 each : function(array, fn, scope){
475 if(typeof array.length == "undefined" || typeof array == "string"){
478 for(var i = 0, len = array.length; i < len; i++){
479 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
484 combine : function(){
485 var as = arguments, l = as.length, r = [];
486 for(var i = 0; i < l; i++){
488 if(a instanceof Array){
490 }else if(a.length !== undefined && !a.substr){
491 r = r.concat(Array.prototype.slice.call(a, 0));
500 * Escapes the passed string for use in a regular expression
501 * @param {String} str
504 escapeRe : function(s) {
505 return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
509 callback : function(cb, scope, args, delay){
510 if(typeof cb == "function"){
512 cb.defer(delay, scope, args || []);
514 cb.apply(scope, args || []);
520 * Return the dom node for the passed string (id), dom node, or Roo.Element
521 * @param {String/HTMLElement/Roo.Element} el
522 * @return HTMLElement
524 getDom : function(el){
528 return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
532 * Shorthand for {@link Roo.ComponentMgr#get}
534 * @return Roo.Component
536 getCmp : function(id){
537 return Roo.ComponentMgr.get(id);
540 num : function(v, defaultValue){
541 if(typeof v != 'number'){
547 destroy : function(){
548 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
552 as.removeAllListeners();
556 if(typeof as.purgeListeners == 'function'){
559 if(typeof as.destroy == 'function'){
566 // inpired by a similar function in mootools library
568 * Returns the type of object that is passed in. If the object passed in is null or undefined it
569 * return false otherwise it returns one of the following values:<ul>
570 * <li><b>string</b>: If the object passed is a string</li>
571 * <li><b>number</b>: If the object passed is a number</li>
572 * <li><b>boolean</b>: If the object passed is a boolean value</li>
573 * <li><b>function</b>: If the object passed is a function reference</li>
574 * <li><b>object</b>: If the object passed is an object</li>
575 * <li><b>array</b>: If the object passed is an array</li>
576 * <li><b>regexp</b>: If the object passed is a regular expression</li>
577 * <li><b>element</b>: If the object passed is a DOM Element</li>
578 * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579 * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580 * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581 * @param {Mixed} object
585 if(o === undefined || o === null){
592 if(t == 'object' && o.nodeName) {
594 case 1: return 'element';
595 case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
598 if(t == 'object' || t == 'function') {
599 switch(o.constructor) {
600 case Array: return 'array';
601 case RegExp: return 'regexp';
603 if(typeof o.length == 'number' && typeof o.item == 'function') {
611 * Returns true if the passed value is null, undefined or an empty string (optional).
612 * @param {Mixed} value The value to test
613 * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
616 isEmpty : function(v, allowBlank){
617 return v === null || v === undefined || (!allowBlank ? v === '' : false);
625 isFirefox : isFirefox,
637 isBorderBox : isBorderBox,
639 isWindows : isWindows,
647 isAndroid : isAndroid,
652 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653 * you may want to set this to true.
656 useShims : ((isIE && !isIE7) || (isGecko && isMac)),
661 * Selects a single element as a Roo Element
662 * This is about as close as you can get to jQuery's $('do crazy stuff')
663 * @param {String} selector The selector/xpath query
664 * @param {Node} root (optional) The start of the query (defaults to document).
665 * @return {Roo.Element}
667 selectNode : function(selector, root)
669 var node = Roo.DomQuery.selectNode(selector,root);
670 return node ? Roo.get(node) : new Roo.Element(false);
673 * Find the current bootstrap width Grid size
674 * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675 * @returns {String} (xs|sm|md|lg|xl)
678 getGridSize : function()
680 var w = Roo.lib.Dom.getViewWidth();
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
707 * Ext JS Library 1.1.1
708 * Copyright(c) 2006-2007, Ext JS, LLC.
710 * Originally Released Under LGPL - original licence link has changed is not relivant.
713 * <script type="text/javascript">
717 // wrappedn so fnCleanup is not in global scope...
719 function fnCleanUp() {
720 var p = Function.prototype;
721 delete p.createSequence;
723 delete p.createDelegate;
724 delete p.createCallback;
725 delete p.createInterceptor;
727 window.detachEvent("onunload", fnCleanUp);
729 window.attachEvent("onunload", fnCleanUp);
736 * These functions are available on every Function object (any JavaScript function).
738 Roo.apply(Function.prototype, {
740 * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741 * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742 * Will create a function that is bound to those 2 args.
743 * @return {Function} The new function
745 createCallback : function(/*args...*/){
746 // make args available, in function below
747 var args = arguments;
750 return method.apply(window, args);
755 * Creates a delegate (callback) that sets the scope to obj.
756 * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757 * Will create a function that is automatically scoped to this.
758 * @param {Object} obj (optional) The object for which the scope is set
759 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761 * if a number the args are inserted at the specified position
762 * @return {Function} The new function
764 createDelegate : function(obj, args, appendArgs){
767 var callArgs = args || arguments;
768 if(appendArgs === true){
769 callArgs = Array.prototype.slice.call(arguments, 0);
770 callArgs = callArgs.concat(args);
771 }else if(typeof appendArgs == "number"){
772 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776 return method.apply(obj || window, callArgs);
781 * Calls this function after the number of millseconds specified.
782 * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783 * @param {Object} obj (optional) The object for which the scope is set
784 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786 * if a number the args are inserted at the specified position
787 * @return {Number} The timeout id that can be used with clearTimeout
789 defer : function(millis, obj, args, appendArgs){
790 var fn = this.createDelegate(obj, args, appendArgs);
792 return setTimeout(fn, millis);
798 * Create a combined function call sequence of the original function + the passed function.
799 * The resulting function returns the results of the original function.
800 * The passed fcn is called with the parameters of the original function
801 * @param {Function} fcn The function to sequence
802 * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803 * @return {Function} The new function
805 createSequence : function(fcn, scope){
806 if(typeof fcn != "function"){
811 var retval = method.apply(this || window, arguments);
812 fcn.apply(scope || this || window, arguments);
818 * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819 * The resulting function returns the results of the original function.
820 * The passed fcn is called with the parameters of the original function.
822 * @param {Function} fcn The function to call before the original
823 * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824 * @return {Function} The new function
826 createInterceptor : function(fcn, scope){
827 if(typeof fcn != "function"){
834 if(fcn.apply(scope || this || window, arguments) === false){
837 return method.apply(this || window, arguments);
843 * Ext JS Library 1.1.1
844 * Copyright(c) 2006-2007, Ext JS, LLC.
846 * Originally Released Under LGPL - original licence link has changed is not relivant.
849 * <script type="text/javascript">
852 Roo.applyIf(String, {
857 * Escapes the passed string for ' and \
858 * @param {String} string The string to escape
859 * @return {String} The escaped string
862 escape : function(string) {
863 return string.replace(/('|\\)/g, "\\$1");
867 * Pads the left side of a string with a specified character. This is especially useful
868 * for normalizing number and date strings. Example usage:
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
873 * @param {String} string The original string
874 * @param {Number} size The total length of the output string
875 * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876 * @return {String} The padded string
879 leftPad : function (val, size, ch) {
880 var result = new String(val);
881 if(ch === null || ch === undefined || ch === '') {
884 while (result.length < size) {
885 result = ch + result;
891 * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
892 * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
898 * @param {String} string The tokenized string to be formatted
899 * @param {String} value1 The value to replace token {0}
900 * @param {String} value2 Etc...
901 * @return {String} The formatted string
904 format : function(format){
905 var args = Array.prototype.slice.call(arguments, 1);
906 return format.replace(/\{(\d+)\}/g, function(m, i){
907 return Roo.util.Format.htmlEncode(args[i]);
915 * Utility function that allows you to easily switch a string between two alternating values. The passed value
916 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
917 * they are already different, the first value passed in is returned. Note that this method returns the new value
918 * but does not change the current string.
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 * @param {String} value The value to compare to the current string
927 * @param {String} other The new value to use if the string already equals the first value passed in
928 * @return {String} The new value
931 String.prototype.toggle = function(value, other){
932 return this == value ? other : value;
937 * Remove invalid unicode characters from a string
939 * @return {String} The clean string
941 String.prototype.unicodeClean = function () {
942 return this.replace(/[\s\S]/g,
943 function(character) {
944 if (character.charCodeAt()< 256) {
948 encodeURIComponent(character);
959 * Make the first letter of a string uppercase
961 * @return {String} The new string.
963 String.prototype.toUpperCaseFirst = function () {
964 return this.charAt(0).toUpperCase() + this.slice(1);
969 * Ext JS Library 1.1.1
970 * Copyright(c) 2006-2007, Ext JS, LLC.
972 * Originally Released Under LGPL - original licence link has changed is not relivant.
975 * <script type="text/javascript">
981 Roo.applyIf(Number.prototype, {
983 * Checks whether or not the current number is within a desired range. If the number is already within the
984 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985 * exceeded. Note that this method returns the constrained value but does not change the current number.
986 * @param {Number} min The minimum number in the range
987 * @param {Number} max The maximum number in the range
988 * @return {Number} The constrained value if outside the range, otherwise the current value
990 constrain : function(min, max){
991 return Math.min(Math.max(this, min), max);
995 * Ext JS Library 1.1.1
996 * Copyright(c) 2006-2007, Ext JS, LLC.
998 * Originally Released Under LGPL - original licence link has changed is not relivant.
1001 * <script type="text/javascript">
1006 Roo.applyIf(Array.prototype, {
1009 * Checks whether or not the specified object exists in the array.
1010 * @param {Object} o The object to check for
1011 * @return {Number} The index of o in the array (or -1 if it is not found)
1013 indexOf : function(o){
1014 for (var i = 0, len = this.length; i < len; i++){
1015 if(this[i] == o) { return i; }
1021 * Removes the specified object from the array. If the object is not found nothing happens.
1022 * @param {Object} o The object to remove
1024 remove : function(o){
1025 var index = this.indexOf(o);
1027 this.splice(index, 1);
1031 * Map (JS 1.6 compatibility)
1032 * @param {Function} function to call
1034 map : function(fun )
1036 var len = this.length >>> 0;
1037 if (typeof fun != "function") {
1038 throw new TypeError();
1040 var res = new Array(len);
1041 var thisp = arguments[1];
1042 for (var i = 0; i < len; i++)
1045 res[i] = fun.call(thisp, this[i], i, this);
1053 * @param {Array} o The array to compare to
1054 * @returns {Boolean} true if the same
1056 equals : function(b)
1058 // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1065 if (this.length !== b.length) {
1069 // sort?? a.sort().equals(b.sort());
1071 for (var i = 0; i < this.length; ++i) {
1072 if (this[i] !== b[i]) {
1084 Roo.applyIf(Array, {
1088 * @param {Array} o Or Array like object (eg. nodelist)
1095 for (var i =0; i < o.length; i++) {
1104 * Ext JS Library 1.1.1
1105 * Copyright(c) 2006-2007, Ext JS, LLC.
1107 * Originally Released Under LGPL - original licence link has changed is not relivant.
1110 * <script type="text/javascript">
1116 * The date parsing and format syntax is a subset of
1117 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118 * supported will provide results equivalent to their PHP versions.
1120 * Following is the list of all currently supported formats:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1125 Format Output Description
1126 ------ ---------- --------------------------------------------------------------
1127 d 10 Day of the month, 2 digits with leading zeros
1128 D Wed A textual representation of a day, three letters
1129 j 10 Day of the month without leading zeros
1130 l Wednesday A full textual representation of the day of the week
1131 S th English ordinal day of month suffix, 2 chars (use with j)
1132 w 3 Numeric representation of the day of the week
1133 z 9 The julian date, or day of the year (0-365)
1134 W 01 ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135 F January A full textual representation of the month
1136 m 01 Numeric representation of a month, with leading zeros
1137 M Jan Month name abbreviation, three letters
1138 n 1 Numeric representation of a month, without leading zeros
1139 t 31 Number of days in the given month
1140 L 0 Whether it's a leap year (1 if it is a leap year, else 0)
1141 Y 2007 A full numeric representation of a year, 4 digits
1142 y 07 A two digit representation of a year
1143 a pm Lowercase Ante meridiem and Post meridiem
1144 A PM Uppercase Ante meridiem and Post meridiem
1145 g 3 12-hour format of an hour without leading zeros
1146 G 15 24-hour format of an hour without leading zeros
1147 h 03 12-hour format of an hour with leading zeros
1148 H 15 24-hour format of an hour with leading zeros
1149 i 05 Minutes with leading zeros
1150 s 01 Seconds, with leading zeros
1151 O -0600 Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152 P -06:00 Difference to Greenwich time (GMT) with colon between hours and minutes
1153 T CST Timezone setting of the machine running the code
1154 Z -21600 Timezone offset in seconds (negative if west of UTC, positive if east)
1157 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d')); //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a')); //January 10, 2007, 3:05 pm
1162 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A')); //Wednesday, the 10th of January 2007 03:05:01 PM
1165 * Here are some standard date/time patterns that you might find helpful. They
1166 * are not part of the source of Date.js, but to use them you can simply copy this
1167 * block of code into any script that is included after Date.js and they will also become
1168 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
1171 ISO8601Long:"Y-m-d H:i:s",
1172 ISO8601Short:"Y-m-d",
1174 LongDate: "l, F d, Y",
1175 FullDateTime: "l, F d, Y g:i:s A",
1178 LongTime: "g:i:s A",
1179 SortableDateTime: "Y-m-d\\TH:i:s",
1180 UniversalSortableDateTime: "Y-m-d H:i:sO",
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1193 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194 * They generate precompiled functions from date formats instead of parsing and
1195 * processing the pattern every time you format a date. These functions are available
1196 * on every Date object (any javascript function).
1198 * The original article and download are here:
1199 * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1206 Returns the number of milliseconds between this date and date
1207 @param {Date} date (optional) Defaults to now
1208 @param {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY)
1209 @return {Number} The diff in milliseconds or units of interval
1210 @member Date getElapsed
1212 Date.prototype.getElapsed = function(date, interval)
1214 date = date || new Date();
1215 var ret = Math.abs(date.getTime()-this.getTime());
1219 return Math.floor(ret / (1000));
1221 return Math.floor(ret / (1000*60));
1223 return Math.floor(ret / (1000*60*60));
1225 return Math.floor(ret / (1000*60*60*24));
1226 case Date.MONTH: // this does not give exact number...??
1227 return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1228 case Date.YEAR: // this does not give exact number...??
1229 return (date.format("Y") - this.format("Y"));
1237 // was in date file..
1241 Date.parseFunctions = {count:0};
1243 Date.parseRegexes = [];
1245 Date.formatFunctions = {count:0};
1248 Date.prototype.dateFormat = function(format) {
1249 if (Date.formatFunctions[format] == null) {
1250 Date.createNewFormat(format);
1252 var func = Date.formatFunctions[format];
1253 return this[func]();
1258 * Formats a date given the supplied format string
1259 * @param {String} format The format string
1260 * @return {String} The formatted date
1263 Date.prototype.format = Date.prototype.dateFormat;
1266 Date.createNewFormat = function(format) {
1267 var funcName = "format" + Date.formatFunctions.count++;
1268 Date.formatFunctions[format] = funcName;
1269 var code = "Date.prototype." + funcName + " = function(){return ";
1270 var special = false;
1272 for (var i = 0; i < format.length; ++i) {
1273 ch = format.charAt(i);
1274 if (!special && ch == "\\") {
1279 code += "'" + String.escape(ch) + "' + ";
1282 code += Date.getFormatCode(ch);
1285 /** eval:var:zzzzzzzzzzzzz */
1286 eval(code.substring(0, code.length - 3) + ";}");
1290 Date.getFormatCode = function(character) {
1291 switch (character) {
1293 return "String.leftPad(this.getDate(), 2, '0') + ";
1295 return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1297 return "this.getDate() + ";
1299 return "Date.dayNames[this.getDay()] + ";
1301 return "this.getSuffix() + ";
1303 return "this.getDay() + ";
1305 return "this.getDayOfYear() + ";
1307 return "this.getWeekOfYear() + ";
1309 return "Date.monthNames[this.getMonth()] + ";
1311 return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1313 return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1315 return "(this.getMonth() + 1) + ";
1317 return "this.getDaysInMonth() + ";
1319 return "(this.isLeapYear() ? 1 : 0) + ";
1321 return "this.getFullYear() + ";
1323 return "('' + this.getFullYear()).substring(2, 4) + ";
1325 return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1327 return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1329 return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1331 return "this.getHours() + ";
1333 return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1335 return "String.leftPad(this.getHours(), 2, '0') + ";
1337 return "String.leftPad(this.getMinutes(), 2, '0') + ";
1339 return "String.leftPad(this.getSeconds(), 2, '0') + ";
1341 return "this.getGMTOffset() + ";
1343 return "this.getGMTColonOffset() + ";
1345 return "this.getTimezone() + ";
1347 return "(this.getTimezoneOffset() * -60) + ";
1349 return "'" + String.escape(character) + "' + ";
1354 * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1355 * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates. Any part of
1356 * the date format that is not specified will default to the current date value for that part. Time parts can also
1357 * be specified, but default to 0. Keep in mind that the input date string must precisely match the specified format
1358 * string or the parse operation will fail.
1361 //dt = Fri May 25 2007 (current date)
1362 var dt = new Date();
1364 //dt = Thu May 25 2006 (today's month/day in 2006)
1365 dt = Date.parseDate("2006", "Y");
1367 //dt = Sun Jan 15 2006 (all date parts specified)
1368 dt = Date.parseDate("2006-1-15", "Y-m-d");
1370 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1371 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1373 * @param {String} input The unparsed date as a string
1374 * @param {String} format The format the date is in
1375 * @return {Date} The parsed date
1378 Date.parseDate = function(input, format) {
1379 if (Date.parseFunctions[format] == null) {
1380 Date.createParser(format);
1382 var func = Date.parseFunctions[format];
1383 return Date[func](input);
1389 Date.createParser = function(format) {
1390 var funcName = "parse" + Date.parseFunctions.count++;
1391 var regexNum = Date.parseRegexes.length;
1392 var currentGroup = 1;
1393 Date.parseFunctions[format] = funcName;
1395 var code = "Date." + funcName + " = function(input){\n"
1396 + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1397 + "var d = new Date();\n"
1398 + "y = d.getFullYear();\n"
1399 + "m = d.getMonth();\n"
1400 + "d = d.getDate();\n"
1401 + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1402 + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1403 + "if (results && results.length > 0) {";
1406 var special = false;
1408 for (var i = 0; i < format.length; ++i) {
1409 ch = format.charAt(i);
1410 if (!special && ch == "\\") {
1415 regex += String.escape(ch);
1418 var obj = Date.formatCodeToRegex(ch, currentGroup);
1419 currentGroup += obj.g;
1421 if (obj.g && obj.c) {
1427 code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1428 + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1429 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1430 + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1431 + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1432 + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1433 + "else if (y >= 0 && m >= 0 && d > 0)\n"
1434 + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1435 + "else if (y >= 0 && m >= 0)\n"
1436 + "{v = new Date(y, m); v.setFullYear(y);}\n"
1437 + "else if (y >= 0)\n"
1438 + "{v = new Date(y); v.setFullYear(y);}\n"
1439 + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1440 + " ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1441 + " v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1444 Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1445 /** eval:var:zzzzzzzzzzzzz */
1450 Date.formatCodeToRegex = function(character, currentGroup) {
1451 switch (character) {
1455 s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1458 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1459 s:"(\\d{1,2})"}; // day of month without leading zeroes
1462 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1463 s:"(\\d{2})"}; // day of month with leading zeroes
1467 s:"(?:" + Date.dayNames.join("|") + ")"};
1471 s:"(?:st|nd|rd|th)"};
1486 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1487 s:"(" + Date.monthNames.join("|") + ")"};
1490 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1491 s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1494 c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1495 s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1498 c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1499 s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1510 c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1514 c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1515 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1519 c:"if (results[" + currentGroup + "] == 'am') {\n"
1520 + "if (h == 12) { h = 0; }\n"
1521 + "} else { if (h < 12) { h += 12; }}",
1525 c:"if (results[" + currentGroup + "] == 'AM') {\n"
1526 + "if (h == 12) { h = 0; }\n"
1527 + "} else { if (h < 12) { h += 12; }}",
1532 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1533 s:"(\\d{1,2})"}; // 12/24-hr format format of an hour without leading zeroes
1537 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1538 s:"(\\d{2})"}; // 12/24-hr format format of an hour with leading zeroes
1541 c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1545 c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1550 "o = results[", currentGroup, "];\n",
1551 "var sn = o.substring(0,1);\n", // get + / - sign
1552 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1553 "var mn = o.substring(3,5) % 60;\n", // get minutes
1554 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1555 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1557 s:"([+\-]\\d{2,4})"};
1563 "o = results[", currentGroup, "];\n",
1564 "var sn = o.substring(0,1);\n",
1565 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1566 "var mn = o.substring(4,6) % 60;\n",
1567 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1568 " (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1574 s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1577 c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1578 + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1579 s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1583 s:String.escape(character)};
1588 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1589 * @return {String} The abbreviated timezone name (e.g. 'CST')
1591 Date.prototype.getTimezone = function() {
1592 return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1596 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1597 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1599 Date.prototype.getGMTOffset = function() {
1600 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1601 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1602 + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1606 * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1607 * @return {String} 2-characters representing hours and 2-characters representing minutes
1608 * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1610 Date.prototype.getGMTColonOffset = function() {
1611 return (this.getTimezoneOffset() > 0 ? "-" : "+")
1612 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1614 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1618 * Get the numeric day number of the year, adjusted for leap year.
1619 * @return {Number} 0 through 364 (365 in leap years)
1621 Date.prototype.getDayOfYear = function() {
1623 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1624 for (var i = 0; i < this.getMonth(); ++i) {
1625 num += Date.daysInMonth[i];
1627 return num + this.getDate() - 1;
1631 * Get the string representation of the numeric week number of the year
1632 * (equivalent to the format specifier 'W').
1633 * @return {String} '00' through '52'
1635 Date.prototype.getWeekOfYear = function() {
1636 // Skip to Thursday of this week
1637 var now = this.getDayOfYear() + (4 - this.getDay());
1638 // Find the first Thursday of the year
1639 var jan1 = new Date(this.getFullYear(), 0, 1);
1640 var then = (7 - jan1.getDay() + 4);
1641 return String.leftPad(((now - then) / 7) + 1, 2, "0");
1645 * Whether or not the current date is in a leap year.
1646 * @return {Boolean} True if the current date is in a leap year, else false
1648 Date.prototype.isLeapYear = function() {
1649 var year = this.getFullYear();
1650 return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1654 * Get the first day of the current month, adjusted for leap year. The returned value
1655 * is the numeric day index within the week (0-6) which can be used in conjunction with
1656 * the {@link #monthNames} array to retrieve the textual day name.
1659 var dt = new Date('1/10/2007');
1660 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1662 * @return {Number} The day number (0-6)
1664 Date.prototype.getFirstDayOfMonth = function() {
1665 var day = (this.getDay() - (this.getDate() - 1)) % 7;
1666 return (day < 0) ? (day + 7) : day;
1670 * Get the last day of the current month, adjusted for leap year. The returned value
1671 * is the numeric day index within the week (0-6) which can be used in conjunction with
1672 * the {@link #monthNames} array to retrieve the textual day name.
1675 var dt = new Date('1/10/2007');
1676 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1678 * @return {Number} The day number (0-6)
1680 Date.prototype.getLastDayOfMonth = function() {
1681 var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1682 return (day < 0) ? (day + 7) : day;
1687 * Get the first date of this date's month
1690 Date.prototype.getFirstDateOfMonth = function() {
1691 return new Date(this.getFullYear(), this.getMonth(), 1);
1695 * Get the last date of this date's month
1698 Date.prototype.getLastDateOfMonth = function() {
1699 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1702 * Get the number of days in the current month, adjusted for leap year.
1703 * @return {Number} The number of days in the month
1705 Date.prototype.getDaysInMonth = function() {
1706 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1707 return Date.daysInMonth[this.getMonth()];
1711 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1712 * @return {String} 'st, 'nd', 'rd' or 'th'
1714 Date.prototype.getSuffix = function() {
1715 switch (this.getDate()) {
1732 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1735 * An array of textual month names.
1736 * Override these values for international dates, for example...
1737 * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1756 * An array of textual day names.
1757 * Override these values for international dates, for example...
1758 * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1774 Date.monthNumbers = {
1789 * Creates and returns a new Date instance with the exact same date value as the called instance.
1790 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1791 * variable will also be changed. When the intention is to create a new variable that will not
1792 * modify the original instance, you should create a clone.
1794 * Example of correctly cloning a date:
1797 var orig = new Date('10/1/2006');
1800 document.write(orig); //returns 'Thu Oct 05 2006'!
1803 var orig = new Date('10/1/2006');
1804 var copy = orig.clone();
1806 document.write(orig); //returns 'Thu Oct 01 2006'
1808 * @return {Date} The new Date instance
1810 Date.prototype.clone = function() {
1811 return new Date(this.getTime());
1815 * Clears any time information from this date
1816 @param {Boolean} clone true to create a clone of this date, clear the time and return it
1817 @return {Date} this or the clone
1819 Date.prototype.clearTime = function(clone){
1821 return this.clone().clearTime();
1826 this.setMilliseconds(0);
1831 // safari setMonth is broken -- check that this is only donw once...
1832 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1833 Date.brokenSetMonth = Date.prototype.setMonth;
1834 Date.prototype.setMonth = function(num){
1836 var n = Math.ceil(-num);
1837 var back_year = Math.ceil(n/12);
1838 var month = (n % 12) ? 12 - n % 12 : 0 ;
1839 this.setFullYear(this.getFullYear() - back_year);
1840 return Date.brokenSetMonth.call(this, month);
1842 return Date.brokenSetMonth.apply(this, arguments);
1847 /** Date interval constant
1851 /** Date interval constant
1855 /** Date interval constant
1859 /** Date interval constant
1863 /** Date interval constant
1867 /** Date interval constant
1871 /** Date interval constant
1877 * Provides a convenient method of performing basic date arithmetic. This method
1878 * does not modify the Date instance being called - it creates and returns
1879 * a new Date instance containing the resulting date value.
1884 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1885 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1887 //Negative values will subtract correctly:
1888 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1889 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1891 //You can even chain several calls together in one line!
1892 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1893 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1896 * @param {String} interval A valid date interval enum value
1897 * @param {Number} value The amount to add to the current date
1898 * @return {Date} The new Date instance
1900 Date.prototype.add = function(interval, value){
1901 var d = this.clone();
1902 if (!interval || value === 0) { return d; }
1903 switch(interval.toLowerCase()){
1905 d.setMilliseconds(this.getMilliseconds() + value);
1908 d.setSeconds(this.getSeconds() + value);
1911 d.setMinutes(this.getMinutes() + value);
1914 d.setHours(this.getHours() + value);
1917 d.setDate(this.getDate() + value);
1920 var day = this.getDate();
1922 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1925 d.setMonth(this.getMonth() + value);
1928 d.setFullYear(this.getFullYear() + value);
1934 * @class Roo.lib.Dom
1938 * Dom utils (from YIU afaik)
1944 * Get the view width
1945 * @param {Boolean} full True will get the full document, otherwise it's the view width
1946 * @return {Number} The width
1949 getViewWidth : function(full) {
1950 return full ? this.getDocumentWidth() : this.getViewportWidth();
1953 * Get the view height
1954 * @param {Boolean} full True will get the full document, otherwise it's the view height
1955 * @return {Number} The height
1957 getViewHeight : function(full) {
1958 return full ? this.getDocumentHeight() : this.getViewportHeight();
1961 * Get the Full Document height
1962 * @return {Number} The height
1964 getDocumentHeight: function() {
1965 var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1966 return Math.max(scrollHeight, this.getViewportHeight());
1969 * Get the Full Document width
1970 * @return {Number} The width
1972 getDocumentWidth: function() {
1973 var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1974 return Math.max(scrollWidth, this.getViewportWidth());
1977 * Get the Window Viewport height
1978 * @return {Number} The height
1980 getViewportHeight: function() {
1981 var height = self.innerHeight;
1982 var mode = document.compatMode;
1984 if ((mode || Roo.isIE) && !Roo.isOpera) {
1985 height = (mode == "CSS1Compat") ?
1986 document.documentElement.clientHeight :
1987 document.body.clientHeight;
1993 * Get the Window Viewport width
1994 * @return {Number} The width
1996 getViewportWidth: function() {
1997 var width = self.innerWidth;
1998 var mode = document.compatMode;
2000 if (mode || Roo.isIE) {
2001 width = (mode == "CSS1Compat") ?
2002 document.documentElement.clientWidth :
2003 document.body.clientWidth;
2008 isAncestor : function(p, c) {
2015 if (p.contains && !Roo.isSafari) {
2016 return p.contains(c);
2017 } else if (p.compareDocumentPosition) {
2018 return !!(p.compareDocumentPosition(c) & 16);
2020 var parent = c.parentNode;
2025 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2028 parent = parent.parentNode;
2034 getRegion : function(el) {
2035 return Roo.lib.Region.getRegion(el);
2038 getY : function(el) {
2039 return this.getXY(el)[1];
2042 getX : function(el) {
2043 return this.getXY(el)[0];
2046 getXY : function(el) {
2047 var p, pe, b, scroll, bd = document.body;
2048 el = Roo.getDom(el);
2049 var fly = Roo.lib.AnimBase.fly;
2050 if (el.getBoundingClientRect) {
2051 b = el.getBoundingClientRect();
2052 scroll = fly(document).getScroll();
2053 return [b.left + scroll.left, b.top + scroll.top];
2059 var hasAbsolute = fly(el).getStyle("position") == "absolute";
2066 if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2073 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2074 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2081 if (p != el && pe.getStyle('overflow') != 'visible') {
2089 if (Roo.isSafari && hasAbsolute) {
2094 if (Roo.isGecko && !hasAbsolute) {
2096 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2097 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2101 while (p && p != bd) {
2102 if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2114 setXY : function(el, xy) {
2115 el = Roo.fly(el, '_setXY');
2117 var pts = el.translatePoints(xy);
2118 if (xy[0] !== false) {
2119 el.dom.style.left = pts.left + "px";
2121 if (xy[1] !== false) {
2122 el.dom.style.top = pts.top + "px";
2126 setX : function(el, x) {
2127 this.setXY(el, [x, false]);
2130 setY : function(el, y) {
2131 this.setXY(el, [false, y]);
2135 * Portions of this file are based on pieces of Yahoo User Interface Library
2136 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2137 * YUI licensed under the BSD License:
2138 * http://developer.yahoo.net/yui/license.txt
2139 * <script type="text/javascript">
2143 Roo.lib.Event = function() {
2144 var loadComplete = false;
2146 var unloadListeners = [];
2148 var onAvailStack = [];
2150 var lastError = null;
2163 startInterval: function() {
2164 if (!this._interval) {
2166 var callback = function() {
2167 self._tryPreloadAttach();
2169 this._interval = setInterval(callback, this.POLL_INTERVAL);
2174 onAvailable: function(p_id, p_fn, p_obj, p_override) {
2175 onAvailStack.push({ id: p_id,
2178 override: p_override,
2179 checkReady: false });
2181 retryCount = this.POLL_RETRYS;
2182 this.startInterval();
2186 addListener: function(el, eventName, fn) {
2187 el = Roo.getDom(el);
2192 if ("unload" == eventName) {
2193 unloadListeners[unloadListeners.length] =
2194 [el, eventName, fn];
2198 var wrappedFn = function(e) {
2199 return fn(Roo.lib.Event.getEvent(e));
2202 var li = [el, eventName, fn, wrappedFn];
2204 var index = listeners.length;
2205 listeners[index] = li;
2207 this.doAdd(el, eventName, wrappedFn, false);
2213 removeListener: function(el, eventName, fn) {
2216 el = Roo.getDom(el);
2219 return this.purgeElement(el, false, eventName);
2223 if ("unload" == eventName) {
2225 for (i = 0,len = unloadListeners.length; i < len; i++) {
2226 var li = unloadListeners[i];
2229 li[1] == eventName &&
2231 unloadListeners.splice(i, 1);
2239 var cacheItem = null;
2242 var index = arguments[3];
2244 if ("undefined" == typeof index) {
2245 index = this._getCacheIndex(el, eventName, fn);
2249 cacheItem = listeners[index];
2252 if (!el || !cacheItem) {
2256 this.doRemove(el, eventName, cacheItem[this.WFN], false);
2258 delete listeners[index][this.WFN];
2259 delete listeners[index][this.FN];
2260 listeners.splice(index, 1);
2267 getTarget: function(ev, resolveTextNode) {
2268 ev = ev.browserEvent || ev;
2269 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2270 var t = ev.target || ev.srcElement;
2271 return this.resolveTextNode(t);
2275 resolveTextNode: function(node) {
2276 if (Roo.isSafari && node && 3 == node.nodeType) {
2277 return node.parentNode;
2284 getPageX: function(ev) {
2285 ev = ev.browserEvent || ev;
2286 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2288 if (!x && 0 !== x) {
2289 x = ev.clientX || 0;
2292 x += this.getScroll()[1];
2300 getPageY: function(ev) {
2301 ev = ev.browserEvent || ev;
2302 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2304 if (!y && 0 !== y) {
2305 y = ev.clientY || 0;
2308 y += this.getScroll()[0];
2317 getXY: function(ev) {
2318 ev = ev.browserEvent || ev;
2319 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2320 return [this.getPageX(ev), this.getPageY(ev)];
2324 getRelatedTarget: function(ev) {
2325 ev = ev.browserEvent || ev;
2326 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2327 var t = ev.relatedTarget;
2329 if (ev.type == "mouseout") {
2331 } else if (ev.type == "mouseover") {
2336 return this.resolveTextNode(t);
2340 getTime: function(ev) {
2341 ev = ev.browserEvent || ev;
2342 ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev ) : ev;
2344 var t = new Date().getTime();
2348 this.lastError = ex;
2357 stopEvent: function(ev) {
2358 this.stopPropagation(ev);
2359 this.preventDefault(ev);
2363 stopPropagation: function(ev) {
2364 ev = ev.browserEvent || ev;
2365 if (ev.stopPropagation) {
2366 ev.stopPropagation();
2368 ev.cancelBubble = true;
2373 preventDefault: function(ev) {
2374 ev = ev.browserEvent || ev;
2375 if(ev.preventDefault) {
2376 ev.preventDefault();
2378 ev.returnValue = false;
2383 getEvent: function(e) {
2384 var ev = e || window.event;
2386 var c = this.getEvent.caller;
2388 ev = c.arguments[0];
2389 if (ev && Event == ev.constructor) {
2399 getCharCode: function(ev) {
2400 ev = ev.browserEvent || ev;
2401 return ev.charCode || ev.keyCode || 0;
2405 _getCacheIndex: function(el, eventName, fn) {
2406 for (var i = 0,len = listeners.length; i < len; ++i) {
2407 var li = listeners[i];
2409 li[this.FN] == fn &&
2410 li[this.EL] == el &&
2411 li[this.TYPE] == eventName) {
2423 getEl: function(id) {
2424 return document.getElementById(id);
2428 clearCache: function() {
2432 _load: function(e) {
2433 loadComplete = true;
2434 var EU = Roo.lib.Event;
2438 EU.doRemove(window, "load", EU._load);
2443 _tryPreloadAttach: function() {
2452 var tryAgain = !loadComplete;
2454 tryAgain = (retryCount > 0);
2459 for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2460 var item = onAvailStack[i];
2462 var el = this.getEl(item.id);
2465 if (!item.checkReady ||
2468 (document && document.body)) {
2471 if (item.override) {
2472 if (item.override === true) {
2475 scope = item.override;
2478 item.fn.call(scope, item.obj);
2479 onAvailStack[i] = null;
2482 notAvail.push(item);
2487 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2491 this.startInterval();
2493 clearInterval(this._interval);
2494 this._interval = null;
2497 this.locked = false;
2504 purgeElement: function(el, recurse, eventName) {
2505 var elListeners = this.getListeners(el, eventName);
2507 for (var i = 0,len = elListeners.length; i < len; ++i) {
2508 var l = elListeners[i];
2509 this.removeListener(el, l.type, l.fn);
2513 if (recurse && el && el.childNodes) {
2514 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2515 this.purgeElement(el.childNodes[i], recurse, eventName);
2521 getListeners: function(el, eventName) {
2522 var results = [], searchLists;
2524 searchLists = [listeners, unloadListeners];
2525 } else if (eventName == "unload") {
2526 searchLists = [unloadListeners];
2528 searchLists = [listeners];
2531 for (var j = 0; j < searchLists.length; ++j) {
2532 var searchList = searchLists[j];
2533 if (searchList && searchList.length > 0) {
2534 for (var i = 0,len = searchList.length; i < len; ++i) {
2535 var l = searchList[i];
2536 if (l && l[this.EL] === el &&
2537 (!eventName || eventName === l[this.TYPE])) {
2542 adjust: l[this.ADJ_SCOPE],
2550 return (results.length) ? results : null;
2554 _unload: function(e) {
2556 var EU = Roo.lib.Event, i, j, l, len, index;
2558 for (i = 0,len = unloadListeners.length; i < len; ++i) {
2559 l = unloadListeners[i];
2562 if (l[EU.ADJ_SCOPE]) {
2563 if (l[EU.ADJ_SCOPE] === true) {
2566 scope = l[EU.ADJ_SCOPE];
2569 l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2570 unloadListeners[i] = null;
2576 unloadListeners = null;
2578 if (listeners && listeners.length > 0) {
2579 j = listeners.length;
2582 l = listeners[index];
2584 EU.removeListener(l[EU.EL], l[EU.TYPE],
2594 EU.doRemove(window, "unload", EU._unload);
2599 getScroll: function() {
2600 var dd = document.documentElement, db = document.body;
2601 if (dd && (dd.scrollTop || dd.scrollLeft)) {
2602 return [dd.scrollTop, dd.scrollLeft];
2604 return [db.scrollTop, db.scrollLeft];
2611 doAdd: function () {
2612 if (window.addEventListener) {
2613 return function(el, eventName, fn, capture) {
2614 el.addEventListener(eventName, fn, (capture));
2616 } else if (window.attachEvent) {
2617 return function(el, eventName, fn, capture) {
2618 el.attachEvent("on" + eventName, fn);
2627 doRemove: function() {
2628 if (window.removeEventListener) {
2629 return function (el, eventName, fn, capture) {
2630 el.removeEventListener(eventName, fn, (capture));
2632 } else if (window.detachEvent) {
2633 return function (el, eventName, fn) {
2634 el.detachEvent("on" + eventName, fn);
2646 var E = Roo.lib.Event;
2647 E.on = E.addListener;
2648 E.un = E.removeListener;
2650 if (document && document.body) {
2653 E.doAdd(window, "load", E._load);
2655 E.doAdd(window, "unload", E._unload);
2656 E._tryPreloadAttach();
2663 * @class Roo.lib.Ajax
2665 * provide a simple Ajax request utility functions
2667 * Portions of this file are based on pieces of Yahoo User Interface Library
2668 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2669 * YUI licensed under the BSD License:
2670 * http://developer.yahoo.net/yui/license.txt
2671 * <script type="text/javascript">
2679 request : function(method, uri, cb, data, options) {
2681 var hs = options.headers;
2684 if(hs.hasOwnProperty(h)){
2685 this.initHeader(h, hs[h], false);
2689 if(options.xmlData){
2690 this.initHeader('Content-Type', 'text/xml', false);
2692 data = options.xmlData;
2696 return this.asyncRequest(method, uri, cb, data);
2702 * @param {DomForm} form element
2703 * @return {String} urlencode form output.
2705 serializeForm : function(form) {
2706 if(typeof form == 'string') {
2707 form = (document.getElementById(form) || document.forms[form]);
2710 var el, name, val, disabled, data = '', hasSubmit = false;
2711 for (var i = 0; i < form.elements.length; i++) {
2712 el = form.elements[i];
2713 disabled = form.elements[i].disabled;
2714 name = form.elements[i].name;
2715 val = form.elements[i].value;
2717 if (!disabled && name){
2721 case 'select-multiple':
2722 for (var j = 0; j < el.options.length; j++) {
2723 if (el.options[j].selected) {
2725 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2728 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2736 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2749 if(hasSubmit == false) {
2750 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2755 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2760 data = data.substr(0, data.length - 1);
2768 useDefaultHeader:true,
2770 defaultPostHeader:'application/x-www-form-urlencoded',
2772 useDefaultXhrHeader:true,
2774 defaultXhrHeader:'XMLHttpRequest',
2776 hasDefaultHeaders:true,
2788 setProgId:function(id)
2790 this.activeX.unshift(id);
2793 setDefaultPostHeader:function(b)
2795 this.useDefaultHeader = b;
2798 setDefaultXhrHeader:function(b)
2800 this.useDefaultXhrHeader = b;
2803 setPollingInterval:function(i)
2805 if (typeof i == 'number' && isFinite(i)) {
2806 this.pollInterval = i;
2810 createXhrObject:function(transactionId)
2816 http = new XMLHttpRequest();
2818 obj = { conn:http, tId:transactionId };
2822 for (var i = 0; i < this.activeX.length; ++i) {
2826 http = new ActiveXObject(this.activeX[i]);
2828 obj = { conn:http, tId:transactionId };
2841 getConnectionObject:function()
2844 var tId = this.transactionId;
2848 o = this.createXhrObject(tId);
2850 this.transactionId++;
2861 asyncRequest:function(method, uri, callback, postData)
2863 var o = this.getConnectionObject();
2869 o.conn.open(method, uri, true);
2871 if (this.useDefaultXhrHeader) {
2872 if (!this.defaultHeaders['X-Requested-With']) {
2873 this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2877 if(postData && this.useDefaultHeader){
2878 this.initHeader('Content-Type', this.defaultPostHeader);
2881 if (this.hasDefaultHeaders || this.hasHeaders) {
2885 this.handleReadyState(o, callback);
2886 o.conn.send(postData || null);
2892 handleReadyState:function(o, callback)
2896 if (callback && callback.timeout) {
2898 this.timeout[o.tId] = window.setTimeout(function() {
2899 oConn.abort(o, callback, true);
2900 }, callback.timeout);
2903 this.poll[o.tId] = window.setInterval(
2905 if (o.conn && o.conn.readyState == 4) {
2906 window.clearInterval(oConn.poll[o.tId]);
2907 delete oConn.poll[o.tId];
2909 if(callback && callback.timeout) {
2910 window.clearTimeout(oConn.timeout[o.tId]);
2911 delete oConn.timeout[o.tId];
2914 oConn.handleTransactionResponse(o, callback);
2917 , this.pollInterval);
2920 handleTransactionResponse:function(o, callback, isAbort)
2924 this.releaseObject(o);
2928 var httpStatus, responseObject;
2932 if (o.conn.status !== undefined && o.conn.status != 0) {
2933 httpStatus = o.conn.status;
2945 if (httpStatus >= 200 && httpStatus < 300) {
2946 responseObject = this.createResponseObject(o, callback.argument);
2947 if (callback.success) {
2948 if (!callback.scope) {
2949 callback.success(responseObject);
2954 callback.success.apply(callback.scope, [responseObject]);
2959 switch (httpStatus) {
2967 responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2968 if (callback.failure) {
2969 if (!callback.scope) {
2970 callback.failure(responseObject);
2973 callback.failure.apply(callback.scope, [responseObject]);
2978 responseObject = this.createResponseObject(o, callback.argument);
2979 if (callback.failure) {
2980 if (!callback.scope) {
2981 callback.failure(responseObject);
2984 callback.failure.apply(callback.scope, [responseObject]);
2990 this.releaseObject(o);
2991 responseObject = null;
2994 createResponseObject:function(o, callbackArg)
3001 var headerStr = o.conn.getAllResponseHeaders();
3002 var header = headerStr.split('\n');
3003 for (var i = 0; i < header.length; i++) {
3004 var delimitPos = header[i].indexOf(':');
3005 if (delimitPos != -1) {
3006 headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3014 obj.status = o.conn.status;
3015 obj.statusText = o.conn.statusText;
3016 obj.getResponseHeader = headerObj;
3017 obj.getAllResponseHeaders = headerStr;
3018 obj.responseText = o.conn.responseText;
3019 obj.responseXML = o.conn.responseXML;
3021 if (typeof callbackArg !== undefined) {
3022 obj.argument = callbackArg;
3028 createExceptionObject:function(tId, callbackArg, isAbort)
3031 var COMM_ERROR = 'communication failure';
3032 var ABORT_CODE = -1;
3033 var ABORT_ERROR = 'transaction aborted';
3039 obj.status = ABORT_CODE;
3040 obj.statusText = ABORT_ERROR;
3043 obj.status = COMM_CODE;
3044 obj.statusText = COMM_ERROR;
3048 obj.argument = callbackArg;
3054 initHeader:function(label, value, isDefault)
3056 var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3058 if (headerObj[label] === undefined) {
3059 headerObj[label] = value;
3064 headerObj[label] = value + "," + headerObj[label];
3068 this.hasDefaultHeaders = true;
3071 this.hasHeaders = true;
3076 setHeader:function(o)
3078 if (this.hasDefaultHeaders) {
3079 for (var prop in this.defaultHeaders) {
3080 if (this.defaultHeaders.hasOwnProperty(prop)) {
3081 o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3086 if (this.hasHeaders) {
3087 for (var prop in this.headers) {
3088 if (this.headers.hasOwnProperty(prop)) {
3089 o.conn.setRequestHeader(prop, this.headers[prop]);
3093 this.hasHeaders = false;
3097 resetDefaultHeaders:function() {
3098 delete this.defaultHeaders;
3099 this.defaultHeaders = {};
3100 this.hasDefaultHeaders = false;
3103 abort:function(o, callback, isTimeout)
3105 if(this.isCallInProgress(o)) {
3107 window.clearInterval(this.poll[o.tId]);
3108 delete this.poll[o.tId];
3110 delete this.timeout[o.tId];
3113 this.handleTransactionResponse(o, callback, true);
3123 isCallInProgress:function(o)
3126 return o.conn.readyState != 4 && o.conn.readyState != 0;
3135 releaseObject:function(o)
3144 'MSXML2.XMLHTTP.3.0',
3152 * Portions of this file are based on pieces of Yahoo User Interface Library
3153 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3154 * YUI licensed under the BSD License:
3155 * http://developer.yahoo.net/yui/license.txt
3156 * <script type="text/javascript">
3160 Roo.lib.Region = function(t, r, b, l) {
3170 Roo.lib.Region.prototype = {
3171 contains : function(region) {
3172 return ( region.left >= this.left &&
3173 region.right <= this.right &&
3174 region.top >= this.top &&
3175 region.bottom <= this.bottom );
3179 getArea : function() {
3180 return ( (this.bottom - this.top) * (this.right - this.left) );
3183 intersect : function(region) {
3184 var t = Math.max(this.top, region.top);
3185 var r = Math.min(this.right, region.right);
3186 var b = Math.min(this.bottom, region.bottom);
3187 var l = Math.max(this.left, region.left);
3189 if (b >= t && r >= l) {
3190 return new Roo.lib.Region(t, r, b, l);
3195 union : function(region) {
3196 var t = Math.min(this.top, region.top);
3197 var r = Math.max(this.right, region.right);
3198 var b = Math.max(this.bottom, region.bottom);
3199 var l = Math.min(this.left, region.left);
3201 return new Roo.lib.Region(t, r, b, l);
3204 adjust : function(t, l, b, r) {
3213 Roo.lib.Region.getRegion = function(el) {
3214 var p = Roo.lib.Dom.getXY(el);
3217 var r = p[0] + el.offsetWidth;
3218 var b = p[1] + el.offsetHeight;
3221 return new Roo.lib.Region(t, r, b, l);
3224 * Portions of this file are based on pieces of Yahoo User Interface Library
3225 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3226 * YUI licensed under the BSD License:
3227 * http://developer.yahoo.net/yui/license.txt
3228 * <script type="text/javascript">
3231 //@@dep Roo.lib.Region
3234 Roo.lib.Point = function(x, y) {
3235 if (x instanceof Array) {
3239 this.x = this.right = this.left = this[0] = x;
3240 this.y = this.top = this.bottom = this[1] = y;
3243 Roo.lib.Point.prototype = new Roo.lib.Region();
3245 * Portions of this file are based on pieces of Yahoo User Interface Library
3246 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3247 * YUI licensed under the BSD License:
3248 * http://developer.yahoo.net/yui/license.txt
3249 * <script type="text/javascript">
3256 scroll : function(el, args, duration, easing, cb, scope) {
3257 this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3260 motion : function(el, args, duration, easing, cb, scope) {
3261 this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3264 color : function(el, args, duration, easing, cb, scope) {
3265 this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3268 run : function(el, args, duration, easing, cb, scope, type) {
3269 type = type || Roo.lib.AnimBase;
3270 if (typeof easing == "string") {
3271 easing = Roo.lib.Easing[easing];
3273 var anim = new type(el, args, duration, easing);
3274 anim.animateX(function() {
3275 Roo.callback(cb, scope);
3281 * Portions of this file are based on pieces of Yahoo User Interface Library
3282 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3283 * YUI licensed under the BSD License:
3284 * http://developer.yahoo.net/yui/license.txt
3285 * <script type="text/javascript">
3293 if (!libFlyweight) {
3294 libFlyweight = new Roo.Element.Flyweight();
3296 libFlyweight.dom = el;
3297 return libFlyweight;
3300 // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3304 Roo.lib.AnimBase = function(el, attributes, duration, method) {
3306 this.init(el, attributes, duration, method);
3310 Roo.lib.AnimBase.fly = fly;
3314 Roo.lib.AnimBase.prototype = {
3316 toString: function() {
3317 var el = this.getEl();
3318 var id = el.id || el.tagName;
3319 return ("Anim " + id);
3323 noNegatives: /width|height|opacity|padding/i,
3324 offsetAttribute: /^((width|height)|(top|left))$/,
3325 defaultUnit: /width|height|top$|bottom$|left$|right$/i,
3326 offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3330 doMethod: function(attr, start, end) {
3331 return this.method(this.currentFrame, start, end - start, this.totalFrames);
3335 setAttribute: function(attr, val, unit) {
3336 if (this.patterns.noNegatives.test(attr)) {
3337 val = (val > 0) ? val : 0;
3340 Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3344 getAttribute: function(attr) {
3345 var el = this.getEl();
3346 var val = fly(el).getStyle(attr);
3348 if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3349 return parseFloat(val);
3352 var a = this.patterns.offsetAttribute.exec(attr) || [];
3353 var pos = !!( a[3] );
3354 var box = !!( a[2] );
3357 if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3358 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3367 getDefaultUnit: function(attr) {
3368 if (this.patterns.defaultUnit.test(attr)) {
3375 animateX : function(callback, scope) {
3376 var f = function() {
3377 this.onComplete.removeListener(f);
3378 if (typeof callback == "function") {
3379 callback.call(scope || this, this);
3382 this.onComplete.addListener(f, this);
3387 setRuntimeAttribute: function(attr) {
3390 var attributes = this.attributes;
3392 this.runtimeAttributes[attr] = {};
3394 var isset = function(prop) {
3395 return (typeof prop !== 'undefined');
3398 if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3402 start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3405 if (isset(attributes[attr]['to'])) {
3406 end = attributes[attr]['to'];
3407 } else if (isset(attributes[attr]['by'])) {
3408 if (start.constructor == Array) {
3410 for (var i = 0, len = start.length; i < len; ++i) {
3411 end[i] = start[i] + attributes[attr]['by'][i];
3414 end = start + attributes[attr]['by'];
3418 this.runtimeAttributes[attr].start = start;
3419 this.runtimeAttributes[attr].end = end;
3422 this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3426 init: function(el, attributes, duration, method) {
3428 var isAnimated = false;
3431 var startTime = null;
3434 var actualFrames = 0;
3437 el = Roo.getDom(el);
3440 this.attributes = attributes || {};
3443 this.duration = duration || 1;
3446 this.method = method || Roo.lib.Easing.easeNone;
3449 this.useSeconds = true;
3452 this.currentFrame = 0;
3455 this.totalFrames = Roo.lib.AnimMgr.fps;
3458 this.getEl = function() {
3463 this.isAnimated = function() {
3468 this.getStartTime = function() {
3472 this.runtimeAttributes = {};
3475 this.animate = function() {
3476 if (this.isAnimated()) {
3480 this.currentFrame = 0;
3482 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3484 Roo.lib.AnimMgr.registerElement(this);
3488 this.stop = function(finish) {
3490 this.currentFrame = this.totalFrames;
3491 this._onTween.fire();
3493 Roo.lib.AnimMgr.stop(this);
3496 var onStart = function() {
3497 this.onStart.fire();
3499 this.runtimeAttributes = {};
3500 for (var attr in this.attributes) {
3501 this.setRuntimeAttribute(attr);
3506 startTime = new Date();
3510 var onTween = function() {
3512 duration: new Date() - this.getStartTime(),
3513 currentFrame: this.currentFrame
3516 data.toString = function() {
3518 'duration: ' + data.duration +
3519 ', currentFrame: ' + data.currentFrame
3523 this.onTween.fire(data);
3525 var runtimeAttributes = this.runtimeAttributes;
3527 for (var attr in runtimeAttributes) {
3528 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3534 var onComplete = function() {
3535 var actual_duration = (new Date() - startTime) / 1000 ;
3538 duration: actual_duration,
3539 frames: actualFrames,
3540 fps: actualFrames / actual_duration
3543 data.toString = function() {
3545 'duration: ' + data.duration +
3546 ', frames: ' + data.frames +
3547 ', fps: ' + data.fps
3553 this.onComplete.fire(data);
3557 this._onStart = new Roo.util.Event(this);
3558 this.onStart = new Roo.util.Event(this);
3559 this.onTween = new Roo.util.Event(this);
3560 this._onTween = new Roo.util.Event(this);
3561 this.onComplete = new Roo.util.Event(this);
3562 this._onComplete = new Roo.util.Event(this);
3563 this._onStart.addListener(onStart);
3564 this._onTween.addListener(onTween);
3565 this._onComplete.addListener(onComplete);
3570 * Portions of this file are based on pieces of Yahoo User Interface Library
3571 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3572 * YUI licensed under the BSD License:
3573 * http://developer.yahoo.net/yui/license.txt
3574 * <script type="text/javascript">
3578 Roo.lib.AnimMgr = new function() {
3595 this.registerElement = function(tween) {
3596 queue[queue.length] = tween;
3598 tween._onStart.fire();
3603 this.unRegister = function(tween, index) {
3604 tween._onComplete.fire();
3605 index = index || getIndex(tween);
3607 queue.splice(index, 1);
3611 if (tweenCount <= 0) {
3617 this.start = function() {
3618 if (thread === null) {
3619 thread = setInterval(this.run, this.delay);
3624 this.stop = function(tween) {
3626 clearInterval(thread);
3628 for (var i = 0, len = queue.length; i < len; ++i) {
3629 if (queue[0].isAnimated()) {
3630 this.unRegister(queue[0], 0);
3639 this.unRegister(tween);
3644 this.run = function() {
3645 for (var i = 0, len = queue.length; i < len; ++i) {
3646 var tween = queue[i];
3647 if (!tween || !tween.isAnimated()) {
3651 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3653 tween.currentFrame += 1;
3655 if (tween.useSeconds) {
3656 correctFrame(tween);
3658 tween._onTween.fire();
3661 Roo.lib.AnimMgr.stop(tween, i);
3666 var getIndex = function(anim) {
3667 for (var i = 0, len = queue.length; i < len; ++i) {
3668 if (queue[i] == anim) {
3676 var correctFrame = function(tween) {
3677 var frames = tween.totalFrames;
3678 var frame = tween.currentFrame;
3679 var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3680 var elapsed = (new Date() - tween.getStartTime());
3683 if (elapsed < tween.duration * 1000) {
3684 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3686 tweak = frames - (frame + 1);
3688 if (tweak > 0 && isFinite(tweak)) {
3689 if (tween.currentFrame + tweak >= frames) {
3690 tweak = frames - (frame + 1);
3693 tween.currentFrame += tweak;
3699 * Portions of this file are based on pieces of Yahoo User Interface Library
3700 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701 * YUI licensed under the BSD License:
3702 * http://developer.yahoo.net/yui/license.txt
3703 * <script type="text/javascript">
3706 Roo.lib.Bezier = new function() {
3708 this.getPosition = function(points, t) {
3709 var n = points.length;
3712 for (var i = 0; i < n; ++i) {
3713 tmp[i] = [points[i][0], points[i][1]];
3716 for (var j = 1; j < n; ++j) {
3717 for (i = 0; i < n - j; ++i) {
3718 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3719 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3723 return [ tmp[0][0], tmp[0][1] ];
3729 * @class Roo.lib.Color
3731 * An abstract Color implementation. Concrete Color implementations should use
3732 * an instance of this function as their prototype, and implement the getRGB and
3733 * getHSL functions. getRGB should return an object representing the RGB
3734 * components of this Color, with the red, green, and blue components in the
3735 * range [0,255] and the alpha component in the range [0,100]. getHSL should
3736 * return an object representing the HSL components of this Color, with the hue
3737 * component in the range [0,360), the saturation and lightness components in
3738 * the range [0,100], and the alpha component in the range [0,1].
3743 * Functions for Color handling and processing.
3745 * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3747 * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3748 * rights to this program, with the intention of it becoming part of the public
3749 * domain. Because this program is released into the public domain, it comes with
3750 * no warranty either expressed or implied, to the extent permitted by law.
3752 * For more free and public domain JavaScript code by the same author, visit:
3753 * http://www.safalra.com/web-design/javascript/
3756 Roo.lib.Color = function() { }
3759 Roo.apply(Roo.lib.Color.prototype, {
3767 * @return {Object} an object representing the RGBA components of this Color. The red,
3768 * green, and blue components are converted to integers in the range [0,255].
3769 * The alpha is a value in the range [0,1].
3771 getIntegerRGB : function(){
3773 // get the RGB components of this Color
3774 var rgb = this.getRGB();
3776 // return the integer components
3778 'r' : Math.round(rgb.r),
3779 'g' : Math.round(rgb.g),
3780 'b' : Math.round(rgb.b),
3788 * @return {Object} an object representing the RGBA components of this Color. The red,
3789 * green, and blue components are converted to numbers in the range [0,100].
3790 * The alpha is a value in the range [0,1].
3792 getPercentageRGB : function(){
3794 // get the RGB components of this Color
3795 var rgb = this.getRGB();
3797 // return the percentage components
3799 'r' : 100 * rgb.r / 255,
3800 'g' : 100 * rgb.g / 255,
3801 'b' : 100 * rgb.b / 255,
3808 * getCSSHexadecimalRGB
3809 * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3810 * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3811 * are two-digit hexadecimal numbers.
3813 getCSSHexadecimalRGB : function()
3816 // get the integer RGB components
3817 var rgb = this.getIntegerRGB();
3819 // determine the hexadecimal equivalents
3820 var r16 = rgb.r.toString(16);
3821 var g16 = rgb.g.toString(16);
3822 var b16 = rgb.b.toString(16);
3824 // return the CSS RGB Color value
3826 + (r16.length == 2 ? r16 : '0' + r16)
3827 + (g16.length == 2 ? g16 : '0' + g16)
3828 + (b16.length == 2 ? b16 : '0' + b16);
3834 * @return {String} a string representing this Color as a CSS integer RGB Color
3835 * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3836 * are integers in the range [0,255].
3838 getCSSIntegerRGB : function(){
3840 // get the integer RGB components
3841 var rgb = this.getIntegerRGB();
3843 // return the CSS RGB Color value
3844 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3850 * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3851 * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3852 * b are integers in the range [0,255] and a is in the range [0,1].
3854 getCSSIntegerRGBA : function(){
3856 // get the integer RGB components
3857 var rgb = this.getIntegerRGB();
3859 // return the CSS integer RGBA Color value
3860 return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3865 * getCSSPercentageRGB
3866 * @return {String} a string representing this Color as a CSS percentage RGB Color
3867 * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3868 * b are in the range [0,100].
3870 getCSSPercentageRGB : function(){
3872 // get the percentage RGB components
3873 var rgb = this.getPercentageRGB();
3875 // return the CSS RGB Color value
3876 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3881 * getCSSPercentageRGBA
3882 * @return {String} a string representing this Color as a CSS percentage RGBA Color
3883 * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3884 * and b are in the range [0,100] and a is in the range [0,1].
3886 getCSSPercentageRGBA : function(){
3888 // get the percentage RGB components
3889 var rgb = this.getPercentageRGB();
3891 // return the CSS percentage RGBA Color value
3892 return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3898 * @return {String} a string representing this Color as a CSS HSL Color value - that
3899 * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3900 * s and l are in the range [0,100].
3902 getCSSHSL : function(){
3904 // get the HSL components
3905 var hsl = this.getHSL();
3907 // return the CSS HSL Color value
3908 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3914 * @return {String} a string representing this Color as a CSS HSLA Color value - that
3915 * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3916 * s and l are in the range [0,100], and a is in the range [0,1].
3918 getCSSHSLA : function(){
3920 // get the HSL components
3921 var hsl = this.getHSL();
3923 // return the CSS HSL Color value
3924 return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3929 * Sets the Color of the specified node to this Color. This functions sets
3930 * the CSS 'color' property for the node. The parameter is:
3932 * @param {DomElement} node - the node whose Color should be set
3934 setNodeColor : function(node){
3936 // set the Color of the node
3937 node.style.color = this.getCSSHexadecimalRGB();
3942 * Sets the background Color of the specified node to this Color. This
3943 * functions sets the CSS 'background-color' property for the node. The
3946 * @param {DomElement} node - the node whose background Color should be set
3948 setNodeBackgroundColor : function(node){
3950 // set the background Color of the node
3951 node.style.backgroundColor = this.getCSSHexadecimalRGB();
3954 // convert between formats..
3957 var r = this.getIntegerRGB();
3958 return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3963 var hsl = this.getHSL();
3964 // return the CSS HSL Color value
3965 return new Roo.lib.HSLColor(hsl.h, hsl.s, hsl.l , hsl.a );
3971 var rgb = this.toRGB();
3972 var hsv = rgb.getHSV();
3973 // return the CSS HSL Color value
3974 return new Roo.lib.HSVColor(hsv.h, hsv.s, hsv.v , hsv.a );
3978 // modify v = 0 ... 1 (eg. 0.5)
3979 saturate : function(v)
3981 var rgb = this.toRGB();
3982 var hsv = rgb.getHSV();
3983 return new Roo.lib.HSVColor(hsv.h, hsv.s * v, hsv.v , hsv.a );
3991 * @return {Object} the RGB and alpha components of this Color as an object with r,
3992 * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3997 // return the RGB components
4009 * @return {Object} the HSV and alpha components of this Color as an object with h,
4010 * s, v, and a properties. h is in the range [0,360), s and v are in the range
4011 * [0,100], and a is in the range [0,1].
4016 // calculate the HSV components if necessary
4017 if (this.hsv == null) {
4018 this.calculateHSV();
4021 // return the HSV components
4033 * @return {Object} the HSL and alpha components of this Color as an object with h,
4034 * s, l, and a properties. h is in the range [0,360), s and l are in the range
4035 * [0,100], and a is in the range [0,1].
4037 getHSL : function(){
4040 // calculate the HSV components if necessary
4041 if (this.hsl == null) { this.calculateHSL(); }
4043 // return the HSL components
4058 * @class Roo.lib.RGBColor
4059 * @extends Roo.lib.Color
4060 * Creates a Color specified in the RGB Color space, with an optional alpha
4061 * component. The parameters are:
4065 * @param {Number} r - the red component, clipped to the range [0,255]
4066 * @param {Number} g - the green component, clipped to the range [0,255]
4067 * @param {Number} b - the blue component, clipped to the range [0,255]
4068 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4069 * optional and defaults to 1
4071 Roo.lib.RGBColor = function (r, g, b, a){
4073 // store the alpha component after clipping it if necessary
4074 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4076 // store the RGB components after clipping them if necessary
4079 'r' : Math.max(0, Math.min(255, r)),
4080 'g' : Math.max(0, Math.min(255, g)),
4081 'b' : Math.max(0, Math.min(255, b))
4084 // initialise the HSV and HSL components to null
4088 * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4089 * range [0,360). The parameters are:
4091 * maximum - the maximum of the RGB component values
4092 * range - the range of the RGB component values
4097 // this does an 'exteds'
4098 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4101 getHue : function(maximum, range)
4105 // check whether the range is zero
4108 // set the hue to zero (any hue is acceptable as the Color is grey)
4113 // determine which of the components has the highest value and set the hue
4116 // red has the highest value
4118 var hue = (rgb.g - rgb.b) / range * 60;
4119 if (hue < 0) { hue += 360; }
4122 // green has the highest value
4124 var hue = (rgb.b - rgb.r) / range * 60 + 120;
4127 // blue has the highest value
4129 var hue = (rgb.r - rgb.g) / range * 60 + 240;
4141 /* //private Calculates and stores the HSV components of this RGBColor so that they can
4142 * be returned be the getHSV function.
4144 calculateHSV : function(){
4146 // get the maximum and range of the RGB component values
4147 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4148 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4150 // store the HSV components
4153 'h' : this.getHue(maximum, range),
4154 's' : (maximum == 0 ? 0 : 100 * range / maximum),
4155 'v' : maximum / 2.55
4160 /* //private Calculates and stores the HSL components of this RGBColor so that they can
4161 * be returned be the getHSL function.
4163 calculateHSL : function(){
4165 // get the maximum and range of the RGB component values
4166 var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4167 var range = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4169 // determine the lightness in the range [0,1]
4170 var l = maximum / 255 - range / 510;
4172 // store the HSL components
4175 'h' : this.getHue(maximum, range),
4176 's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4185 * @class Roo.lib.HSVColor
4186 * @extends Roo.lib.Color
4187 * Creates a Color specified in the HSV Color space, with an optional alpha
4188 * component. The parameters are:
4191 * @param {Number} h - the hue component, wrapped to the range [0,360)
4192 * @param {Number} s - the saturation component, clipped to the range [0,100]
4193 * @param {Number} v - the value component, clipped to the range [0,100]
4194 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4195 * optional and defaults to 1
4197 Roo.lib.HSVColor = function (h, s, v, a){
4199 // store the alpha component after clipping it if necessary
4200 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4202 // store the HSV components after clipping or wrapping them if necessary
4205 'h' : (h % 360 + 360) % 360,
4206 's' : Math.max(0, Math.min(100, s)),
4207 'v' : Math.max(0, Math.min(100, v))
4210 // initialise the RGB and HSL components to null
4215 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4216 /* Calculates and stores the RGB components of this HSVColor so that they can
4217 * be returned be the getRGB function.
4219 calculateRGB: function ()
4222 // check whether the saturation is zero
4225 // set the Color to the appropriate shade of grey
4232 // set some temporary values
4233 var f = hsv.h / 60 - Math.floor(hsv.h / 60);
4234 var p = hsv.v * (1 - hsv.s / 100);
4235 var q = hsv.v * (1 - hsv.s / 100 * f);
4236 var t = hsv.v * (1 - hsv.s / 100 * (1 - f));
4238 // set the RGB Color components to their temporary values
4239 switch (Math.floor(hsv.h / 60)){
4240 case 0: var r = hsv.v; var g = t; var b = p; break;
4241 case 1: var r = q; var g = hsv.v; var b = p; break;
4242 case 2: var r = p; var g = hsv.v; var b = t; break;
4243 case 3: var r = p; var g = q; var b = hsv.v; break;
4244 case 4: var r = t; var g = p; var b = hsv.v; break;
4245 case 5: var r = hsv.v; var g = p; var b = q; break;
4250 // store the RGB components
4260 /* Calculates and stores the HSL components of this HSVColor so that they can
4261 * be returned be the getHSL function.
4263 calculateHSL : function (){
4266 // determine the lightness in the range [0,100]
4267 var l = (2 - hsv.s / 100) * hsv.v / 2;
4269 // store the HSL components
4273 's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4277 // correct a division-by-zero error
4278 if (isNaN(hsl.s)) { hsl.s = 0; }
4287 * @class Roo.lib.HSLColor
4288 * @extends Roo.lib.Color
4291 * Creates a Color specified in the HSL Color space, with an optional alpha
4292 * component. The parameters are:
4294 * @param {Number} h - the hue component, wrapped to the range [0,360)
4295 * @param {Number} s - the saturation component, clipped to the range [0,100]
4296 * @param {Number} l - the lightness component, clipped to the range [0,100]
4297 * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4298 * optional and defaults to 1
4301 Roo.lib.HSLColor = function(h, s, l, a){
4303 // store the alpha component after clipping it if necessary
4304 this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4306 // store the HSL components after clipping or wrapping them if necessary
4309 'h' : (h % 360 + 360) % 360,
4310 's' : Math.max(0, Math.min(100, s)),
4311 'l' : Math.max(0, Math.min(100, l))
4314 // initialise the RGB and HSV components to null
4317 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4319 /* Calculates and stores the RGB components of this HSLColor so that they can
4320 * be returned be the getRGB function.
4322 calculateRGB: function (){
4324 // check whether the saturation is zero
4325 if (this.hsl.s == 0){
4327 // store the RGB components representing the appropriate shade of grey
4330 'r' : this.hsl.l * 2.55,
4331 'g' : this.hsl.l * 2.55,
4332 'b' : this.hsl.l * 2.55
4337 // set some temporary values
4338 var p = this.hsl.l < 50
4339 ? this.hsl.l * (1 + hsl.s / 100)
4340 : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4341 var q = 2 * hsl.l - p;
4343 // initialise the RGB components
4346 'r' : (h + 120) / 60 % 6,
4348 'b' : (h + 240) / 60 % 6
4351 // loop over the RGB components
4352 for (var key in this.rgb){
4354 // ensure that the property is not inherited from the root object
4355 if (this.rgb.hasOwnProperty(key)){
4357 // set the component to its value in the range [0,100]
4358 if (this.rgb[key] < 1){
4359 this.rgb[key] = q + (p - q) * this.rgb[key];
4360 }else if (this.rgb[key] < 3){
4362 }else if (this.rgb[key] < 4){
4363 this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4368 // set the component to its value in the range [0,255]
4369 this.rgb[key] *= 2.55;
4379 /* Calculates and stores the HSV components of this HSLColor so that they can
4380 * be returned be the getHSL function.
4382 calculateHSV : function(){
4384 // set a temporary value
4385 var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4387 // store the HSV components
4391 's' : 200 * t / (this.hsl.l + t),
4392 'v' : t + this.hsl.l
4395 // correct a division-by-zero error
4396 if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4403 * Portions of this file are based on pieces of Yahoo User Interface Library
4404 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4405 * YUI licensed under the BSD License:
4406 * http://developer.yahoo.net/yui/license.txt
4407 * <script type="text/javascript">
4412 Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4413 Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4416 Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4418 var fly = Roo.lib.AnimBase.fly;
4420 var superclass = Y.ColorAnim.superclass;
4421 var proto = Y.ColorAnim.prototype;
4423 proto.toString = function() {
4424 var el = this.getEl();
4425 var id = el.id || el.tagName;
4426 return ("ColorAnim " + id);
4429 proto.patterns.color = /color$/i;
4430 proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4431 proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4432 proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4433 proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4436 proto.parseColor = function(s) {
4437 if (s.length == 3) {
4441 var c = this.patterns.hex.exec(s);
4442 if (c && c.length == 4) {
4443 return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4446 c = this.patterns.rgb.exec(s);
4447 if (c && c.length == 4) {
4448 return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4451 c = this.patterns.hex3.exec(s);
4452 if (c && c.length == 4) {
4453 return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4458 // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4459 proto.getAttribute = function(attr) {
4460 var el = this.getEl();
4461 if (this.patterns.color.test(attr)) {
4462 var val = fly(el).getStyle(attr);
4464 if (this.patterns.transparent.test(val)) {
4465 var parent = el.parentNode;
4466 val = fly(parent).getStyle(attr);
4468 while (parent && this.patterns.transparent.test(val)) {
4469 parent = parent.parentNode;
4470 val = fly(parent).getStyle(attr);
4471 if (parent.tagName.toUpperCase() == 'HTML') {
4477 val = superclass.getAttribute.call(this, attr);
4482 proto.getAttribute = function(attr) {
4483 var el = this.getEl();
4484 if (this.patterns.color.test(attr)) {
4485 var val = fly(el).getStyle(attr);
4487 if (this.patterns.transparent.test(val)) {
4488 var parent = el.parentNode;
4489 val = fly(parent).getStyle(attr);
4491 while (parent && this.patterns.transparent.test(val)) {
4492 parent = parent.parentNode;
4493 val = fly(parent).getStyle(attr);
4494 if (parent.tagName.toUpperCase() == 'HTML') {
4500 val = superclass.getAttribute.call(this, attr);
4506 proto.doMethod = function(attr, start, end) {
4509 if (this.patterns.color.test(attr)) {
4511 for (var i = 0, len = start.length; i < len; ++i) {
4512 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4515 val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4518 val = superclass.doMethod.call(this, attr, start, end);
4524 proto.setRuntimeAttribute = function(attr) {
4525 superclass.setRuntimeAttribute.call(this, attr);
4527 if (this.patterns.color.test(attr)) {
4528 var attributes = this.attributes;
4529 var start = this.parseColor(this.runtimeAttributes[attr].start);
4530 var end = this.parseColor(this.runtimeAttributes[attr].end);
4532 if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4533 end = this.parseColor(attributes[attr].by);
4535 for (var i = 0, len = start.length; i < len; ++i) {
4536 end[i] = start[i] + end[i];
4540 this.runtimeAttributes[attr].start = start;
4541 this.runtimeAttributes[attr].end = end;
4547 * Portions of this file are based on pieces of Yahoo User Interface Library
4548 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4549 * YUI licensed under the BSD License:
4550 * http://developer.yahoo.net/yui/license.txt
4551 * <script type="text/javascript">
4557 easeNone: function (t, b, c, d) {
4558 return c * t / d + b;
4562 easeIn: function (t, b, c, d) {
4563 return c * (t /= d) * t + b;
4567 easeOut: function (t, b, c, d) {
4568 return -c * (t /= d) * (t - 2) + b;
4572 easeBoth: function (t, b, c, d) {
4573 if ((t /= d / 2) < 1) {
4574 return c / 2 * t * t + b;
4577 return -c / 2 * ((--t) * (t - 2) - 1) + b;
4581 easeInStrong: function (t, b, c, d) {
4582 return c * (t /= d) * t * t * t + b;
4586 easeOutStrong: function (t, b, c, d) {
4587 return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4591 easeBothStrong: function (t, b, c, d) {
4592 if ((t /= d / 2) < 1) {
4593 return c / 2 * t * t * t * t + b;
4596 return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4601 elasticIn: function (t, b, c, d, a, p) {
4605 if ((t /= d) == 1) {
4612 if (!a || a < Math.abs(c)) {
4617 var s = p / (2 * Math.PI) * Math.asin(c / a);
4620 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4624 elasticOut: function (t, b, c, d, a, p) {
4628 if ((t /= d) == 1) {
4635 if (!a || a < Math.abs(c)) {
4640 var s = p / (2 * Math.PI) * Math.asin(c / a);
4643 return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4647 elasticBoth: function (t, b, c, d, a, p) {
4652 if ((t /= d / 2) == 2) {
4660 if (!a || a < Math.abs(c)) {
4665 var s = p / (2 * Math.PI) * Math.asin(c / a);
4669 return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4670 Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4672 return a * Math.pow(2, -10 * (t -= 1)) *
4673 Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4678 backIn: function (t, b, c, d, s) {
4679 if (typeof s == 'undefined') {
4682 return c * (t /= d) * t * ((s + 1) * t - s) + b;
4686 backOut: function (t, b, c, d, s) {
4687 if (typeof s == 'undefined') {
4690 return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4694 backBoth: function (t, b, c, d, s) {
4695 if (typeof s == 'undefined') {
4699 if ((t /= d / 2 ) < 1) {
4700 return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4702 return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4706 bounceIn: function (t, b, c, d) {
4707 return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4711 bounceOut: function (t, b, c, d) {
4712 if ((t /= d) < (1 / 2.75)) {
4713 return c * (7.5625 * t * t) + b;
4714 } else if (t < (2 / 2.75)) {
4715 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4716 } else if (t < (2.5 / 2.75)) {
4717 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4719 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4723 bounceBoth: function (t, b, c, d) {
4725 return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4727 return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4730 * Portions of this file are based on pieces of Yahoo User Interface Library
4731 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4732 * YUI licensed under the BSD License:
4733 * http://developer.yahoo.net/yui/license.txt
4734 * <script type="text/javascript">
4738 Roo.lib.Motion = function(el, attributes, duration, method) {
4740 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4744 Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4748 var superclass = Y.Motion.superclass;
4749 var proto = Y.Motion.prototype;
4751 proto.toString = function() {
4752 var el = this.getEl();
4753 var id = el.id || el.tagName;
4754 return ("Motion " + id);
4757 proto.patterns.points = /^points$/i;
4759 proto.setAttribute = function(attr, val, unit) {
4760 if (this.patterns.points.test(attr)) {
4761 unit = unit || 'px';
4762 superclass.setAttribute.call(this, 'left', val[0], unit);
4763 superclass.setAttribute.call(this, 'top', val[1], unit);
4765 superclass.setAttribute.call(this, attr, val, unit);
4769 proto.getAttribute = function(attr) {
4770 if (this.patterns.points.test(attr)) {
4772 superclass.getAttribute.call(this, 'left'),
4773 superclass.getAttribute.call(this, 'top')
4776 val = superclass.getAttribute.call(this, attr);
4782 proto.doMethod = function(attr, start, end) {
4785 if (this.patterns.points.test(attr)) {
4786 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4787 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4789 val = superclass.doMethod.call(this, attr, start, end);
4794 proto.setRuntimeAttribute = function(attr) {
4795 if (this.patterns.points.test(attr)) {
4796 var el = this.getEl();
4797 var attributes = this.attributes;
4799 var control = attributes['points']['control'] || [];
4803 if (control.length > 0 && !(control[0] instanceof Array)) {
4804 control = [control];
4807 for (i = 0,len = control.length; i < len; ++i) {
4808 tmp[i] = control[i];
4813 Roo.fly(el).position();
4815 if (isset(attributes['points']['from'])) {
4816 Roo.lib.Dom.setXY(el, attributes['points']['from']);
4819 Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4822 start = this.getAttribute('points');
4825 if (isset(attributes['points']['to'])) {
4826 end = translateValues.call(this, attributes['points']['to'], start);
4828 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4829 for (i = 0,len = control.length; i < len; ++i) {
4830 control[i] = translateValues.call(this, control[i], start);
4834 } else if (isset(attributes['points']['by'])) {
4835 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4837 for (i = 0,len = control.length; i < len; ++i) {
4838 control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4842 this.runtimeAttributes[attr] = [start];
4844 if (control.length > 0) {
4845 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4848 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4851 superclass.setRuntimeAttribute.call(this, attr);
4855 var translateValues = function(val, start) {
4856 var pageXY = Roo.lib.Dom.getXY(this.getEl());
4857 val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4862 var isset = function(prop) {
4863 return (typeof prop !== 'undefined');
4867 * Portions of this file are based on pieces of Yahoo User Interface Library
4868 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4869 * YUI licensed under the BSD License:
4870 * http://developer.yahoo.net/yui/license.txt
4871 * <script type="text/javascript">
4875 Roo.lib.Scroll = function(el, attributes, duration, method) {
4877 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4881 Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4885 var superclass = Y.Scroll.superclass;
4886 var proto = Y.Scroll.prototype;
4888 proto.toString = function() {
4889 var el = this.getEl();
4890 var id = el.id || el.tagName;
4891 return ("Scroll " + id);
4894 proto.doMethod = function(attr, start, end) {
4897 if (attr == 'scroll') {
4899 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4900 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4904 val = superclass.doMethod.call(this, attr, start, end);
4909 proto.getAttribute = function(attr) {
4911 var el = this.getEl();
4913 if (attr == 'scroll') {
4914 val = [ el.scrollLeft, el.scrollTop ];
4916 val = superclass.getAttribute.call(this, attr);
4922 proto.setAttribute = function(attr, val, unit) {
4923 var el = this.getEl();
4925 if (attr == 'scroll') {
4926 el.scrollLeft = val[0];
4927 el.scrollTop = val[1];
4929 superclass.setAttribute.call(this, attr, val, unit);
4934 * Originally based of this code... - refactored for Roo...
4935 * https://github.com/aaalsaleh/undo-manager
4938 * @author Abdulrahman Alsaleh
4939 * @copyright 2015 Abdulrahman Alsaleh
4940 * @license MIT License (c)
4942 * Hackily modifyed by alan@roojs.com
4947 * TOTALLY UNTESTED...
4949 * Documentation to be done....
4954 * @class Roo.lib.UndoManager
4955 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4956 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4962 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4966 * For more information see this blog post with examples:
4967 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4968 - Create Elements using DOM, HTML fragments and Templates</a>.
4970 * @param {Number} limit how far back to go ... use 1000?
4971 * @param {Object} scope usually use document..
4974 Roo.lib.UndoManager = function (limit, undoScopeHost)
4978 this.scope = undoScopeHost;
4979 this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4980 if (this.fireEvent) {
4987 Roo.lib.UndoManager.prototype = {
4998 * To push and execute a transaction, the method undoManager.transact
4999 * must be called by passing a transaction object as the first argument, and a merge
5000 * flag as the second argument. A transaction object has the following properties:
5004 undoManager.transact({
5006 execute: function() { ... },
5007 undo: function() { ... },
5008 // redo same as execute
5009 redo: function() { this.execute(); }
5012 // merge transaction
5013 undoManager.transact({
5015 execute: function() { ... }, // this will be run...
5016 undo: function() { ... }, // what to do when undo is run.
5017 // redo same as execute
5018 redo: function() { this.execute(); }
5023 * @param {Object} transaction The transaction to add to the stack.
5024 * @return {String} The HTML fragment
5028 transact : function (transaction, merge)
5030 if (arguments.length < 2) {
5031 throw new TypeError('Not enough arguments to UndoManager.transact.');
5034 transaction.execute();
5036 this.stack.splice(0, this.position);
5037 if (merge && this.length) {
5038 this.stack[0].push(transaction);
5040 this.stack.unshift([transaction]);
5045 if (this.limit && this.stack.length > this.limit) {
5046 this.length = this.stack.length = this.limit;
5048 this.length = this.stack.length;
5051 if (this.fireEvent) {
5052 this.scope.dispatchEvent(
5053 new CustomEvent('DOMTransaction', {
5055 transactions: this.stack[0].slice()
5063 //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5070 //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5072 if (this.position < this.length) {
5073 for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5074 this.stack[this.position][i].undo();
5078 if (this.fireEvent) {
5079 this.scope.dispatchEvent(
5080 new CustomEvent('undo', {
5082 transactions: this.stack[this.position - 1].slice()
5094 if (this.position > 0) {
5095 for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5096 this.stack[this.position - 1][i].redo();
5100 if (this.fireEvent) {
5101 this.scope.dispatchEvent(
5102 new CustomEvent('redo', {
5104 transactions: this.stack[this.position].slice()
5114 item : function (index)
5116 if (index >= 0 && index < this.length) {
5117 return this.stack[index].slice();
5122 clearUndo : function () {
5123 this.stack.length = this.length = this.position;
5126 clearRedo : function () {
5127 this.stack.splice(0, this.position);
5129 this.length = this.stack.length;
5132 * Reset the undo - probaly done on load to clear all history.
5139 this.current_html = this.scope.innerHTML;
5140 if (this.timer !== false) {
5141 clearTimeout(this.timer);
5153 // this will handle the undo/redo on the element.?
5154 bindEvents : function()
5156 var el = this.scope;
5157 el.undoManager = this;
5160 this.scope.addEventListener('keydown', function(e) {
5161 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5163 el.undoManager.redo(); // Ctrl/Command + Shift + Z
5165 el.undoManager.undo(); // Ctrl/Command + Z
5172 this.scope.addEventListener('keyup', function(e) {
5173 if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5182 el.addEventListener('input', function(e) {
5183 if(el.innerHTML == t.current_html) {
5186 // only record events every second.
5187 if (t.timer !== false) {
5188 clearTimeout(t.timer);
5191 t.timer = setTimeout(function() { t.merge = false; }, 1000);
5193 t.addEvent(t.merge);
5194 t.merge = true; // ignore changes happening every second..
5198 * Manually add an event.
5199 * Normall called without arguements - and it will just get added to the stack.
5203 addEvent : function(merge)
5205 //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5206 // not sure if this should clear the timer
5207 merge = typeof(merge) == 'undefined' ? false : merge;
5209 this.scope.undoManager.transact({
5211 oldHTML: this.current_html,
5212 newHTML: this.scope.innerHTML,
5213 // nothing to execute (content already changed when input is fired)
5214 execute: function() { },
5216 this.scope.innerHTML = this.current_html = this.oldHTML;
5219 this.scope.innerHTML = this.current_html = this.newHTML;
5221 }, false); //merge);
5225 this.current_html = this.scope.innerHTML;
5235 * @class Roo.lib.Range
5237 * This is a toolkit, normally used to copy features into a Dom Range element
5238 * Roo.lib.Range.wrap(x);
5243 Roo.lib.Range = function() { };
5246 * Wrap a Dom Range object, to give it new features...
5248 * @param {Range} the range to wrap
5250 Roo.lib.Range.wrap = function(r) {
5251 return Roo.apply(r, Roo.lib.Range.prototype);
5254 * find a parent node eg. LI / OL
5255 * @param {string|Array} node name or array of nodenames
5256 * @return {DomElement|false}
5258 Roo.apply(Roo.lib.Range.prototype,
5261 closest : function(str)
5263 if (typeof(str) != 'string') {
5264 // assume it's a array.
5265 for(var i = 0;i < str.length;i++) {
5266 var r = this.closest(str[i]);
5274 str = str.toLowerCase();
5275 var n = this.commonAncestorContainer; // might not be a node
5276 while (n.nodeType != 1) {
5280 if (n.nodeName.toLowerCase() == str ) {
5283 if (n.nodeName.toLowerCase() == 'body') {
5287 return n.closest(str) || false;
5290 cloneRange : function()
5292 return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5295 * @class Roo.lib.Selection
5297 * This is a toolkit, normally used to copy features into a Dom Selection element
5298 * Roo.lib.Selection.wrap(x);
5303 Roo.lib.Selection = function() { };
5306 * Wrap a Dom Range object, to give it new features...
5308 * @param {Range} the range to wrap
5310 Roo.lib.Selection.wrap = function(r, doc) {
5311 Roo.apply(r, Roo.lib.Selection.prototype);
5312 r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5316 * find a parent node eg. LI / OL
5317 * @param {string|Array} node name or array of nodenames
5318 * @return {DomElement|false}
5320 Roo.apply(Roo.lib.Selection.prototype,
5323 * the owner document
5325 ownerDocument : false,
5327 getRangeAt : function(n)
5329 return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5333 * insert node at selection
5334 * @param {DomElement|string} node
5335 * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5337 insertNode: function(node, cursor)
5339 if (typeof(node) == 'string') {
5340 node = this.ownerDocument.createElement(node);
5341 if (cursor == 'in') {
5342 node.innerHTML = ' ';
5346 var range = this.getRangeAt(0);
5348 if (this.type != 'Caret') {
5349 range.deleteContents();
5351 var sn = node.childNodes[0]; // select the contents.
5355 range.insertNode(node);
5356 if (cursor == 'after') {
5357 node.insertAdjacentHTML('afterend', ' ');
5358 sn = node.nextSibling;
5361 if (cursor == 'none') {
5365 this.cursorText(sn);
5368 cursorText : function(n)
5371 //var range = this.getRangeAt(0);
5372 range = Roo.lib.Range.wrap(new Range());
5373 //range.selectNode(n);
5375 var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5376 range.setStart(n.parentNode,ix);
5377 range.setEnd(n.parentNode,ix+1);
5378 //range.collapse(false);
5380 this.removeAllRanges();
5381 this.addRange(range);
5383 Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5385 cursorAfter : function(n)
5387 if (!n.nextSibling || n.nextSibling.nodeValue != ' ') {
5388 n.insertAdjacentHTML('afterend', ' ');
5390 this.cursorText (n.nextSibling);
5396 * Ext JS Library 1.1.1
5397 * Copyright(c) 2006-2007, Ext JS, LLC.
5399 * Originally Released Under LGPL - original licence link has changed is not relivant.
5402 * <script type="text/javascript">
5406 // nasty IE9 hack - what a pile of crap that is..
5408 if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5409 Range.prototype.createContextualFragment = function (html) {
5410 var doc = window.document;
5411 var container = doc.createElement("div");
5412 container.innerHTML = html;
5413 var frag = doc.createDocumentFragment(), n;
5414 while ((n = container.firstChild)) {
5415 frag.appendChild(n);
5422 * @class Roo.DomHelper
5423 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5424 * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
5427 Roo.DomHelper = function(){
5428 var tempTableEl = null;
5429 var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5430 var tableRe = /^table|tbody|tr|td$/i;
5432 // build as innerHTML where available
5434 var createHtml = function(o){
5435 if(typeof o == 'string'){
5444 if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5445 if(attr == "style"){
5447 if(typeof s == "function"){
5450 if(typeof s == "string"){
5451 b += ' style="' + s + '"';
5452 }else if(typeof s == "object"){
5455 if(typeof s[key] != "function"){
5456 b += key + ":" + s[key] + ";";
5463 b += ' class="' + o["cls"] + '"';
5464 }else if(attr == "htmlFor"){
5465 b += ' for="' + o["htmlFor"] + '"';
5467 b += " " + attr + '="' + o[attr] + '"';
5471 if(emptyTags.test(o.tag)){
5475 var cn = o.children || o.cn;
5477 //http://bugs.kde.org/show_bug.cgi?id=71506
5478 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5479 for(var i = 0, len = cn.length; i < len; i++) {
5480 b += createHtml(cn[i], b);
5483 b += createHtml(cn, b);
5489 b += "</" + o.tag + ">";
5496 var createDom = function(o, parentNode){
5498 // defininition craeted..
5500 if (o.ns && o.ns != 'html') {
5502 if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5503 xmlns[o.ns] = o.xmlns;
5506 if (typeof(xmlns[o.ns]) == 'undefined') {
5507 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5513 if (typeof(o) == 'string') {
5514 return parentNode.appendChild(document.createTextNode(o));
5516 o.tag = o.tag || div;
5517 if (o.ns && Roo.isIE) {
5519 o.tag = o.ns + ':' + o.tag;
5522 var el = ns ? document.createElementNS( ns, o.tag||'div') : document.createElement(o.tag||'div');
5523 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5526 if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" ||
5527 attr == "style" || typeof o[attr] == "function") { continue; }
5529 if(attr=="cls" && Roo.isIE){
5530 el.className = o["cls"];
5532 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5538 Roo.DomHelper.applyStyles(el, o.style);
5539 var cn = o.children || o.cn;
5541 //http://bugs.kde.org/show_bug.cgi?id=71506
5542 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5543 for(var i = 0, len = cn.length; i < len; i++) {
5544 createDom(cn[i], el);
5551 el.innerHTML = o.html;
5554 parentNode.appendChild(el);
5559 var ieTable = function(depth, s, h, e){
5560 tempTableEl.innerHTML = [s, h, e].join('');
5561 var i = -1, el = tempTableEl;
5562 while(++i < depth && el.firstChild){
5568 // kill repeat to save bytes
5572 tbe = '</tbody>'+te,
5578 * Nasty code for IE's broken table implementation
5580 var insertIntoTable = function(tag, where, el, html){
5582 tempTableEl = document.createElement('div');
5587 if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5590 if(where == 'beforebegin'){
5594 before = el.nextSibling;
5597 node = ieTable(4, trs, html, tre);
5599 else if(tag == 'tr'){
5600 if(where == 'beforebegin'){
5603 node = ieTable(3, tbs, html, tbe);
5604 } else if(where == 'afterend'){
5605 before = el.nextSibling;
5607 node = ieTable(3, tbs, html, tbe);
5608 } else{ // INTO a TR
5609 if(where == 'afterbegin'){
5610 before = el.firstChild;
5612 node = ieTable(4, trs, html, tre);
5614 } else if(tag == 'tbody'){
5615 if(where == 'beforebegin'){
5618 node = ieTable(2, ts, html, te);
5619 } else if(where == 'afterend'){
5620 before = el.nextSibling;
5622 node = ieTable(2, ts, html, te);
5624 if(where == 'afterbegin'){
5625 before = el.firstChild;
5627 node = ieTable(3, tbs, html, tbe);
5630 if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5633 if(where == 'afterbegin'){
5634 before = el.firstChild;
5636 node = ieTable(2, ts, html, te);
5638 el.insertBefore(node, before);
5642 // this is a bit like the react update code...
5645 var updateNode = function(from, to)
5647 // should we handle non-standard elements?
5648 Roo.log(["UpdateNode" , from, to]);
5649 if (from.nodeType != to.nodeType) {
5650 Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5651 from.parentNode.replaceChild(to, from);
5654 if (from.nodeType == 3) {
5655 // assume it's text?!
5656 if (from.data == to.data) {
5659 from.data = to.data;
5662 if (!from.parentNode) {
5663 // not sure why this is happening?
5666 // assume 'to' doesnt have '1/3 nodetypes!
5667 // not sure why, by from, parent node might not exist?
5668 if (from.nodeType !=1 || from.tagName != to.tagName) {
5669 Roo.log(["ReplaceChild" , from, to ]);
5671 from.parentNode.replaceChild(to, from);
5674 // compare attributes
5675 var ar = Array.from(from.attributes);
5676 for(var i = 0; i< ar.length;i++) {
5677 if (to.hasAttribute(ar[i].name)) {
5680 if (ar[i].name == 'id') { // always keep ids?
5683 //if (ar[i].name == 'style') {
5684 // throw "style removed?";
5686 Roo.log("removeAttribute" + ar[i].name);
5687 from.removeAttribute(ar[i].name);
5690 for(var i = 0; i< ar.length;i++) {
5691 if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5692 Roo.log("skipAttribute " + ar[i].name + '=' + to.getAttribute(ar[i].name));
5695 Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5696 from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5699 var far = Array.from(from.childNodes);
5700 var tar = Array.from(to.childNodes);
5701 // if the lengths are different.. then it's probably a editable content change, rather than
5702 // a change of the block definition..
5704 // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5705 /*if (from.innerHTML == to.innerHTML) {
5708 if (far.length != tar.length) {
5709 from.innerHTML = to.innerHTML;
5714 for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5715 if (i >= far.length) {
5716 from.appendChild(tar[i]);
5717 Roo.log(["add", tar[i]]);
5719 } else if ( i >= tar.length) {
5720 from.removeChild(far[i]);
5721 Roo.log(["remove", far[i]]);
5724 updateNode(far[i], tar[i]);
5736 /** True to force the use of DOM instead of html fragments @type Boolean */
5740 * Returns the markup for the passed Element(s) config
5741 * @param {Object} o The Dom object spec (and children)
5744 markup : function(o){
5745 return createHtml(o);
5749 * Applies a style specification to an element
5750 * @param {String/HTMLElement} el The element to apply styles to
5751 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5752 * a function which returns such a specification.
5754 applyStyles : function(el, styles){
5757 if(typeof styles == "string"){
5758 var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5760 while ((matches = re.exec(styles)) != null){
5761 el.setStyle(matches[1], matches[2]);
5763 }else if (typeof styles == "object"){
5764 for (var style in styles){
5765 el.setStyle(style, styles[style]);
5767 }else if (typeof styles == "function"){
5768 Roo.DomHelper.applyStyles(el, styles.call());
5774 * Inserts an HTML fragment into the Dom
5775 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5776 * @param {HTMLElement} el The context element
5777 * @param {String} html The HTML fragmenet
5778 * @return {HTMLElement} The new node
5780 insertHtml : function(where, el, html){
5781 where = where.toLowerCase();
5782 if(el.insertAdjacentHTML){
5783 if(tableRe.test(el.tagName)){
5785 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5791 el.insertAdjacentHTML('BeforeBegin', html);
5792 return el.previousSibling;
5794 el.insertAdjacentHTML('AfterBegin', html);
5795 return el.firstChild;
5797 el.insertAdjacentHTML('BeforeEnd', html);
5798 return el.lastChild;
5800 el.insertAdjacentHTML('AfterEnd', html);
5801 return el.nextSibling;
5803 throw 'Illegal insertion point -> "' + where + '"';
5805 var range = el.ownerDocument.createRange();
5809 range.setStartBefore(el);
5810 frag = range.createContextualFragment(html);
5811 el.parentNode.insertBefore(frag, el);
5812 return el.previousSibling;
5815 range.setStartBefore(el.firstChild);
5816 frag = range.createContextualFragment(html);
5817 el.insertBefore(frag, el.firstChild);
5818 return el.firstChild;
5820 el.innerHTML = html;
5821 return el.firstChild;
5825 range.setStartAfter(el.lastChild);
5826 frag = range.createContextualFragment(html);
5827 el.appendChild(frag);
5828 return el.lastChild;
5830 el.innerHTML = html;
5831 return el.lastChild;
5834 range.setStartAfter(el);
5835 frag = range.createContextualFragment(html);
5836 el.parentNode.insertBefore(frag, el.nextSibling);
5837 return el.nextSibling;
5839 throw 'Illegal insertion point -> "' + where + '"';
5843 * Creates new Dom element(s) and inserts them before el
5844 * @param {String/HTMLElement/Element} el The context element
5845 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5846 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5847 * @return {HTMLElement/Roo.Element} The new node
5849 insertBefore : function(el, o, returnElement){
5850 return this.doInsert(el, o, returnElement, "beforeBegin");
5854 * Creates new Dom element(s) and inserts them after el
5855 * @param {String/HTMLElement/Element} el The context element
5856 * @param {Object} o The Dom object spec (and children)
5857 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5858 * @return {HTMLElement/Roo.Element} The new node
5860 insertAfter : function(el, o, returnElement){
5861 return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5865 * Creates new Dom element(s) and inserts them as the first child of el
5866 * @param {String/HTMLElement/Element} el The context element
5867 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5868 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5869 * @return {HTMLElement/Roo.Element} The new node
5871 insertFirst : function(el, o, returnElement){
5872 return this.doInsert(el, o, returnElement, "afterBegin");
5876 doInsert : function(el, o, returnElement, pos, sibling){
5877 el = Roo.getDom(el);
5879 if(this.useDom || o.ns){
5880 newNode = createDom(o, null);
5881 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5883 var html = createHtml(o);
5884 newNode = this.insertHtml(pos, el, html);
5886 return returnElement ? Roo.get(newNode, true) : newNode;
5890 * Creates new Dom element(s) and appends them to el
5891 * @param {String/HTMLElement/Element} el The context element
5892 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5893 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5894 * @return {HTMLElement/Roo.Element} The new node
5896 append : function(el, o, returnElement){
5897 el = Roo.getDom(el);
5899 if(this.useDom || o.ns){
5900 newNode = createDom(o, null);
5901 el.appendChild(newNode);
5903 var html = createHtml(o);
5904 newNode = this.insertHtml("beforeEnd", el, html);
5906 return returnElement ? Roo.get(newNode, true) : newNode;
5910 * Creates new Dom element(s) and overwrites the contents of el with them
5911 * @param {String/HTMLElement/Element} el The context element
5912 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5913 * @param {Boolean} returnElement (optional) true to return a Roo.Element
5914 * @return {HTMLElement/Roo.Element} The new node
5916 overwrite : function(el, o, returnElement)
5918 el = Roo.getDom(el);
5921 while (el.childNodes.length) {
5922 el.removeChild(el.firstChild);
5926 el.innerHTML = createHtml(o);
5929 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5933 * Creates a new Roo.DomHelper.Template from the Dom object spec
5934 * @param {Object} o The Dom object spec (and children)
5935 * @return {Roo.DomHelper.Template} The new template
5937 createTemplate : function(o){
5938 var html = createHtml(o);
5939 return new Roo.Template(html);
5942 * Updates the first element with the spec from the o (replacing if necessary)
5943 * This iterates through the children, and updates attributes / children etc..
5944 * @param {String/HTMLElement/Element} el The context element
5945 * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5948 update : function(el, o)
5950 updateNode(Roo.getDom(el), createDom(o));
5959 * Ext JS Library 1.1.1
5960 * Copyright(c) 2006-2007, Ext JS, LLC.
5962 * Originally Released Under LGPL - original licence link has changed is not relivant.
5965 * <script type="text/javascript">
5969 * @class Roo.Template
5970 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5971 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5974 var t = new Roo.Template({
5975 html : '<div name="{id}">' +
5976 '<span class="{cls}">{name:trim} {someval:this.myformat}{value:ellipsis(10)}</span>' +
5978 myformat: function (value, allValues) {
5979 return 'XX' + value;
5982 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5984 * For more information see this blog post with examples:
5985 * <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5986 - Create Elements using DOM, HTML fragments and Templates</a>.
5988 * @param {Object} cfg - Configuration object.
5990 Roo.Template = function(cfg){
5992 if(cfg instanceof Array){
5994 }else if(arguments.length > 1){
5995 cfg = Array.prototype.join.call(arguments, "");
5999 if (typeof(cfg) == 'object') {
6010 Roo.Template.prototype = {
6013 * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6019 * @cfg {String} url The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
6020 * it should be fixed so that template is observable...
6024 * @cfg {String} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6032 * Returns an HTML fragment of this template with the specified values applied.
6033 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6034 * @return {String} The HTML fragment
6039 applyTemplate : function(values){
6040 //Roo.log(["applyTemplate", values]);
6044 return this.compiled(values);
6046 var useF = this.disableFormats !== true;
6047 var fm = Roo.util.Format, tpl = this;
6048 var fn = function(m, name, format, args){
6050 if(format.substr(0, 5) == "this."){
6051 return tpl.call(format.substr(5), values[name], values);
6054 // quoted values are required for strings in compiled templates,
6055 // but for non compiled we need to strip them
6056 // quoted reversed for jsmin
6057 var re = /^\s*['"](.*)["']\s*$/;
6058 args = args.split(',');
6059 for(var i = 0, len = args.length; i < len; i++){
6060 args[i] = args[i].replace(re, "$1");
6062 args = [values[name]].concat(args);
6064 args = [values[name]];
6066 return fm[format].apply(fm, args);
6069 return values[name] !== undefined ? values[name] : "";
6072 return this.html.replace(this.re, fn);
6090 this.loading = true;
6091 this.compiled = false;
6093 var cx = new Roo.data.Connection();
6097 success : function (response) {
6101 _t.set(response.responseText,true);
6107 failure : function(response) {
6108 Roo.log("Template failed to load from " + _t.url);
6115 * Sets the HTML used as the template and optionally compiles it.
6116 * @param {String} html
6117 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6118 * @return {Roo.Template} this
6120 set : function(html, compile){
6122 this.compiled = false;
6130 * True to disable format functions (defaults to false)
6133 disableFormats : false,
6136 * The regular expression used to match template variables
6140 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6143 * Compiles the template into an internal function, eliminating the RegEx overhead.
6144 * @return {Roo.Template} this
6146 compile : function(){
6147 var fm = Roo.util.Format;
6148 var useF = this.disableFormats !== true;
6149 var sep = Roo.isGecko ? "+" : ",";
6150 var fn = function(m, name, format, args){
6152 args = args ? ',' + args : "";
6153 if(format.substr(0, 5) != "this."){
6154 format = "fm." + format + '(';
6156 format = 'this.call("'+ format.substr(5) + '", ';
6160 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6162 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6165 // branched to use + in gecko and [].join() in others
6167 body = "this.compiled = function(values){ return '" +
6168 this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6171 body = ["this.compiled = function(values){ return ['"];
6172 body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6173 body.push("'].join('');};");
6174 body = body.join('');
6184 // private function used to call members
6185 call : function(fnName, value, allValues){
6186 return this[fnName](value, allValues);
6190 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6191 * @param {String/HTMLElement/Roo.Element} el The context element
6192 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6193 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6194 * @return {HTMLElement/Roo.Element} The new node or Element
6196 insertFirst: function(el, values, returnElement){
6197 return this.doInsert('afterBegin', el, values, returnElement);
6201 * Applies the supplied values to the template and inserts the new node(s) before el.
6202 * @param {String/HTMLElement/Roo.Element} el The context element
6203 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6204 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6205 * @return {HTMLElement/Roo.Element} The new node or Element
6207 insertBefore: function(el, values, returnElement){
6208 return this.doInsert('beforeBegin', el, values, returnElement);
6212 * Applies the supplied values to the template and inserts the new node(s) after el.
6213 * @param {String/HTMLElement/Roo.Element} el The context element
6214 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6215 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6216 * @return {HTMLElement/Roo.Element} The new node or Element
6218 insertAfter : function(el, values, returnElement){
6219 return this.doInsert('afterEnd', el, values, returnElement);
6223 * Applies the supplied values to the template and appends the new node(s) to el.
6224 * @param {String/HTMLElement/Roo.Element} el The context element
6225 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6226 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6227 * @return {HTMLElement/Roo.Element} The new node or Element
6229 append : function(el, values, returnElement){
6230 return this.doInsert('beforeEnd', el, values, returnElement);
6233 doInsert : function(where, el, values, returnEl){
6234 el = Roo.getDom(el);
6235 var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6236 return returnEl ? Roo.get(newNode, true) : newNode;
6240 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6241 * @param {String/HTMLElement/Roo.Element} el The context element
6242 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6243 * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6244 * @return {HTMLElement/Roo.Element} The new node or Element
6246 overwrite : function(el, values, returnElement){
6247 el = Roo.getDom(el);
6248 el.innerHTML = this.applyTemplate(values);
6249 return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6253 * Alias for {@link #applyTemplate}
6256 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6259 Roo.DomHelper.Template = Roo.Template;
6262 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6263 * @param {String/HTMLElement} el A DOM element or its id
6264 * @returns {Roo.Template} The created template
6267 Roo.Template.from = function(el){
6268 el = Roo.getDom(el);
6269 return new Roo.Template(el.value || el.innerHTML);
6272 * Ext JS Library 1.1.1
6273 * Copyright(c) 2006-2007, Ext JS, LLC.
6275 * Originally Released Under LGPL - original licence link has changed is not relivant.
6278 * <script type="text/javascript">
6283 * This is code is also distributed under MIT license for use
6284 * with jQuery and prototype JavaScript libraries.
6287 * @class Roo.DomQuery
6288 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
6290 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
6293 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
6295 <h4>Element Selectors:</h4>
6297 <li> <b>*</b> any element</li>
6298 <li> <b>E</b> an element with the tag E</li>
6299 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6300 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6301 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6302 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6304 <h4>Attribute Selectors:</h4>
6305 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6307 <li> <b>E[foo]</b> has an attribute "foo"</li>
6308 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6309 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6310 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6311 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6312 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6313 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6315 <h4>Pseudo Classes:</h4>
6317 <li> <b>E:first-child</b> E is the first child of its parent</li>
6318 <li> <b>E:last-child</b> E is the last child of its parent</li>
6319 <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
6320 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6321 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6322 <li> <b>E:only-child</b> E is the only child of its parent</li>
6323 <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
6324 <li> <b>E:first</b> the first E in the resultset</li>
6325 <li> <b>E:last</b> the last E in the resultset</li>
6326 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6327 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6328 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6329 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6330 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6331 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6332 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6333 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6334 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6336 <h4>CSS Value Selectors:</h4>
6338 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6339 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6340 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6341 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6342 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6343 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6347 Roo.DomQuery = function(){
6348 var cache = {}, simpleCache = {}, valueCache = {};
6349 var nonSpace = /\S/;
6350 var trimRe = /^\s+|\s+$/g;
6351 var tplRe = /\{(\d+)\}/g;
6352 var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6353 var tagTokenRe = /^(#)?([\w-\*]+)/;
6354 var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6356 function child(p, index){
6358 var n = p.firstChild;
6360 if(n.nodeType == 1){
6371 while((n = n.nextSibling) && n.nodeType != 1);
6376 while((n = n.previousSibling) && n.nodeType != 1);
6380 function children(d){
6381 var n = d.firstChild, ni = -1;
6383 var nx = n.nextSibling;
6384 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6394 function byClassName(c, a, v){
6398 var r = [], ri = -1, cn;
6399 for(var i = 0, ci; ci = c[i]; i++){
6403 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6404 +' ').indexOf(v) != -1){
6411 function attrValue(n, attr){
6412 if(!n.tagName && typeof n.length != "undefined"){
6421 if(attr == "class" || attr == "className"){
6422 return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6424 return n.getAttribute(attr) || n[attr];
6428 function getNodes(ns, mode, tagName){
6429 var result = [], ri = -1, cs;
6433 tagName = tagName || "*";
6434 if(typeof ns.getElementsByTagName != "undefined"){
6438 for(var i = 0, ni; ni = ns[i]; i++){
6439 cs = ni.getElementsByTagName(tagName);
6440 for(var j = 0, ci; ci = cs[j]; j++){
6444 }else if(mode == "/" || mode == ">"){
6445 var utag = tagName.toUpperCase();
6446 for(var i = 0, ni, cn; ni = ns[i]; i++){
6447 cn = ni.children || ni.childNodes;
6448 for(var j = 0, cj; cj = cn[j]; j++){
6449 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
6454 }else if(mode == "+"){
6455 var utag = tagName.toUpperCase();
6456 for(var i = 0, n; n = ns[i]; i++){
6457 while((n = n.nextSibling) && n.nodeType != 1);
6458 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6462 }else if(mode == "~"){
6463 for(var i = 0, n; n = ns[i]; i++){
6464 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6473 function concat(a, b){
6477 for(var i = 0, l = b.length; i < l; i++){
6483 function byTag(cs, tagName){
6484 if(cs.tagName || cs == document){
6490 var r = [], ri = -1;
6491 tagName = tagName.toLowerCase();
6492 for(var i = 0, ci; ci = cs[i]; i++){
6493 if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6500 function byId(cs, attr, id){
6501 if(cs.tagName || cs == document){
6507 var r = [], ri = -1;
6508 for(var i = 0,ci; ci = cs[i]; i++){
6509 if(ci && ci.id == id){
6517 function byAttribute(cs, attr, value, op, custom){
6518 var r = [], ri = -1, st = custom=="{";
6519 var f = Roo.DomQuery.operators[op];
6520 for(var i = 0, ci; ci = cs[i]; i++){
6523 a = Roo.DomQuery.getStyle(ci, attr);
6525 else if(attr == "class" || attr == "className"){
6526 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6527 }else if(attr == "for"){
6529 }else if(attr == "href"){
6530 a = ci.getAttribute("href", 2);
6532 a = ci.getAttribute(attr);
6534 if((f && f(a, value)) || (!f && a)){
6541 function byPseudo(cs, name, value){
6542 return Roo.DomQuery.pseudos[name](cs, value);
6545 // This is for IE MSXML which does not support expandos.
6546 // IE runs the same speed using setAttribute, however FF slows way down
6547 // and Safari completely fails so they need to continue to use expandos.
6548 var isIE = window.ActiveXObject ? true : false;
6550 // this eval is stop the compressor from
6551 // renaming the variable to something shorter
6553 /** eval:var:batch */
6558 function nodupIEXml(cs){
6560 cs[0].setAttribute("_nodup", d);
6562 for(var i = 1, len = cs.length; i < len; i++){
6564 if(!c.getAttribute("_nodup") != d){
6565 c.setAttribute("_nodup", d);
6569 for(var i = 0, len = cs.length; i < len; i++){
6570 cs[i].removeAttribute("_nodup");
6579 var len = cs.length, c, i, r = cs, cj, ri = -1;
6580 if(!len || typeof cs.nodeType != "undefined" || len == 1){
6583 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6584 return nodupIEXml(cs);
6588 for(i = 1; c = cs[i]; i++){
6593 for(var j = 0; j < i; j++){
6596 for(j = i+1; cj = cs[j]; j++){
6608 function quickDiffIEXml(c1, c2){
6610 for(var i = 0, len = c1.length; i < len; i++){
6611 c1[i].setAttribute("_qdiff", d);
6614 for(var i = 0, len = c2.length; i < len; i++){
6615 if(c2[i].getAttribute("_qdiff") != d){
6616 r[r.length] = c2[i];
6619 for(var i = 0, len = c1.length; i < len; i++){
6620 c1[i].removeAttribute("_qdiff");
6625 function quickDiff(c1, c2){
6626 var len1 = c1.length;
6630 if(isIE && c1[0].selectSingleNode){
6631 return quickDiffIEXml(c1, c2);
6634 for(var i = 0; i < len1; i++){
6638 for(var i = 0, len = c2.length; i < len; i++){
6639 if(c2[i]._qdiff != d){
6640 r[r.length] = c2[i];
6646 function quickId(ns, mode, root, id){
6648 var d = root.ownerDocument || root;
6649 return d.getElementById(id);
6651 ns = getNodes(ns, mode, "*");
6652 return byId(ns, null, id);
6656 getStyle : function(el, name){
6657 return Roo.fly(el).getStyle(name);
6660 * Compiles a selector/xpath query into a reusable function. The returned function
6661 * takes one parameter "root" (optional), which is the context node from where the query should start.
6662 * @param {String} selector The selector/xpath query
6663 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6664 * @return {Function}
6666 compile : function(path, type){
6667 type = type || "select";
6669 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6670 var q = path, mode, lq;
6671 var tk = Roo.DomQuery.matchers;
6672 var tklen = tk.length;
6675 // accept leading mode switch
6676 var lmode = q.match(modeRe);
6677 if(lmode && lmode[1]){
6678 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6679 q = q.replace(lmode[1], "");
6681 // strip leading slashes
6682 while(path.substr(0, 1)=="/"){
6683 path = path.substr(1);
6686 while(q && lq != q){
6688 var tm = q.match(tagTokenRe);
6689 if(type == "select"){
6692 fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6694 fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6696 q = q.replace(tm[0], "");
6697 }else if(q.substr(0, 1) != '@'){
6698 fn[fn.length] = 'n = getNodes(n, mode, "*");';
6703 fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6705 fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6707 q = q.replace(tm[0], "");
6710 while(!(mm = q.match(modeRe))){
6711 var matched = false;
6712 for(var j = 0; j < tklen; j++){
6714 var m = q.match(t.re);
6716 fn[fn.length] = t.select.replace(tplRe, function(x, i){
6719 q = q.replace(m[0], "");
6724 // prevent infinite loop on bad selector
6726 throw 'Error parsing selector, parsing failed at "' + q + '"';
6730 fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6731 q = q.replace(mm[1], "");
6734 fn[fn.length] = "return nodup(n);\n}";
6737 * list of variables that need from compression as they are used by eval.
6747 * eval:var:byClassName
6749 * eval:var:byAttribute
6750 * eval:var:attrValue
6758 * Selects a group of elements.
6759 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6760 * @param {Node} root (optional) The start of the query (defaults to document).
6763 select : function(path, root, type){
6764 if(!root || root == document){
6767 if(typeof root == "string"){
6768 root = document.getElementById(root);
6770 var paths = path.split(",");
6772 for(var i = 0, len = paths.length; i < len; i++){
6773 var p = paths[i].replace(trimRe, "");
6775 cache[p] = Roo.DomQuery.compile(p);
6777 throw p + " is not a valid selector";
6780 var result = cache[p](root);
6781 if(result && result != document){
6782 results = results.concat(result);
6785 if(paths.length > 1){
6786 return nodup(results);
6792 * Selects a single element.
6793 * @param {String} selector The selector/xpath query
6794 * @param {Node} root (optional) The start of the query (defaults to document).
6797 selectNode : function(path, root){
6798 return Roo.DomQuery.select(path, root)[0];
6802 * Selects the value of a node, optionally replacing null with the defaultValue.
6803 * @param {String} selector The selector/xpath query
6804 * @param {Node} root (optional) The start of the query (defaults to document).
6805 * @param {String} defaultValue
6807 selectValue : function(path, root, defaultValue){
6808 path = path.replace(trimRe, "");
6809 if(!valueCache[path]){
6810 valueCache[path] = Roo.DomQuery.compile(path, "select");
6812 var n = valueCache[path](root);
6813 n = n[0] ? n[0] : n;
6814 var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6815 return ((v === null||v === undefined||v==='') ? defaultValue : v);
6819 * Selects the value of a node, parsing integers and floats.
6820 * @param {String} selector The selector/xpath query
6821 * @param {Node} root (optional) The start of the query (defaults to document).
6822 * @param {Number} defaultValue
6825 selectNumber : function(path, root, defaultValue){
6826 var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6827 return parseFloat(v);
6831 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6832 * @param {String/HTMLElement/Array} el An element id, element or array of elements
6833 * @param {String} selector The simple selector to test
6836 is : function(el, ss){
6837 if(typeof el == "string"){
6838 el = document.getElementById(el);
6840 var isArray = (el instanceof Array);
6841 var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6842 return isArray ? (result.length == el.length) : (result.length > 0);
6846 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6847 * @param {Array} el An array of elements to filter
6848 * @param {String} selector The simple selector to test
6849 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6850 * the selector instead of the ones that match
6853 filter : function(els, ss, nonMatches){
6854 ss = ss.replace(trimRe, "");
6855 if(!simpleCache[ss]){
6856 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6858 var result = simpleCache[ss](els);
6859 return nonMatches ? quickDiff(result, els) : result;
6863 * Collection of matching regular expressions and code snippets.
6867 select: 'n = byClassName(n, null, " {1} ");'
6869 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6870 select: 'n = byPseudo(n, "{1}", "{2}");'
6872 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6873 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6876 select: 'n = byId(n, null, "{1}");'
6879 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6884 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6885 * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, > <.
6888 "=" : function(a, v){
6891 "!=" : function(a, v){
6894 "^=" : function(a, v){
6895 return a && a.substr(0, v.length) == v;
6897 "$=" : function(a, v){
6898 return a && a.substr(a.length-v.length) == v;
6900 "*=" : function(a, v){
6901 return a && a.indexOf(v) !== -1;
6903 "%=" : function(a, v){
6904 return (a % v) == 0;
6906 "|=" : function(a, v){
6907 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6909 "~=" : function(a, v){
6910 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6915 * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6916 * and the argument (if any) supplied in the selector.
6919 "first-child" : function(c){
6920 var r = [], ri = -1, n;
6921 for(var i = 0, ci; ci = n = c[i]; i++){
6922 while((n = n.previousSibling) && n.nodeType != 1);
6930 "last-child" : function(c){
6931 var r = [], ri = -1, n;
6932 for(var i = 0, ci; ci = n = c[i]; i++){
6933 while((n = n.nextSibling) && n.nodeType != 1);
6941 "nth-child" : function(c, a) {
6942 var r = [], ri = -1;
6943 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6944 var f = (m[1] || 1) - 0, l = m[2] - 0;
6945 for(var i = 0, n; n = c[i]; i++){
6946 var pn = n.parentNode;
6947 if (batch != pn._batch) {
6949 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6950 if(cn.nodeType == 1){
6957 if (l == 0 || n.nodeIndex == l){
6960 } else if ((n.nodeIndex + l) % f == 0){
6968 "only-child" : function(c){
6969 var r = [], ri = -1;;
6970 for(var i = 0, ci; ci = c[i]; i++){
6971 if(!prev(ci) && !next(ci)){
6978 "empty" : function(c){
6979 var r = [], ri = -1;
6980 for(var i = 0, ci; ci = c[i]; i++){
6981 var cns = ci.childNodes, j = 0, cn, empty = true;
6984 if(cn.nodeType == 1 || cn.nodeType == 3){
6996 "contains" : function(c, v){
6997 var r = [], ri = -1;
6998 for(var i = 0, ci; ci = c[i]; i++){
6999 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7006 "nodeValue" : function(c, v){
7007 var r = [], ri = -1;
7008 for(var i = 0, ci; ci = c[i]; i++){
7009 if(ci.firstChild && ci.firstChild.nodeValue == v){
7016 "checked" : function(c){
7017 var r = [], ri = -1;
7018 for(var i = 0, ci; ci = c[i]; i++){
7019 if(ci.checked == true){
7026 "not" : function(c, ss){
7027 return Roo.DomQuery.filter(c, ss, true);
7030 "odd" : function(c){
7031 return this["nth-child"](c, "odd");
7034 "even" : function(c){
7035 return this["nth-child"](c, "even");
7038 "nth" : function(c, a){
7039 return c[a-1] || [];
7042 "first" : function(c){
7046 "last" : function(c){
7047 return c[c.length-1] || [];
7050 "has" : function(c, ss){
7051 var s = Roo.DomQuery.select;
7052 var r = [], ri = -1;
7053 for(var i = 0, ci; ci = c[i]; i++){
7054 if(s(ss, ci).length > 0){
7061 "next" : function(c, ss){
7062 var is = Roo.DomQuery.is;
7063 var r = [], ri = -1;
7064 for(var i = 0, ci; ci = c[i]; i++){
7073 "prev" : function(c, ss){
7074 var is = Roo.DomQuery.is;
7075 var r = [], ri = -1;
7076 for(var i = 0, ci; ci = c[i]; i++){
7089 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7090 * @param {String} path The selector/xpath query
7091 * @param {Node} root (optional) The start of the query (defaults to document).
7096 Roo.query = Roo.DomQuery.select;
7099 * Ext JS Library 1.1.1
7100 * Copyright(c) 2006-2007, Ext JS, LLC.
7102 * Originally Released Under LGPL - original licence link has changed is not relivant.
7105 * <script type="text/javascript">
7109 * @class Roo.util.Observable
7110 * Base class that provides a common interface for publishing events. Subclasses are expected to
7111 * to have a property "events" with all the events defined.<br>
7114 Employee = function(name){
7121 Roo.extend(Employee, Roo.util.Observable);
7123 * @param {Object} config properties to use (incuding events / listeners)
7126 Roo.util.Observable = function(cfg){
7129 this.addEvents(cfg.events || {});
7131 delete cfg.events; // make sure
7134 Roo.apply(this, cfg);
7137 this.on(this.listeners);
7138 delete this.listeners;
7141 Roo.util.Observable.prototype = {
7143 * @cfg {Object} listeners list of events and functions to call for this object,
7147 'click' : function(e) {
7157 * Fires the specified event with the passed parameters (minus the event name).
7158 * @param {String} eventName
7159 * @param {Object...} args Variable number of parameters are passed to handlers
7160 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7162 fireEvent : function(){
7163 var ce = this.events[arguments[0].toLowerCase()];
7164 if(typeof ce == "object"){
7165 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7172 filterOptRe : /^(?:scope|delay|buffer|single)$/,
7175 * Appends an event handler to this component
7176 * @param {String} eventName The type of event to listen for
7177 * @param {Function} handler The method the event invokes
7178 * @param {Object} scope (optional) The scope in which to execute the handler
7179 * function. The handler function's "this" context.
7180 * @param {Object} options (optional) An object containing handler configuration
7181 * properties. This may contain any of the following properties:<ul>
7182 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7183 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7184 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7185 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7186 * by the specified number of milliseconds. If the event fires again within that time, the original
7187 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7190 * <b>Combining Options</b><br>
7191 * Using the options argument, it is possible to combine different types of listeners:<br>
7193 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7195 el.on('click', this.onClick, this, {
7202 * <b>Attaching multiple handlers in 1 call</b><br>
7203 * The method also allows for a single argument to be passed which is a config object containing properties
7204 * which specify multiple handlers.
7213 fn: this.onMouseOver,
7217 fn: this.onMouseOut,
7223 * Or a shorthand syntax which passes the same scope object to all handlers:
7226 'click': this.onClick,
7227 'mouseover': this.onMouseOver,
7228 'mouseout': this.onMouseOut,
7233 addListener : function(eventName, fn, scope, o){
7234 if(typeof eventName == "object"){
7237 if(this.filterOptRe.test(e)){
7240 if(typeof o[e] == "function"){
7242 this.addListener(e, o[e], o.scope, o);
7244 // individual options
7245 this.addListener(e, o[e].fn, o[e].scope, o[e]);
7250 o = (!o || typeof o == "boolean") ? {} : o;
7251 eventName = eventName.toLowerCase();
7252 var ce = this.events[eventName] || true;
7253 if(typeof ce == "boolean"){
7254 ce = new Roo.util.Event(this, eventName);
7255 this.events[eventName] = ce;
7257 ce.addListener(fn, scope, o);
7261 * Removes a listener
7262 * @param {String} eventName The type of event to listen for
7263 * @param {Function} handler The handler to remove
7264 * @param {Object} scope (optional) The scope (this object) for the handler
7266 removeListener : function(eventName, fn, scope){
7267 var ce = this.events[eventName.toLowerCase()];
7268 if(typeof ce == "object"){
7269 ce.removeListener(fn, scope);
7274 * Removes all listeners for this object
7276 purgeListeners : function(){
7277 for(var evt in this.events){
7278 if(typeof this.events[evt] == "object"){
7279 this.events[evt].clearListeners();
7284 relayEvents : function(o, events){
7285 var createHandler = function(ename){
7288 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7291 for(var i = 0, len = events.length; i < len; i++){
7292 var ename = events[i];
7293 if(!this.events[ename]){
7294 this.events[ename] = true;
7296 o.on(ename, createHandler(ename), this);
7301 * Used to define events on this Observable
7302 * @param {Object} object The object with the events defined
7304 addEvents : function(o){
7308 Roo.applyIf(this.events, o);
7312 * Checks to see if this object has any listeners for a specified event
7313 * @param {String} eventName The name of the event to check for
7314 * @return {Boolean} True if the event is being listened for, else false
7316 hasListener : function(eventName){
7317 var e = this.events[eventName];
7318 return typeof e == "object" && e.listeners.length > 0;
7322 * Appends an event handler to this element (shorthand for addListener)
7323 * @param {String} eventName The type of event to listen for
7324 * @param {Function} handler The method the event invokes
7325 * @param {Object} scope (optional) The scope in which to execute the handler
7326 * function. The handler function's "this" context.
7327 * @param {Object} options (optional)
7330 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7332 * Removes a listener (shorthand for removeListener)
7333 * @param {String} eventName The type of event to listen for
7334 * @param {Function} handler The handler to remove
7335 * @param {Object} scope (optional) The scope (this object) for the handler
7338 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7341 * Starts capture on the specified Observable. All events will be passed
7342 * to the supplied function with the event name + standard signature of the event
7343 * <b>before</b> the event is fired. If the supplied function returns false,
7344 * the event will not fire.
7345 * @param {Observable} o The Observable to capture
7346 * @param {Function} fn The function to call
7347 * @param {Object} scope (optional) The scope (this object) for the fn
7350 Roo.util.Observable.capture = function(o, fn, scope){
7351 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7355 * Removes <b>all</b> added captures from the Observable.
7356 * @param {Observable} o The Observable to release
7359 Roo.util.Observable.releaseCapture = function(o){
7360 o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7365 var createBuffered = function(h, o, scope){
7366 var task = new Roo.util.DelayedTask();
7368 task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7372 var createSingle = function(h, e, fn, scope){
7374 e.removeListener(fn, scope);
7375 return h.apply(scope, arguments);
7379 var createDelayed = function(h, o, scope){
7381 var args = Array.prototype.slice.call(arguments, 0);
7382 setTimeout(function(){
7383 h.apply(scope, args);
7388 Roo.util.Event = function(obj, name){
7391 this.listeners = [];
7394 Roo.util.Event.prototype = {
7395 addListener : function(fn, scope, options){
7396 var o = options || {};
7397 scope = scope || this.obj;
7398 if(!this.isListening(fn, scope)){
7399 var l = {fn: fn, scope: scope, options: o};
7402 h = createDelayed(h, o, scope);
7405 h = createSingle(h, this, fn, scope);
7408 h = createBuffered(h, o, scope);
7411 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7412 this.listeners.push(l);
7414 this.listeners = this.listeners.slice(0);
7415 this.listeners.push(l);
7420 findListener : function(fn, scope){
7421 scope = scope || this.obj;
7422 var ls = this.listeners;
7423 for(var i = 0, len = ls.length; i < len; i++){
7425 if(l.fn == fn && l.scope == scope){
7432 isListening : function(fn, scope){
7433 return this.findListener(fn, scope) != -1;
7436 removeListener : function(fn, scope){
7438 if((index = this.findListener(fn, scope)) != -1){
7440 this.listeners.splice(index, 1);
7442 this.listeners = this.listeners.slice(0);
7443 this.listeners.splice(index, 1);
7450 clearListeners : function(){
7451 this.listeners = [];
7455 var ls = this.listeners, scope, len = ls.length;
7458 var args = Array.prototype.slice.call(arguments, 0);
7459 for(var i = 0; i < len; i++){
7461 if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7462 this.firing = false;
7466 this.firing = false;
7473 * Copyright(c) 2007-2017, Roo J Solutions Ltd
7480 * @class Roo.Document
7481 * @extends Roo.util.Observable
7482 * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7484 * @param {Object} config the methods and properties of the 'base' class for the application.
7486 * Generic Page handler - implement this to start your app..
7489 * MyProject = new Roo.Document({
7491 'load' : true // your events..
7494 'ready' : function() {
7495 // fired on Roo.onReady()
7500 Roo.Document = function(cfg) {
7505 Roo.util.Observable.call(this,cfg);
7509 Roo.onReady(function() {
7510 _this.fireEvent('ready');
7516 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7518 * Ext JS Library 1.1.1
7519 * Copyright(c) 2006-2007, Ext JS, LLC.
7521 * Originally Released Under LGPL - original licence link has changed is not relivant.
7524 * <script type="text/javascript">
7528 * @class Roo.EventManager
7529 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
7530 * several useful events directly.
7531 * See {@link Roo.EventObject} for more details on normalized event objects.
7534 Roo.EventManager = function(){
7535 var docReadyEvent, docReadyProcId, docReadyState = false;
7536 var resizeEvent, resizeTask, textEvent, textSize;
7537 var E = Roo.lib.Event;
7538 var D = Roo.lib.Dom;
7543 var fireDocReady = function(){
7545 docReadyState = true;
7548 clearInterval(docReadyProcId);
7550 if(Roo.isGecko || Roo.isOpera) {
7551 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7554 var defer = document.getElementById("ie-deferred-loader");
7556 defer.onreadystatechange = null;
7557 defer.parentNode.removeChild(defer);
7561 docReadyEvent.fire();
7562 docReadyEvent.clearListeners();
7567 var initDocReady = function(){
7568 docReadyEvent = new Roo.util.Event();
7569 if(Roo.isGecko || Roo.isOpera) {
7570 document.addEventListener("DOMContentLoaded", fireDocReady, false);
7572 document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7573 var defer = document.getElementById("ie-deferred-loader");
7574 defer.onreadystatechange = function(){
7575 if(this.readyState == "complete"){
7579 }else if(Roo.isSafari){
7580 docReadyProcId = setInterval(function(){
7581 var rs = document.readyState;
7582 if(rs == "complete") {
7587 // no matter what, make sure it fires on load
7588 E.on(window, "load", fireDocReady);
7591 var createBuffered = function(h, o){
7592 var task = new Roo.util.DelayedTask(h);
7594 // create new event object impl so new events don't wipe out properties
7595 e = new Roo.EventObjectImpl(e);
7596 task.delay(o.buffer, h, null, [e]);
7600 var createSingle = function(h, el, ename, fn){
7602 Roo.EventManager.removeListener(el, ename, fn);
7607 var createDelayed = function(h, o){
7609 // create new event object impl so new events don't wipe out properties
7610 e = new Roo.EventObjectImpl(e);
7611 setTimeout(function(){
7616 var transitionEndVal = false;
7618 var transitionEnd = function()
7620 if (transitionEndVal) {
7621 return transitionEndVal;
7623 var el = document.createElement('div');
7625 var transEndEventNames = {
7626 WebkitTransition : 'webkitTransitionEnd',
7627 MozTransition : 'transitionend',
7628 OTransition : 'oTransitionEnd otransitionend',
7629 transition : 'transitionend'
7632 for (var name in transEndEventNames) {
7633 if (el.style[name] !== undefined) {
7634 transitionEndVal = transEndEventNames[name];
7635 return transitionEndVal ;
7642 var listen = function(element, ename, opt, fn, scope)
7644 var o = (!opt || typeof opt == "boolean") ? {} : opt;
7645 fn = fn || o.fn; scope = scope || o.scope;
7646 var el = Roo.getDom(element);
7650 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7653 if (ename == 'transitionend') {
7654 ename = transitionEnd();
7656 var h = function(e){
7657 e = Roo.EventObject.setEvent(e);
7660 t = e.getTarget(o.delegate, el);
7667 if(o.stopEvent === true){
7670 if(o.preventDefault === true){
7673 if(o.stopPropagation === true){
7674 e.stopPropagation();
7677 if(o.normalized === false){
7681 fn.call(scope || el, e, t, o);
7684 h = createDelayed(h, o);
7687 h = createSingle(h, el, ename, fn);
7690 h = createBuffered(h, o);
7693 fn._handlers = fn._handlers || [];
7696 fn._handlers.push([Roo.id(el), ename, h]);
7700 E.on(el, ename, h); // this adds the actuall listener to the object..
7703 if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7704 el.addEventListener("DOMMouseScroll", h, false);
7705 E.on(window, 'unload', function(){
7706 el.removeEventListener("DOMMouseScroll", h, false);
7709 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7710 Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7715 var stopListening = function(el, ename, fn){
7716 var id = Roo.id(el), hds = fn._handlers, hd = fn;
7718 for(var i = 0, len = hds.length; i < len; i++){
7720 if(h[0] == id && h[1] == ename){
7727 E.un(el, ename, hd);
7728 el = Roo.getDom(el);
7729 if(ename == "mousewheel" && el.addEventListener){
7730 el.removeEventListener("DOMMouseScroll", hd, false);
7732 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7733 Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7737 var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7744 * @scope Roo.EventManager
7749 * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7750 * object with a Roo.EventObject
7751 * @param {Function} fn The method the event invokes
7752 * @param {Object} scope An object that becomes the scope of the handler
7753 * @param {boolean} override If true, the obj passed in becomes
7754 * the execution scope of the listener
7755 * @return {Function} The wrapped function
7758 wrap : function(fn, scope, override){
7760 Roo.EventObject.setEvent(e);
7761 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7766 * Appends an event handler to an element (shorthand for addListener)
7767 * @param {String/HTMLElement} element The html element or id to assign the
7768 * @param {String} eventName The type of event to listen for
7769 * @param {Function} handler The method the event invokes
7770 * @param {Object} scope (optional) The scope in which to execute the handler
7771 * function. The handler function's "this" context.
7772 * @param {Object} options (optional) An object containing handler configuration
7773 * properties. This may contain any of the following properties:<ul>
7774 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7775 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7776 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7777 * <li>preventDefault {Boolean} True to prevent the default action</li>
7778 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7779 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7780 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7781 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7782 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7783 * by the specified number of milliseconds. If the event fires again within that time, the original
7784 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7787 * <b>Combining Options</b><br>
7788 * Using the options argument, it is possible to combine different types of listeners:<br>
7790 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7792 el.on('click', this.onClick, this, {
7799 * <b>Attaching multiple handlers in 1 call</b><br>
7800 * The method also allows for a single argument to be passed which is a config object containing properties
7801 * which specify multiple handlers.
7811 fn: this.onMouseOver
7820 * Or a shorthand syntax:<br>
7823 'click' : this.onClick,
7824 'mouseover' : this.onMouseOver,
7825 'mouseout' : this.onMouseOut
7829 addListener : function(element, eventName, fn, scope, options){
7830 if(typeof eventName == "object"){
7836 if(typeof o[e] == "function"){
7838 listen(element, e, o, o[e], o.scope);
7840 // individual options
7841 listen(element, e, o[e]);
7846 return listen(element, eventName, options, fn, scope);
7850 * Removes an event handler
7852 * @param {String/HTMLElement} element The id or html element to remove the
7854 * @param {String} eventName The type of event
7855 * @param {Function} fn
7856 * @return {Boolean} True if a listener was actually removed
7858 removeListener : function(element, eventName, fn){
7859 return stopListening(element, eventName, fn);
7863 * Fires when the document is ready (before onload and before images are loaded). Can be
7864 * accessed shorthanded Roo.onReady().
7865 * @param {Function} fn The method the event invokes
7866 * @param {Object} scope An object that becomes the scope of the handler
7867 * @param {boolean} options
7869 onDocumentReady : function(fn, scope, options){
7870 if(docReadyState){ // if it already fired
7871 docReadyEvent.addListener(fn, scope, options);
7872 docReadyEvent.fire();
7873 docReadyEvent.clearListeners();
7879 docReadyEvent.addListener(fn, scope, options);
7883 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7884 * @param {Function} fn The method the event invokes
7885 * @param {Object} scope An object that becomes the scope of the handler
7886 * @param {boolean} options
7888 onWindowResize : function(fn, scope, options)
7891 resizeEvent = new Roo.util.Event();
7892 resizeTask = new Roo.util.DelayedTask(function(){
7893 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7895 E.on(window, "resize", function()
7898 resizeTask.delay(50);
7900 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7904 resizeEvent.addListener(fn, scope, options);
7908 * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7909 * @param {Function} fn The method the event invokes
7910 * @param {Object} scope An object that becomes the scope of the handler
7911 * @param {boolean} options
7913 onTextResize : function(fn, scope, options){
7915 textEvent = new Roo.util.Event();
7916 var textEl = new Roo.Element(document.createElement('div'));
7917 textEl.dom.className = 'x-text-resize';
7918 textEl.dom.innerHTML = 'X';
7919 textEl.appendTo(document.body);
7920 textSize = textEl.dom.offsetHeight;
7921 setInterval(function(){
7922 if(textEl.dom.offsetHeight != textSize){
7923 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7925 }, this.textResizeInterval);
7927 textEvent.addListener(fn, scope, options);
7931 * Removes the passed window resize listener.
7932 * @param {Function} fn The method the event invokes
7933 * @param {Object} scope The scope of handler
7935 removeResizeListener : function(fn, scope){
7937 resizeEvent.removeListener(fn, scope);
7942 fireResize : function(){
7944 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7948 * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7952 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7954 textResizeInterval : 50
7959 * @scopeAlias pub=Roo.EventManager
7963 * Appends an event handler to an element (shorthand for addListener)
7964 * @param {String/HTMLElement} element The html element or id to assign the
7965 * @param {String} eventName The type of event to listen for
7966 * @param {Function} handler The method the event invokes
7967 * @param {Object} scope (optional) The scope in which to execute the handler
7968 * function. The handler function's "this" context.
7969 * @param {Object} options (optional) An object containing handler configuration
7970 * properties. This may contain any of the following properties:<ul>
7971 * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7972 * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7973 * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7974 * <li>preventDefault {Boolean} True to prevent the default action</li>
7975 * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7976 * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7977 * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7978 * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7979 * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7980 * by the specified number of milliseconds. If the event fires again within that time, the original
7981 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7984 * <b>Combining Options</b><br>
7985 * Using the options argument, it is possible to combine different types of listeners:<br>
7987 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7989 el.on('click', this.onClick, this, {
7996 * <b>Attaching multiple handlers in 1 call</b><br>
7997 * The method also allows for a single argument to be passed which is a config object containing properties
7998 * which specify multiple handlers.
8008 fn: this.onMouseOver
8017 * Or a shorthand syntax:<br>
8020 'click' : this.onClick,
8021 'mouseover' : this.onMouseOver,
8022 'mouseout' : this.onMouseOut
8026 pub.on = pub.addListener;
8027 pub.un = pub.removeListener;
8029 pub.stoppedMouseDownEvent = new Roo.util.Event();
8033 * Fires when the document is ready (before onload and before images are loaded). Shorthand of {@link Roo.EventManager#onDocumentReady}.
8034 * @param {Function} fn The method the event invokes
8035 * @param {Object} scope An object that becomes the scope of the handler
8036 * @param {boolean} override If true, the obj passed in becomes
8037 * the execution scope of the listener
8041 Roo.onReady = Roo.EventManager.onDocumentReady;
8043 Roo.onReady(function(){
8044 var bd = Roo.get(document.body);
8049 : Roo.isIE11 ? "roo-ie11"
8050 : Roo.isEdge ? "roo-edge"
8051 : Roo.isGecko ? "roo-gecko"
8052 : Roo.isOpera ? "roo-opera"
8053 : Roo.isSafari ? "roo-safari" : ""];
8056 cls.push("roo-mac");
8059 cls.push("roo-linux");
8062 cls.push("roo-ios");
8065 cls.push("roo-touch");
8067 if(Roo.isBorderBox){
8068 cls.push('roo-border-box');
8070 if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8071 var p = bd.dom.parentNode;
8073 p.className += ' roo-strict';
8076 bd.addClass(cls.join(' '));
8080 * @class Roo.EventObject
8081 * EventObject exposes the Yahoo! UI Event functionality directly on the object
8082 * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code
8085 function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8087 var target = e.getTarget();
8090 var myDiv = Roo.get("myDiv");
8091 myDiv.on("click", handleClick);
8093 Roo.EventManager.on("myDiv", 'click', handleClick);
8094 Roo.EventManager.addListener("myDiv", 'click', handleClick);
8098 Roo.EventObject = function(){
8100 var E = Roo.lib.Event;
8102 // safari keypress events for special keys return bad keycodes
8105 63235 : 39, // right
8108 63276 : 33, // page up
8109 63277 : 34, // page down
8110 63272 : 46, // delete
8115 // normalize button clicks
8116 var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8117 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8119 Roo.EventObjectImpl = function(e){
8121 this.setEvent(e.browserEvent || e);
8124 Roo.EventObjectImpl.prototype = {
8126 * Used to fix doc tools.
8127 * @scope Roo.EventObject.prototype
8133 /** The normal browser event */
8134 browserEvent : null,
8135 /** The button pressed in a mouse event */
8137 /** True if the shift key was down during the event */
8139 /** True if the control key was down during the event */
8141 /** True if the alt key was down during the event */
8200 setEvent : function(e){
8201 if(e == this || (e && e.browserEvent)){ // already wrapped
8204 this.browserEvent = e;
8206 // normalize buttons
8207 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8208 if(e.type == 'click' && this.button == -1){
8212 this.shiftKey = e.shiftKey;
8213 // mac metaKey behaves like ctrlKey
8214 this.ctrlKey = e.ctrlKey || e.metaKey;
8215 this.altKey = e.altKey;
8216 // in getKey these will be normalized for the mac
8217 this.keyCode = e.keyCode;
8218 // keyup warnings on firefox.
8219 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8220 // cache the target for the delayed and or buffered events
8221 this.target = E.getTarget(e);
8223 this.xy = E.getXY(e);
8226 this.shiftKey = false;
8227 this.ctrlKey = false;
8228 this.altKey = false;
8238 * Stop the event (preventDefault and stopPropagation)
8240 stopEvent : function(){
8241 if(this.browserEvent){
8242 if(this.browserEvent.type == 'mousedown'){
8243 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8245 E.stopEvent(this.browserEvent);
8250 * Prevents the browsers default handling of the event.
8252 preventDefault : function(){
8253 if(this.browserEvent){
8254 E.preventDefault(this.browserEvent);
8259 isNavKeyPress : function(){
8260 var k = this.keyCode;
8261 k = Roo.isSafari ? (safariKeys[k] || k) : k;
8262 return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8265 isSpecialKey : function(){
8266 var k = this.keyCode;
8267 return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13 || k == 40 || k == 27 ||
8268 (k == 16) || (k == 17) ||
8269 (k >= 18 && k <= 20) ||
8270 (k >= 33 && k <= 35) ||
8271 (k >= 36 && k <= 39) ||
8272 (k >= 44 && k <= 45);
8275 * Cancels bubbling of the event.
8277 stopPropagation : function(){
8278 if(this.browserEvent){
8279 if(this.type == 'mousedown'){
8280 Roo.EventManager.stoppedMouseDownEvent.fire(this);
8282 E.stopPropagation(this.browserEvent);
8287 * Gets the key code for the event.
8290 getCharCode : function(){
8291 return this.charCode || this.keyCode;
8295 * Returns a normalized keyCode for the event.
8296 * @return {Number} The key code
8298 getKey : function(){
8299 var k = this.keyCode || this.charCode;
8300 return Roo.isSafari ? (safariKeys[k] || k) : k;
8304 * Gets the x coordinate of the event.
8307 getPageX : function(){
8312 * Gets the y coordinate of the event.
8315 getPageY : function(){
8320 * Gets the time of the event.
8323 getTime : function(){
8324 if(this.browserEvent){
8325 return E.getTime(this.browserEvent);
8331 * Gets the page coordinates of the event.
8332 * @return {Array} The xy values like [x, y]
8339 * Gets the target for the event.
8340 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8341 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8342 search as a number or element (defaults to 10 || document.body)
8343 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8344 * @return {HTMLelement}
8346 getTarget : function(selector, maxDepth, returnEl){
8347 return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8350 * Gets the related target.
8351 * @return {HTMLElement}
8353 getRelatedTarget : function(){
8354 if(this.browserEvent){
8355 return E.getRelatedTarget(this.browserEvent);
8361 * Normalizes mouse wheel delta across browsers
8362 * @return {Number} The delta
8364 getWheelDelta : function(){
8365 var e = this.browserEvent;
8367 if(e.wheelDelta){ /* IE/Opera. */
8368 delta = e.wheelDelta/120;
8369 }else if(e.detail){ /* Mozilla case. */
8370 delta = -e.detail/3;
8376 * Returns true if the control, meta, shift or alt key was pressed during this event.
8379 hasModifier : function(){
8380 return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8384 * Returns true if the target of this event equals el or is a child of el
8385 * @param {String/HTMLElement/Element} el
8386 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8389 within : function(el, related){
8390 var t = this[related ? "getRelatedTarget" : "getTarget"]();
8391 return t && Roo.fly(el).contains(t);
8394 getPoint : function(){
8395 return new Roo.lib.Point(this.xy[0], this.xy[1]);
8399 return new Roo.EventObjectImpl();
8404 * Ext JS Library 1.1.1
8405 * Copyright(c) 2006-2007, Ext JS, LLC.
8407 * Originally Released Under LGPL - original licence link has changed is not relivant.
8410 * <script type="text/javascript">
8414 // was in Composite Element!??!?!
8417 var D = Roo.lib.Dom;
8418 var E = Roo.lib.Event;
8419 var A = Roo.lib.Anim;
8421 // local style camelizing for speed
8423 var camelRe = /(-[a-z])/gi;
8424 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8425 var view = document.defaultView;
8428 * @class Roo.Element
8429 * Represents an Element in the DOM.<br><br>
8432 var el = Roo.get("my-div");
8435 var el = getEl("my-div");
8437 // or with a DOM element
8438 var el = Roo.get(myDivElement);
8440 * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8441 * each call instead of constructing a new one.<br><br>
8442 * <b>Animations</b><br />
8443 * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8444 * should either be a boolean (true) or an object literal with animation options. The animation options are:
8446 Option Default Description
8447 --------- -------- ---------------------------------------------
8448 duration .35 The duration of the animation in seconds
8449 easing easeOut The YUI easing method
8450 callback none A function to execute when the anim completes
8451 scope this The scope (this) of the callback function
8453 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8454 * manipulate the animation. Here's an example:
8456 var el = Roo.get("my-div");
8461 // default animation
8462 el.setWidth(100, true);
8464 // animation with some options set
8471 // using the "anim" property to get the Anim object
8477 el.setWidth(100, opt);
8479 if(opt.anim.isAnimated()){
8483 * <b> Composite (Collections of) Elements</b><br />
8484 * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8485 * @constructor Create a new Element directly.
8486 * @param {String/HTMLElement} element
8487 * @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).
8489 Roo.Element = function(element, forceNew)
8491 var dom = typeof element == "string" ?
8492 document.getElementById(element) : element;
8494 this.listeners = {};
8496 if(!dom){ // invalid id/element
8500 if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8501 return Roo.Element.cache[id];
8511 * The DOM element ID
8514 this.id = id || Roo.id(dom);
8516 return this; // assumed for cctor?
8519 var El = Roo.Element;
8523 * The element's default display mode (defaults to "")
8526 originalDisplay : "",
8529 // note this is overridden in BS version..
8532 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8538 * Sets the element's visibility mode. When setVisible() is called it
8539 * will use this to determine whether to set the visibility or the display property.
8540 * @param visMode Element.VISIBILITY or Element.DISPLAY
8541 * @return {Roo.Element} this
8543 setVisibilityMode : function(visMode){
8544 this.visibilityMode = visMode;
8548 * Convenience method for setVisibilityMode(Element.DISPLAY)
8549 * @param {String} display (optional) What to set display to when visible
8550 * @return {Roo.Element} this
8552 enableDisplayMode : function(display){
8553 this.setVisibilityMode(El.DISPLAY);
8554 if(typeof display != "undefined") { this.originalDisplay = display; }
8559 * 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)
8560 * @param {String} selector The simple selector to test
8561 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8562 search as a number or element (defaults to 10 || document.body)
8563 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8564 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8566 findParent : function(simpleSelector, maxDepth, returnEl){
8567 var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8568 maxDepth = maxDepth || 50;
8569 if(typeof maxDepth != "number"){
8570 stopEl = Roo.getDom(maxDepth);
8573 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8574 if(dq.is(p, simpleSelector)){
8575 return returnEl ? Roo.get(p) : p;
8585 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8586 * @param {String} selector The simple selector to test
8587 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8588 search as a number or element (defaults to 10 || document.body)
8589 * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8590 * @return {HTMLElement} The matching DOM node (or null if no match was found)
8592 findParentNode : function(simpleSelector, maxDepth, returnEl){
8593 var p = Roo.fly(this.dom.parentNode, '_internal');
8594 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8598 * Looks at the scrollable parent element
8600 findScrollableParent : function()
8602 var overflowRegex = /(auto|scroll)/;
8604 if(this.getStyle('position') === 'fixed'){
8605 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8608 var excludeStaticParent = this.getStyle('position') === "absolute";
8610 for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8612 if (excludeStaticParent && parent.getStyle('position') === "static") {
8616 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8620 if(parent.dom.nodeName.toLowerCase() == 'body'){
8621 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8625 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8629 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8630 * This is a shortcut for findParentNode() that always returns an Roo.Element.
8631 * @param {String} selector The simple selector to test
8632 * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8633 search as a number or element (defaults to 10 || document.body)
8634 * @return {Roo.Element} The matching DOM node (or null if no match was found)
8636 up : function(simpleSelector, maxDepth){
8637 return this.findParentNode(simpleSelector, maxDepth, true);
8643 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8644 * @param {String} selector The simple selector to test
8645 * @return {Boolean} True if this element matches the selector, else false
8647 is : function(simpleSelector){
8648 return Roo.DomQuery.is(this.dom, simpleSelector);
8652 * Perform animation on this element.
8653 * @param {Object} args The YUI animation control args
8654 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8655 * @param {Function} onComplete (optional) Function to call when animation completes
8656 * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8657 * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8658 * @return {Roo.Element} this
8660 animate : function(args, duration, onComplete, easing, animType){
8661 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8666 * @private Internal animation call
8668 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8669 animType = animType || 'run';
8671 var anim = Roo.lib.Anim[animType](
8673 (opt.duration || defaultDur) || .35,
8674 (opt.easing || defaultEase) || 'easeOut',
8676 Roo.callback(cb, this);
8677 Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8685 // private legacy anim prep
8686 preanim : function(a, i){
8687 return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8691 * Removes worthless text nodes
8692 * @param {Boolean} forceReclean (optional) By default the element
8693 * keeps track if it has been cleaned already so
8694 * you can call this over and over. However, if you update the element and
8695 * need to force a reclean, you can pass true.
8697 clean : function(forceReclean){
8698 if(this.isCleaned && forceReclean !== true){
8702 var d = this.dom, n = d.firstChild, ni = -1;
8704 var nx = n.nextSibling;
8705 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8712 this.isCleaned = true;
8717 calcOffsetsTo : function(el){
8720 var restorePos = false;
8721 if(el.getStyle('position') == 'static'){
8722 el.position('relative');
8727 while(op && op != d && op.tagName != 'HTML'){
8730 op = op.offsetParent;
8733 el.position('static');
8739 * Scrolls this element into view within the passed container.
8740 * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8741 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8742 * @return {Roo.Element} this
8744 scrollIntoView : function(container, hscroll){
8745 var c = Roo.getDom(container) || document.body;
8748 var o = this.calcOffsetsTo(c),
8751 b = t+el.offsetHeight,
8752 r = l+el.offsetWidth;
8754 var ch = c.clientHeight;
8755 var ct = parseInt(c.scrollTop, 10);
8756 var cl = parseInt(c.scrollLeft, 10);
8758 var cr = cl + c.clientWidth;
8766 if(hscroll !== false){
8770 c.scrollLeft = r-c.clientWidth;
8777 scrollChildIntoView : function(child, hscroll){
8778 Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8782 * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8783 * the new height may not be available immediately.
8784 * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8785 * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8786 * @param {Function} onComplete (optional) Function to call when animation completes
8787 * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8788 * @return {Roo.Element} this
8790 autoHeight : function(animate, duration, onComplete, easing){
8791 var oldHeight = this.getHeight();
8793 this.setHeight(1); // force clipping
8794 setTimeout(function(){
8795 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8797 this.setHeight(height);
8799 if(typeof onComplete == "function"){
8803 this.setHeight(oldHeight); // restore original height
8804 this.setHeight(height, animate, duration, function(){
8806 if(typeof onComplete == "function") { onComplete(); }
8807 }.createDelegate(this), easing);
8809 }.createDelegate(this), 0);
8814 * Returns true if this element is an ancestor of the passed element
8815 * @param {HTMLElement/String} el The element to check
8816 * @return {Boolean} True if this element is an ancestor of el, else false
8818 contains : function(el){
8819 if(!el){return false;}
8820 return D.isAncestor(this.dom, el.dom ? el.dom : el);
8824 * Checks whether the element is currently visible using both visibility and display properties.
8825 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8826 * @return {Boolean} True if the element is currently visible, else false
8828 isVisible : function(deep) {
8829 var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8830 if(deep !== true || !vis){
8833 var p = this.dom.parentNode;
8834 while(p && p.tagName.toLowerCase() != "body"){
8835 if(!Roo.fly(p, '_isVisible').isVisible()){
8844 * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8845 * @param {String} selector The CSS selector
8846 * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8847 * @return {CompositeElement/CompositeElementLite} The composite element
8849 select : function(selector, unique){
8850 return El.select(selector, unique, this.dom);
8854 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8855 * @param {String} selector The CSS selector
8856 * @return {Array} An array of the matched nodes
8858 query : function(selector, unique){
8859 return Roo.DomQuery.select(selector, this.dom);
8863 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8864 * @param {String} selector The CSS selector
8865 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8866 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8868 child : function(selector, returnDom){
8869 var n = Roo.DomQuery.selectNode(selector, this.dom);
8870 return returnDom ? n : Roo.get(n);
8874 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8875 * @param {String} selector The CSS selector
8876 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8877 * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8879 down : function(selector, returnDom){
8880 var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8881 return returnDom ? n : Roo.get(n);
8885 * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8886 * @param {String} group The group the DD object is member of
8887 * @param {Object} config The DD config object
8888 * @param {Object} overrides An object containing methods to override/implement on the DD object
8889 * @return {Roo.dd.DD} The DD object
8891 initDD : function(group, config, overrides){
8892 var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8893 return Roo.apply(dd, overrides);
8897 * Initializes a {@link Roo.dd.DDProxy} object for this element.
8898 * @param {String} group The group the DDProxy object is member of
8899 * @param {Object} config The DDProxy config object
8900 * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8901 * @return {Roo.dd.DDProxy} The DDProxy object
8903 initDDProxy : function(group, config, overrides){
8904 var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8905 return Roo.apply(dd, overrides);
8909 * Initializes a {@link Roo.dd.DDTarget} object for this element.
8910 * @param {String} group The group the DDTarget object is member of
8911 * @param {Object} config The DDTarget config object
8912 * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8913 * @return {Roo.dd.DDTarget} The DDTarget object
8915 initDDTarget : function(group, config, overrides){
8916 var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8917 return Roo.apply(dd, overrides);
8921 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8922 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8923 * @param {Boolean} visible Whether the element is visible
8924 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8925 * @return {Roo.Element} this
8927 setVisible : function(visible, animate){
8929 if(this.visibilityMode == El.DISPLAY){
8930 this.setDisplayed(visible);
8933 this.dom.style.visibility = visible ? "visible" : "hidden";
8936 // closure for composites
8938 var visMode = this.visibilityMode;
8940 this.setOpacity(.01);
8941 this.setVisible(true);
8943 this.anim({opacity: { to: (visible?1:0) }},
8944 this.preanim(arguments, 1),
8945 null, .35, 'easeIn', function(){
8947 if(visMode == El.DISPLAY){
8948 dom.style.display = "none";
8950 dom.style.visibility = "hidden";
8952 Roo.get(dom).setOpacity(1);
8960 * Returns true if display is not "none"
8963 isDisplayed : function() {
8964 return this.getStyle("display") != "none";
8968 * Toggles the element's visibility or display, depending on visibility mode.
8969 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8970 * @return {Roo.Element} this
8972 toggle : function(animate){
8973 this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8978 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8979 * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8980 * @return {Roo.Element} this
8982 setDisplayed : function(value) {
8983 if(typeof value == "boolean"){
8984 value = value ? this.originalDisplay : "none";
8986 this.setStyle("display", value);
8991 * Tries to focus the element. Any exceptions are caught and ignored.
8992 * @return {Roo.Element} this
8994 focus : function() {
9002 * Tries to blur the element. Any exceptions are caught and ignored.
9003 * @return {Roo.Element} this
9013 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9014 * @param {String/Array} className The CSS class to add, or an array of classes
9015 * @return {Roo.Element} this
9017 addClass : function(className){
9018 if(className instanceof Array){
9019 for(var i = 0, len = className.length; i < len; i++) {
9020 this.addClass(className[i]);
9023 if(className && !this.hasClass(className)){
9024 if (this.dom instanceof SVGElement) {
9025 this.dom.className.baseVal =this.dom.className.baseVal + " " + className;
9027 this.dom.className = this.dom.className + " " + className;
9035 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9036 * @param {String/Array} className The CSS class to add, or an array of classes
9037 * @return {Roo.Element} this
9039 radioClass : function(className){
9040 var siblings = this.dom.parentNode.childNodes;
9041 for(var i = 0; i < siblings.length; i++) {
9042 var s = siblings[i];
9043 if(s.nodeType == 1){
9044 Roo.get(s).removeClass(className);
9047 this.addClass(className);
9052 * Removes one or more CSS classes from the element.
9053 * @param {String/Array} className The CSS class to remove, or an array of classes
9054 * @return {Roo.Element} this
9056 removeClass : function(className){
9058 var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9059 if(!className || !cn){
9062 if(className instanceof Array){
9063 for(var i = 0, len = className.length; i < len; i++) {
9064 this.removeClass(className[i]);
9067 if(this.hasClass(className)){
9068 var re = this.classReCache[className];
9070 re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9071 this.classReCache[className] = re;
9073 if (this.dom instanceof SVGElement) {
9074 this.dom.className.baseVal = cn.replace(re, " ");
9076 this.dom.className = cn.replace(re, " ");
9087 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9088 * @param {String} className The CSS class to toggle
9089 * @return {Roo.Element} this
9091 toggleClass : function(className){
9092 if(this.hasClass(className)){
9093 this.removeClass(className);
9095 this.addClass(className);
9101 * Checks if the specified CSS class exists on this element's DOM node.
9102 * @param {String} className The CSS class to check for
9103 * @return {Boolean} True if the class exists, else false
9105 hasClass : function(className){
9106 if (this.dom instanceof SVGElement) {
9107 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1;
9109 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9113 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
9114 * @param {String} oldClassName The CSS class to replace
9115 * @param {String} newClassName The replacement CSS class
9116 * @return {Roo.Element} this
9118 replaceClass : function(oldClassName, newClassName){
9119 this.removeClass(oldClassName);
9120 this.addClass(newClassName);
9125 * Returns an object with properties matching the styles requested.
9126 * For example, el.getStyles('color', 'font-size', 'width') might return
9127 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9128 * @param {String} style1 A style name
9129 * @param {String} style2 A style name
9130 * @param {String} etc.
9131 * @return {Object} The style object
9133 getStyles : function(){
9134 var a = arguments, len = a.length, r = {};
9135 for(var i = 0; i < len; i++){
9136 r[a[i]] = this.getStyle(a[i]);
9142 * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9143 * @param {String} property The style property whose value is returned.
9144 * @return {String} The current value of the style property for this element.
9146 getStyle : function(){
9147 return view && view.getComputedStyle ?
9149 var el = this.dom, v, cs, camel;
9150 if(prop == 'float'){
9153 if(el.style && (v = el.style[prop])){
9156 if(cs = view.getComputedStyle(el, "")){
9157 if(!(camel = propCache[prop])){
9158 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9165 var el = this.dom, v, cs, camel;
9166 if(prop == 'opacity'){
9167 if(typeof el.style.filter == 'string'){
9168 var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9170 var fv = parseFloat(m[1]);
9172 return fv ? fv / 100 : 0;
9177 }else if(prop == 'float'){
9178 prop = "styleFloat";
9180 if(!(camel = propCache[prop])){
9181 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9183 if(v = el.style[camel]){
9186 if(cs = el.currentStyle){
9194 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9195 * @param {String/Object} property The style property to be set, or an object of multiple styles.
9196 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9197 * @return {Roo.Element} this
9199 setStyle : function(prop, value){
9200 if(typeof prop == "string"){
9202 if (prop == 'float') {
9203 this.setStyle(Roo.isIE ? 'styleFloat' : 'cssFloat', value);
9208 if(!(camel = propCache[prop])){
9209 camel = propCache[prop] = prop.replace(camelRe, camelFn);
9212 if(camel == 'opacity') {
9213 this.setOpacity(value);
9215 this.dom.style[camel] = value;
9218 for(var style in prop){
9219 if(typeof prop[style] != "function"){
9220 this.setStyle(style, prop[style]);
9228 * More flexible version of {@link #setStyle} for setting style properties.
9229 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9230 * a function which returns such a specification.
9231 * @return {Roo.Element} this
9233 applyStyles : function(style){
9234 Roo.DomHelper.applyStyles(this.dom, style);
9239 * 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).
9240 * @return {Number} The X position of the element
9243 return D.getX(this.dom);
9247 * 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).
9248 * @return {Number} The Y position of the element
9251 return D.getY(this.dom);
9255 * 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).
9256 * @return {Array} The XY position of the element
9259 return D.getXY(this.dom);
9263 * 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).
9264 * @param {Number} The X position of the element
9265 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9266 * @return {Roo.Element} this
9268 setX : function(x, animate){
9270 D.setX(this.dom, x);
9272 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9278 * 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).
9279 * @param {Number} The Y position of the element
9280 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9281 * @return {Roo.Element} this
9283 setY : function(y, animate){
9285 D.setY(this.dom, y);
9287 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9293 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9294 * @param {String} left The left CSS property value
9295 * @return {Roo.Element} this
9297 setLeft : function(left){
9298 this.setStyle("left", this.addUnits(left));
9303 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9304 * @param {String} top The top CSS property value
9305 * @return {Roo.Element} this
9307 setTop : function(top){
9308 this.setStyle("top", this.addUnits(top));
9313 * Sets the element's CSS right style.
9314 * @param {String} right The right CSS property value
9315 * @return {Roo.Element} this
9317 setRight : function(right){
9318 this.setStyle("right", this.addUnits(right));
9323 * Sets the element's CSS bottom style.
9324 * @param {String} bottom The bottom CSS property value
9325 * @return {Roo.Element} this
9327 setBottom : function(bottom){
9328 this.setStyle("bottom", this.addUnits(bottom));
9333 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9334 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9335 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9336 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9337 * @return {Roo.Element} this
9339 setXY : function(pos, animate){
9341 D.setXY(this.dom, pos);
9343 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9349 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9350 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9351 * @param {Number} x X value for new position (coordinates are page-based)
9352 * @param {Number} y Y value for new position (coordinates are page-based)
9353 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9354 * @return {Roo.Element} this
9356 setLocation : function(x, y, animate){
9357 this.setXY([x, y], this.preanim(arguments, 2));
9362 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9363 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9364 * @param {Number} x X value for new position (coordinates are page-based)
9365 * @param {Number} y Y value for new position (coordinates are page-based)
9366 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9367 * @return {Roo.Element} this
9369 moveTo : function(x, y, animate){
9370 this.setXY([x, y], this.preanim(arguments, 2));
9375 * Returns the region of the given element.
9376 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9377 * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9379 getRegion : function(){
9380 return D.getRegion(this.dom);
9384 * Returns the offset height of the element
9385 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9386 * @return {Number} The element's height
9388 getHeight : function(contentHeight){
9389 var h = this.dom.offsetHeight || 0;
9390 return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9394 * Returns the offset width of the element
9395 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9396 * @return {Number} The element's width
9398 getWidth : function(contentWidth){
9399 var w = this.dom.offsetWidth || 0;
9400 return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9404 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9405 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9406 * if a height has not been set using CSS.
9409 getComputedHeight : function(){
9410 var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9412 h = parseInt(this.getStyle('height'), 10) || 0;
9413 if(!this.isBorderBox()){
9414 h += this.getFrameWidth('tb');
9421 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9422 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9423 * if a width has not been set using CSS.
9426 getComputedWidth : function(){
9427 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9429 w = parseInt(this.getStyle('width'), 10) || 0;
9430 if(!this.isBorderBox()){
9431 w += this.getFrameWidth('lr');
9438 * Returns the size of the element.
9439 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9440 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9442 getSize : function(contentSize){
9443 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9447 * Returns the width and height of the viewport.
9448 * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9450 getViewSize : function(){
9451 var d = this.dom, doc = document, aw = 0, ah = 0;
9452 if(d == doc || d == doc.body){
9453 return {width : D.getViewWidth(), height: D.getViewHeight()};
9456 width : d.clientWidth,
9457 height: d.clientHeight
9463 * Returns the value of the "value" attribute
9464 * @param {Boolean} asNumber true to parse the value as a number
9465 * @return {String/Number}
9467 getValue : function(asNumber){
9468 return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9472 adjustWidth : function(width){
9473 if(typeof width == "number"){
9474 if(this.autoBoxAdjust && !this.isBorderBox()){
9475 width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9485 adjustHeight : function(height){
9486 if(typeof height == "number"){
9487 if(this.autoBoxAdjust && !this.isBorderBox()){
9488 height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9498 * Set the width of the element
9499 * @param {Number} width The new width
9500 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9501 * @return {Roo.Element} this
9503 setWidth : function(width, animate){
9504 width = this.adjustWidth(width);
9506 this.dom.style.width = this.addUnits(width);
9508 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9514 * Set the height of the element
9515 * @param {Number} height The new height
9516 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9517 * @return {Roo.Element} this
9519 setHeight : function(height, animate){
9520 height = this.adjustHeight(height);
9522 this.dom.style.height = this.addUnits(height);
9524 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9530 * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9531 * @param {Number} width The new width
9532 * @param {Number} height The new height
9533 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9534 * @return {Roo.Element} this
9536 setSize : function(width, height, animate){
9537 if(typeof width == "object"){ // in case of object from getSize()
9538 height = width.height; width = width.width;
9540 width = this.adjustWidth(width); height = this.adjustHeight(height);
9542 this.dom.style.width = this.addUnits(width);
9543 this.dom.style.height = this.addUnits(height);
9545 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9551 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9552 * @param {Number} x X value for new position (coordinates are page-based)
9553 * @param {Number} y Y value for new position (coordinates are page-based)
9554 * @param {Number} width The new width
9555 * @param {Number} height The new height
9556 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9557 * @return {Roo.Element} this
9559 setBounds : function(x, y, width, height, animate){
9561 this.setSize(width, height);
9562 this.setLocation(x, y);
9564 width = this.adjustWidth(width); height = this.adjustHeight(height);
9565 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9566 this.preanim(arguments, 4), 'motion');
9572 * 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.
9573 * @param {Roo.lib.Region} region The region to fill
9574 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9575 * @return {Roo.Element} this
9577 setRegion : function(region, animate){
9578 this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9583 * Appends an event handler
9585 * @param {String} eventName The type of event to append
9586 * @param {Function} fn The method the event invokes
9587 * @param {Object} scope (optional) The scope (this object) of the fn
9588 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
9590 addListener : function(eventName, fn, scope, options)
9592 if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9593 this.addListener('touchstart', this.onTapHandler, this);
9596 // we need to handle a special case where dom element is a svg element.
9597 // in this case we do not actua
9602 if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9603 if (typeof(this.listeners[eventName]) == 'undefined') {
9604 this.listeners[eventName] = new Roo.util.Event(this, eventName);
9606 this.listeners[eventName].addListener(fn, scope, options);
9611 Roo.EventManager.on(this.dom, eventName, fn, scope || this, options);
9616 onTapHandler : function(event)
9618 if(!this.tapedTwice) {
9619 this.tapedTwice = true;
9621 setTimeout( function() {
9622 s.tapedTwice = false;
9626 event.preventDefault();
9627 var revent = new MouseEvent('dblclick', {
9633 this.dom.dispatchEvent(revent);
9634 //action on double tap goes below
9639 * Removes an event handler from this element
9640 * @param {String} eventName the type of event to remove
9641 * @param {Function} fn the method the event invokes
9642 * @param {Function} scope (needed for svg fake listeners)
9643 * @return {Roo.Element} this
9645 removeListener : function(eventName, fn, scope){
9646 Roo.EventManager.removeListener(this.dom, eventName, fn);
9647 if (typeof(this.listeners) == 'undefined' || typeof(this.listeners[eventName]) == 'undefined') {
9650 this.listeners[eventName].removeListener(fn, scope);
9655 * Removes all previous added listeners from this element
9656 * @return {Roo.Element} this
9658 removeAllListeners : function(){
9659 E.purgeElement(this.dom);
9660 this.listeners = {};
9664 relayEvent : function(eventName, observable){
9665 this.on(eventName, function(e){
9666 observable.fireEvent(eventName, e);
9672 * Set the opacity of the element
9673 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9674 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9675 * @return {Roo.Element} this
9677 setOpacity : function(opacity, animate){
9679 var s = this.dom.style;
9682 s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9683 (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9685 s.opacity = opacity;
9688 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9694 * Gets the left X coordinate
9695 * @param {Boolean} local True to get the local css position instead of page coordinate
9698 getLeft : function(local){
9702 return parseInt(this.getStyle("left"), 10) || 0;
9707 * Gets the right X coordinate of the element (element X position + element width)
9708 * @param {Boolean} local True to get the local css position instead of page coordinate
9711 getRight : function(local){
9713 return this.getX() + this.getWidth();
9715 return (this.getLeft(true) + this.getWidth()) || 0;
9720 * Gets the top Y coordinate
9721 * @param {Boolean} local True to get the local css position instead of page coordinate
9724 getTop : function(local) {
9728 return parseInt(this.getStyle("top"), 10) || 0;
9733 * Gets the bottom Y coordinate of the element (element Y position + element height)
9734 * @param {Boolean} local True to get the local css position instead of page coordinate
9737 getBottom : function(local){
9739 return this.getY() + this.getHeight();
9741 return (this.getTop(true) + this.getHeight()) || 0;
9746 * Initializes positioning on this element. If a desired position is not passed, it will make the
9747 * the element positioned relative IF it is not already positioned.
9748 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9749 * @param {Number} zIndex (optional) The zIndex to apply
9750 * @param {Number} x (optional) Set the page X position
9751 * @param {Number} y (optional) Set the page Y position
9753 position : function(pos, zIndex, x, y){
9755 if(this.getStyle('position') == 'static'){
9756 this.setStyle('position', 'relative');
9759 this.setStyle("position", pos);
9762 this.setStyle("z-index", zIndex);
9764 if(x !== undefined && y !== undefined){
9766 }else if(x !== undefined){
9768 }else if(y !== undefined){
9774 * Clear positioning back to the default when the document was loaded
9775 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9776 * @return {Roo.Element} this
9778 clearPositioning : function(value){
9786 "position" : "static"
9792 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9793 * snapshot before performing an update and then restoring the element.
9796 getPositioning : function(){
9797 var l = this.getStyle("left");
9798 var t = this.getStyle("top");
9800 "position" : this.getStyle("position"),
9802 "right" : l ? "" : this.getStyle("right"),
9804 "bottom" : t ? "" : this.getStyle("bottom"),
9805 "z-index" : this.getStyle("z-index")
9810 * Gets the width of the border(s) for the specified side(s)
9811 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9812 * passing lr would get the border (l)eft width + the border (r)ight width.
9813 * @return {Number} The width of the sides passed added together
9815 getBorderWidth : function(side){
9816 return this.addStyles(side, El.borders);
9820 * Gets the width of the padding(s) for the specified side(s)
9821 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9822 * passing lr would get the padding (l)eft + the padding (r)ight.
9823 * @return {Number} The padding of the sides passed added together
9825 getPadding : function(side){
9826 return this.addStyles(side, El.paddings);
9830 * Set positioning with an object returned by getPositioning().
9831 * @param {Object} posCfg
9832 * @return {Roo.Element} this
9834 setPositioning : function(pc){
9835 this.applyStyles(pc);
9836 if(pc.right == "auto"){
9837 this.dom.style.right = "";
9839 if(pc.bottom == "auto"){
9840 this.dom.style.bottom = "";
9846 fixDisplay : function(){
9847 if(this.getStyle("display") == "none"){
9848 this.setStyle("visibility", "hidden");
9849 this.setStyle("display", this.originalDisplay); // first try reverting to default
9850 if(this.getStyle("display") == "none"){ // if that fails, default to block
9851 this.setStyle("display", "block");
9857 * Quick set left and top adding default units
9858 * @param {String} left The left CSS property value
9859 * @param {String} top The top CSS property value
9860 * @return {Roo.Element} this
9862 setLeftTop : function(left, top){
9863 this.dom.style.left = this.addUnits(left);
9864 this.dom.style.top = this.addUnits(top);
9869 * Move this element relative to its current position.
9870 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9871 * @param {Number} distance How far to move the element in pixels
9872 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9873 * @return {Roo.Element} this
9875 move : function(direction, distance, animate){
9876 var xy = this.getXY();
9877 direction = direction.toLowerCase();
9881 this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9885 this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9890 this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9895 this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9902 * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9903 * @return {Roo.Element} this
9906 if(!this.isClipped){
9907 this.isClipped = true;
9908 this.originalClip = {
9909 "o": this.getStyle("overflow"),
9910 "x": this.getStyle("overflow-x"),
9911 "y": this.getStyle("overflow-y")
9913 this.setStyle("overflow", "hidden");
9914 this.setStyle("overflow-x", "hidden");
9915 this.setStyle("overflow-y", "hidden");
9921 * Return clipping (overflow) to original clipping before clip() was called
9922 * @return {Roo.Element} this
9924 unclip : function(){
9926 this.isClipped = false;
9927 var o = this.originalClip;
9928 if(o.o){this.setStyle("overflow", o.o);}
9929 if(o.x){this.setStyle("overflow-x", o.x);}
9930 if(o.y){this.setStyle("overflow-y", o.y);}
9937 * Gets the x,y coordinates specified by the anchor position on the element.
9938 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo} for details on supported anchor positions.
9939 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9940 * {width: (target width), height: (target height)} (defaults to the element's current size)
9941 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9942 * @return {Array} [x, y] An array containing the element's x and y coordinates
9944 getAnchorXY : function(anchor, local, s){
9945 //Passing a different size is useful for pre-calculating anchors,
9946 //especially for anchored animations that change the el size.
9948 var w, h, vp = false;
9951 if(d == document.body || d == document){
9953 w = D.getViewWidth(); h = D.getViewHeight();
9955 w = this.getWidth(); h = this.getHeight();
9958 w = s.width; h = s.height;
9960 var x = 0, y = 0, r = Math.round;
9961 switch((anchor || "tl").toLowerCase()){
10003 var sc = this.getScroll();
10004 return [x + sc.left, y + sc.top];
10006 //Add the element's offset xy
10007 var o = this.getXY();
10008 return [x+o[0], y+o[1]];
10012 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10013 * supported position values.
10014 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10015 * @param {String} position The position to align to.
10016 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10017 * @return {Array} [x, y]
10019 getAlignToXY : function(el, p, o)
10024 throw "Element.alignTo with an element that doesn't exist";
10026 var c = false; //constrain to viewport
10027 var p1 = "", p2 = "";
10032 }else if(p == "?"){
10034 }else if(p.indexOf("-") == -1){
10037 p = p.toLowerCase();
10038 var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10040 throw "Element.alignTo with an invalid alignment " + p;
10042 p1 = m[1]; p2 = m[2]; c = !!m[3];
10044 //Subtract the aligned el's internal xy from the target's offset xy
10045 //plus custom offset to get the aligned el's new offset xy
10046 var a1 = this.getAnchorXY(p1, true);
10047 var a2 = el.getAnchorXY(p2, false);
10048 var x = a2[0] - a1[0] + o[0];
10049 var y = a2[1] - a1[1] + o[1];
10051 //constrain the aligned el to viewport if necessary
10052 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10053 // 5px of margin for ie
10054 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10056 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10057 //perpendicular to the vp border, allow the aligned el to slide on that border,
10058 //otherwise swap the aligned el to the opposite border of the target.
10059 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10060 var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10061 var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t") );
10062 var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10064 var doc = document;
10065 var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10066 var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10068 if((x+w) > dw + scrollX){
10069 x = swapX ? r.left-w : dw+scrollX-w;
10072 x = swapX ? r.right : scrollX;
10074 if((y+h) > dh + scrollY){
10075 y = swapY ? r.top-h : dh+scrollY-h;
10078 y = swapY ? r.bottom : scrollY;
10085 getConstrainToXY : function(){
10086 var os = {top:0, left:0, bottom:0, right: 0};
10088 return function(el, local, offsets, proposedXY){
10090 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10092 var vw, vh, vx = 0, vy = 0;
10093 if(el.dom == document.body || el.dom == document){
10094 vw = Roo.lib.Dom.getViewWidth();
10095 vh = Roo.lib.Dom.getViewHeight();
10097 vw = el.dom.clientWidth;
10098 vh = el.dom.clientHeight;
10100 var vxy = el.getXY();
10106 var s = el.getScroll();
10108 vx += offsets.left + s.left;
10109 vy += offsets.top + s.top;
10111 vw -= offsets.right;
10112 vh -= offsets.bottom;
10117 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10118 var x = xy[0], y = xy[1];
10119 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10121 // only move it if it needs it
10124 // first validate right/bottom
10133 // then make sure top/left isn't negative
10142 return moved ? [x, y] : false;
10147 adjustForConstraints : function(xy, parent, offsets){
10148 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
10152 * Aligns this element with another element relative to the specified anchor points. If the other element is the
10153 * document it aligns it to the viewport.
10154 * The position parameter is optional, and can be specified in any one of the following formats:
10156 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10157 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10158 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
10159 * deprecated in favor of the newer two anchor syntax below</i>.</li>
10160 * <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
10161 * element's anchor point, and the second value is used as the target's anchor point.</li>
10163 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
10164 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10165 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
10166 * that specified in order to enforce the viewport constraints.
10167 * Following are all of the supported anchor positions:
10170 ----- -----------------------------
10171 tl The top left corner (default)
10172 t The center of the top edge
10173 tr The top right corner
10174 l The center of the left edge
10175 c In the center of the element
10176 r The center of the right edge
10177 bl The bottom left corner
10178 b The center of the bottom edge
10179 br The bottom right corner
10183 // align el to other-el using the default positioning ("tl-bl", non-constrained)
10184 el.alignTo("other-el");
10186 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10187 el.alignTo("other-el", "tr?");
10189 // align the bottom right corner of el with the center left edge of other-el
10190 el.alignTo("other-el", "br-l?");
10192 // align the center of el with the bottom left corner of other-el and
10193 // adjust the x position by -6 pixels (and the y position by 0)
10194 el.alignTo("other-el", "c-bl", [-6, 0]);
10196 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10197 * @param {String} position The position to align to.
10198 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10199 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10200 * @return {Roo.Element} this
10202 alignTo : function(element, position, offsets, animate){
10203 var xy = this.getAlignToXY(element, position, offsets);
10204 this.setXY(xy, this.preanim(arguments, 3));
10209 * Anchors an element to another element and realigns it when the window is resized.
10210 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10211 * @param {String} position The position to align to.
10212 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10213 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10214 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10215 * is a number, it is used as the buffer delay (defaults to 50ms).
10216 * @param {Function} callback The function to call after the animation finishes
10217 * @return {Roo.Element} this
10219 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10220 var action = function(){
10221 this.alignTo(el, alignment, offsets, animate);
10222 Roo.callback(callback, this);
10224 Roo.EventManager.onWindowResize(action, this);
10225 var tm = typeof monitorScroll;
10226 if(tm != 'undefined'){
10227 Roo.EventManager.on(window, 'scroll', action, this,
10228 {buffer: tm == 'number' ? monitorScroll : 50});
10230 action.call(this); // align immediately
10234 * Clears any opacity settings from this element. Required in some cases for IE.
10235 * @return {Roo.Element} this
10237 clearOpacity : function(){
10238 if (window.ActiveXObject) {
10239 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10240 this.dom.style.filter = "";
10243 this.dom.style.opacity = "";
10244 this.dom.style["-moz-opacity"] = "";
10245 this.dom.style["-khtml-opacity"] = "";
10251 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10252 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10253 * @return {Roo.Element} this
10255 hide : function(animate){
10256 this.setVisible(false, this.preanim(arguments, 0));
10261 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10262 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10263 * @return {Roo.Element} this
10265 show : function(animate){
10266 this.setVisible(true, this.preanim(arguments, 0));
10271 * @private Test if size has a unit, otherwise appends the default
10273 addUnits : function(size){
10274 return Roo.Element.addUnits(size, this.defaultUnit);
10278 * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10279 * @return {Roo.Element} this
10281 beginMeasure : function(){
10283 if(el.offsetWidth || el.offsetHeight){
10284 return this; // offsets work already
10287 var p = this.dom, b = document.body; // start with this element
10288 while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10289 var pe = Roo.get(p);
10290 if(pe.getStyle('display') == 'none'){
10291 changed.push({el: p, visibility: pe.getStyle("visibility")});
10292 p.style.visibility = "hidden";
10293 p.style.display = "block";
10297 this._measureChanged = changed;
10303 * Restores displays to before beginMeasure was called
10304 * @return {Roo.Element} this
10306 endMeasure : function(){
10307 var changed = this._measureChanged;
10309 for(var i = 0, len = changed.length; i < len; i++) {
10310 var r = changed[i];
10311 r.el.style.visibility = r.visibility;
10312 r.el.style.display = "none";
10314 this._measureChanged = null;
10320 * Update the innerHTML of this element, optionally searching for and processing scripts
10321 * @param {String} html The new HTML
10322 * @param {Boolean} loadScripts (optional) true to look for and process scripts
10323 * @param {Function} callback For async script loading you can be noticed when the update completes
10324 * @return {Roo.Element} this
10326 update : function(html, loadScripts, callback){
10327 if(typeof html == "undefined"){
10330 if(loadScripts !== true){
10331 this.dom.innerHTML = html;
10332 if(typeof callback == "function"){
10338 var dom = this.dom;
10340 html += '<span id="' + id + '"></span>';
10342 E.onAvailable(id, function(){
10343 var hd = document.getElementsByTagName("head")[0];
10344 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10345 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10346 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10349 while(match = re.exec(html)){
10350 var attrs = match[1];
10351 var srcMatch = attrs ? attrs.match(srcRe) : false;
10352 if(srcMatch && srcMatch[2]){
10353 var s = document.createElement("script");
10354 s.src = srcMatch[2];
10355 var typeMatch = attrs.match(typeRe);
10356 if(typeMatch && typeMatch[2]){
10357 s.type = typeMatch[2];
10360 }else if(match[2] && match[2].length > 0){
10361 if(window.execScript) {
10362 window.execScript(match[2]);
10370 window.eval(match[2]);
10374 var el = document.getElementById(id);
10375 if(el){el.parentNode.removeChild(el);}
10376 if(typeof callback == "function"){
10380 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10385 * Direct access to the UpdateManager update() method (takes the same parameters).
10386 * @param {String/Function} url The url for this request or a function to call to get the url
10387 * @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}
10388 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10389 * @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.
10390 * @return {Roo.Element} this
10393 var um = this.getUpdateManager();
10394 um.update.apply(um, arguments);
10399 * Gets this element's UpdateManager
10400 * @return {Roo.UpdateManager} The UpdateManager
10402 getUpdateManager : function(){
10403 if(!this.updateManager){
10404 this.updateManager = new Roo.UpdateManager(this);
10406 return this.updateManager;
10410 * Disables text selection for this element (normalized across browsers)
10411 * @return {Roo.Element} this
10413 unselectable : function(){
10414 this.dom.unselectable = "on";
10415 this.swallowEvent("selectstart", true);
10416 this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10417 this.addClass("x-unselectable");
10422 * Calculates the x, y to center this element on the screen
10423 * @return {Array} The x, y values [x, y]
10425 getCenterXY : function(){
10426 return this.getAlignToXY(document, 'c-c');
10430 * Centers the Element in either the viewport, or another Element.
10431 * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10433 center : function(centerIn){
10434 this.alignTo(centerIn || document, 'c-c');
10439 * Tests various css rules/browsers to determine if this element uses a border box
10440 * @return {Boolean}
10442 isBorderBox : function(){
10443 return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10447 * Return a box {x, y, width, height} that can be used to set another elements
10448 * size/location to match this element.
10449 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10450 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10451 * @return {Object} box An object in the format {x, y, width, height}
10453 getBox : function(contentBox, local){
10458 var left = parseInt(this.getStyle("left"), 10) || 0;
10459 var top = parseInt(this.getStyle("top"), 10) || 0;
10462 var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10464 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10466 var l = this.getBorderWidth("l")+this.getPadding("l");
10467 var r = this.getBorderWidth("r")+this.getPadding("r");
10468 var t = this.getBorderWidth("t")+this.getPadding("t");
10469 var b = this.getBorderWidth("b")+this.getPadding("b");
10470 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)};
10472 bx.right = bx.x + bx.width;
10473 bx.bottom = bx.y + bx.height;
10478 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10479 for more information about the sides.
10480 * @param {String} sides
10483 getFrameWidth : function(sides, onlyContentBox){
10484 return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10488 * 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.
10489 * @param {Object} box The box to fill {x, y, width, height}
10490 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10491 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10492 * @return {Roo.Element} this
10494 setBox : function(box, adjust, animate){
10495 var w = box.width, h = box.height;
10496 if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10497 w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10498 h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10500 this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10505 * Forces the browser to repaint this element
10506 * @return {Roo.Element} this
10508 repaint : function(){
10509 var dom = this.dom;
10510 this.addClass("x-repaint");
10511 setTimeout(function(){
10512 Roo.get(dom).removeClass("x-repaint");
10518 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10519 * then it returns the calculated width of the sides (see getPadding)
10520 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10521 * @return {Object/Number}
10523 getMargins : function(side){
10526 top: parseInt(this.getStyle("margin-top"), 10) || 0,
10527 left: parseInt(this.getStyle("margin-left"), 10) || 0,
10528 bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10529 right: parseInt(this.getStyle("margin-right"), 10) || 0
10532 return this.addStyles(side, El.margins);
10537 addStyles : function(sides, styles){
10539 for(var i = 0, len = sides.length; i < len; i++){
10540 v = this.getStyle(styles[sides.charAt(i)]);
10542 w = parseInt(v, 10);
10550 * Creates a proxy element of this element
10551 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10552 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10553 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10554 * @return {Roo.Element} The new proxy element
10556 createProxy : function(config, renderTo, matchBox){
10558 renderTo = Roo.getDom(renderTo);
10560 renderTo = document.body;
10562 config = typeof config == "object" ?
10563 config : {tag : "div", cls: config};
10564 var proxy = Roo.DomHelper.append(renderTo, config, true);
10566 proxy.setBox(this.getBox());
10572 * Puts a mask over this element to disable user interaction. Requires core.css.
10573 * This method can only be applied to elements which accept child nodes.
10574 * @param {String} msg (optional) A message to display in the mask
10575 * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10576 * @return {Element} The mask element
10578 mask : function(msg, msgCls)
10580 if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10581 this.setStyle("position", "relative");
10584 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10587 this.addClass("x-masked");
10588 this._mask.setDisplayed(true);
10592 var dom = this.dom;
10593 while (dom && dom.style) {
10594 if (!isNaN(parseInt(dom.style.zIndex))) {
10595 z = Math.max(z, parseInt(dom.style.zIndex));
10597 dom = dom.parentNode;
10599 // if we are masking the body - then it hides everything..
10600 if (this.dom == document.body) {
10602 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10603 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10606 if(typeof msg == 'string'){
10607 if(!this._maskMsg){
10608 this._maskMsg = Roo.DomHelper.append(this.dom, {
10609 cls: "roo-el-mask-msg",
10613 cls: 'fa fa-spinner fa-spin'
10621 var mm = this._maskMsg;
10622 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10623 if (mm.dom.lastChild) { // weird IE issue?
10624 mm.dom.lastChild.innerHTML = msg;
10626 mm.setDisplayed(true);
10628 mm.setStyle('z-index', z + 102);
10630 if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10631 this._mask.setHeight(this.getHeight());
10633 this._mask.setStyle('z-index', z + 100);
10639 * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10640 * it is cached for reuse.
10642 unmask : function(removeEl){
10644 if(removeEl === true){
10645 this._mask.remove();
10648 this._maskMsg.remove();
10649 delete this._maskMsg;
10652 this._mask.setDisplayed(false);
10654 this._maskMsg.setDisplayed(false);
10658 this.removeClass("x-masked");
10662 * Returns true if this element is masked
10663 * @return {Boolean}
10665 isMasked : function(){
10666 return this._mask && this._mask.isVisible();
10670 * Creates an iframe shim for this element to keep selects and other windowed objects from
10672 * @return {Roo.Element} The new shim element
10674 createShim : function(){
10675 var el = document.createElement('iframe');
10676 el.frameBorder = 'no';
10677 el.className = 'roo-shim';
10678 if(Roo.isIE && Roo.isSecure){
10679 el.src = Roo.SSL_SECURE_URL;
10681 var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10682 shim.autoBoxAdjust = false;
10687 * Removes this element from the DOM and deletes it from the cache
10689 remove : function(){
10690 if(this.dom.parentNode){
10691 this.dom.parentNode.removeChild(this.dom);
10693 delete El.cache[this.dom.id];
10697 * Sets up event handlers to add and remove a css class when the mouse is over this element
10698 * @param {String} className
10699 * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10700 * mouseout events for children elements
10701 * @return {Roo.Element} this
10703 addClassOnOver : function(className, preventFlicker){
10704 this.on("mouseover", function(){
10705 Roo.fly(this, '_internal').addClass(className);
10707 var removeFn = function(e){
10708 if(preventFlicker !== true || !e.within(this, true)){
10709 Roo.fly(this, '_internal').removeClass(className);
10712 this.on("mouseout", removeFn, this.dom);
10717 * Sets up event handlers to add and remove a css class when this element has the focus
10718 * @param {String} className
10719 * @return {Roo.Element} this
10721 addClassOnFocus : function(className){
10722 this.on("focus", function(){
10723 Roo.fly(this, '_internal').addClass(className);
10725 this.on("blur", function(){
10726 Roo.fly(this, '_internal').removeClass(className);
10731 * 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)
10732 * @param {String} className
10733 * @return {Roo.Element} this
10735 addClassOnClick : function(className){
10736 var dom = this.dom;
10737 this.on("mousedown", function(){
10738 Roo.fly(dom, '_internal').addClass(className);
10739 var d = Roo.get(document);
10740 var fn = function(){
10741 Roo.fly(dom, '_internal').removeClass(className);
10742 d.removeListener("mouseup", fn);
10744 d.on("mouseup", fn);
10750 * Stops the specified event from bubbling and optionally prevents the default action
10751 * @param {String} eventName
10752 * @param {Boolean} preventDefault (optional) true to prevent the default action too
10753 * @return {Roo.Element} this
10755 swallowEvent : function(eventName, preventDefault){
10756 var fn = function(e){
10757 e.stopPropagation();
10758 if(preventDefault){
10759 e.preventDefault();
10762 if(eventName instanceof Array){
10763 for(var i = 0, len = eventName.length; i < len; i++){
10764 this.on(eventName[i], fn);
10768 this.on(eventName, fn);
10775 fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10778 * Sizes this element to its parent element's dimensions performing
10779 * neccessary box adjustments.
10780 * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10781 * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10782 * @return {Roo.Element} this
10784 fitToParent : function(monitorResize, targetParent) {
10785 Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10786 this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10787 if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10790 var p = Roo.get(targetParent || this.dom.parentNode);
10791 this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10792 if (monitorResize === true) {
10793 this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10794 Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10800 * Gets the next sibling, skipping text nodes
10801 * @return {HTMLElement} The next sibling or null
10803 getNextSibling : function(){
10804 var n = this.dom.nextSibling;
10805 while(n && n.nodeType != 1){
10812 * Gets the previous sibling, skipping text nodes
10813 * @return {HTMLElement} The previous sibling or null
10815 getPrevSibling : function(){
10816 var n = this.dom.previousSibling;
10817 while(n && n.nodeType != 1){
10818 n = n.previousSibling;
10825 * Appends the passed element(s) to this element
10826 * @param {String/HTMLElement/Array/Element/CompositeElement} el
10827 * @return {Roo.Element} this
10829 appendChild: function(el){
10836 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10837 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
10838 * automatically generated with the specified attributes.
10839 * @param {HTMLElement} insertBefore (optional) a child element of this element
10840 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10841 * @return {Roo.Element} The new child element
10843 createChild: function(config, insertBefore, returnDom){
10844 config = config || {tag:'div'};
10846 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10848 return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
10852 * Appends this element to the passed element
10853 * @param {String/HTMLElement/Element} el The new parent element
10854 * @return {Roo.Element} this
10856 appendTo: function(el){
10857 el = Roo.getDom(el);
10858 el.appendChild(this.dom);
10863 * Inserts this element before the passed element in the DOM
10864 * @param {String/HTMLElement/Element} el The element to insert before
10865 * @return {Roo.Element} this
10867 insertBefore: function(el){
10868 el = Roo.getDom(el);
10869 el.parentNode.insertBefore(this.dom, el);
10874 * Inserts this element after the passed element in the DOM
10875 * @param {String/HTMLElement/Element} el The element to insert after
10876 * @return {Roo.Element} this
10878 insertAfter: function(el){
10879 el = Roo.getDom(el);
10880 el.parentNode.insertBefore(this.dom, el.nextSibling);
10885 * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10886 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10887 * @return {Roo.Element} The new child
10889 insertFirst: function(el, returnDom){
10891 if(typeof el == 'object' && !el.nodeType){ // dh config
10892 return this.createChild(el, this.dom.firstChild, returnDom);
10894 el = Roo.getDom(el);
10895 this.dom.insertBefore(el, this.dom.firstChild);
10896 return !returnDom ? Roo.get(el) : el;
10901 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10902 * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10903 * @param {String} where (optional) 'before' or 'after' defaults to before
10904 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10905 * @return {Roo.Element} the inserted Element
10907 insertSibling: function(el, where, returnDom){
10908 where = where ? where.toLowerCase() : 'before';
10910 var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10912 if(typeof el == 'object' && !el.nodeType){ // dh config
10913 if(where == 'after' && !this.dom.nextSibling){
10914 rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10916 rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10920 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10921 where == 'before' ? this.dom : this.dom.nextSibling);
10930 * Creates and wraps this element with another element
10931 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10932 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10933 * @return {HTMLElement/Element} The newly created wrapper element
10935 wrap: function(config, returnDom){
10937 config = {tag: "div"};
10939 var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10940 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10945 * Replaces the passed element with this element
10946 * @param {String/HTMLElement/Element} el The element to replace
10947 * @return {Roo.Element} this
10949 replace: function(el){
10951 this.insertBefore(el);
10957 * Inserts an html fragment into this element
10958 * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10959 * @param {String} html The HTML fragment
10960 * @param {Boolean} returnEl True to return an Roo.Element
10961 * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10963 insertHtml : function(where, html, returnEl){
10964 var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10965 return returnEl ? Roo.get(el) : el;
10969 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10970 * @param {Object} o The object with the attributes
10971 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10972 * @return {Roo.Element} this
10974 set : function(o, useSet){
10976 useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10977 for(var attr in o){
10978 if(attr == "style" || typeof o[attr] == "function") { continue; }
10980 el.className = o["cls"];
10983 el.setAttribute(attr, o[attr]);
10985 el[attr] = o[attr];
10990 Roo.DomHelper.applyStyles(el, o.style);
10996 * Convenience method for constructing a KeyMap
10997 * @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:
10998 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10999 * @param {Function} fn The function to call
11000 * @param {Object} scope (optional) The scope of the function
11001 * @return {Roo.KeyMap} The KeyMap created
11003 addKeyListener : function(key, fn, scope){
11005 if(typeof key != "object" || key instanceof Array){
11021 return new Roo.KeyMap(this, config);
11025 * Creates a KeyMap for this element
11026 * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11027 * @return {Roo.KeyMap} The KeyMap created
11029 addKeyMap : function(config){
11030 return new Roo.KeyMap(this, config);
11034 * Returns true if this element is scrollable.
11035 * @return {Boolean}
11037 isScrollable : function(){
11038 var dom = this.dom;
11039 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11043 * 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().
11044 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11045 * @param {Number} value The new scroll value
11046 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11047 * @return {Element} this
11050 scrollTo : function(side, value, animate){
11051 var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11052 if(!animate || !A){
11053 this.dom[prop] = value;
11055 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11056 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11062 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11063 * within this element's scrollable range.
11064 * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11065 * @param {Number} distance How far to scroll the element in pixels
11066 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11067 * @return {Boolean} Returns true if a scroll was triggered or false if the element
11068 * was scrolled as far as it could go.
11070 scroll : function(direction, distance, animate){
11071 if(!this.isScrollable()){
11075 var l = el.scrollLeft, t = el.scrollTop;
11076 var w = el.scrollWidth, h = el.scrollHeight;
11077 var cw = el.clientWidth, ch = el.clientHeight;
11078 direction = direction.toLowerCase();
11079 var scrolled = false;
11080 var a = this.preanim(arguments, 2);
11085 var v = Math.min(l + distance, w-cw);
11086 this.scrollTo("left", v, a);
11093 var v = Math.max(l - distance, 0);
11094 this.scrollTo("left", v, a);
11102 var v = Math.max(t - distance, 0);
11103 this.scrollTo("top", v, a);
11111 var v = Math.min(t + distance, h-ch);
11112 this.scrollTo("top", v, a);
11121 * Translates the passed page coordinates into left/top css values for this element
11122 * @param {Number/Array} x The page x or an array containing [x, y]
11123 * @param {Number} y The page y
11124 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11126 translatePoints : function(x, y){
11127 if(typeof x == 'object' || x instanceof Array){
11128 y = x[1]; x = x[0];
11130 var p = this.getStyle('position');
11131 var o = this.getXY();
11133 var l = parseInt(this.getStyle('left'), 10);
11134 var t = parseInt(this.getStyle('top'), 10);
11137 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11140 t = (p == "relative") ? 0 : this.dom.offsetTop;
11143 return {left: (x - o[0] + l), top: (y - o[1] + t)};
11147 * Returns the current scroll position of the element.
11148 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11150 getScroll : function(){
11151 var d = this.dom, doc = document;
11152 if(d == doc || d == doc.body){
11153 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11154 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11155 return {left: l, top: t};
11157 return {left: d.scrollLeft, top: d.scrollTop};
11162 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11163 * are convert to standard 6 digit hex color.
11164 * @param {String} attr The css attribute
11165 * @param {String} defaultValue The default value to use when a valid color isn't found
11166 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11169 getColor : function(attr, defaultValue, prefix){
11170 var v = this.getStyle(attr);
11171 if(!v || v == "transparent" || v == "inherit") {
11172 return defaultValue;
11174 var color = typeof prefix == "undefined" ? "#" : prefix;
11175 if(v.substr(0, 4) == "rgb("){
11176 var rvs = v.slice(4, v.length -1).split(",");
11177 for(var i = 0; i < 3; i++){
11178 var h = parseInt(rvs[i]).toString(16);
11185 if(v.substr(0, 1) == "#"){
11186 if(v.length == 4) {
11187 for(var i = 1; i < 4; i++){
11188 var c = v.charAt(i);
11191 }else if(v.length == 7){
11192 color += v.substr(1);
11196 return(color.length > 5 ? color.toLowerCase() : defaultValue);
11200 * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11201 * gradient background, rounded corners and a 4-way shadow.
11202 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11203 * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11204 * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11205 * @return {Roo.Element} this
11207 boxWrap : function(cls){
11208 cls = cls || 'x-box';
11209 var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11210 el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11215 * Returns the value of a namespaced attribute from the element's underlying DOM node.
11216 * @param {String} namespace The namespace in which to look for the attribute
11217 * @param {String} name The attribute name
11218 * @return {String} The attribute value
11220 getAttributeNS : Roo.isIE ? function(ns, name){
11222 var type = typeof d[ns+":"+name];
11223 if(type != 'undefined' && type != 'unknown'){
11224 return d[ns+":"+name];
11227 } : function(ns, name){
11229 return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11234 * Sets or Returns the value the dom attribute value
11235 * @param {String|Object} name The attribute name (or object to set multiple attributes)
11236 * @param {String} value (optional) The value to set the attribute to
11237 * @return {String} The attribute value
11239 attr : function(name){
11240 if (arguments.length > 1) {
11241 this.dom.setAttribute(name, arguments[1]);
11242 return arguments[1];
11244 if (typeof(name) == 'object') {
11245 for(var i in name) {
11246 this.attr(i, name[i]);
11252 if (!this.dom.hasAttribute(name)) {
11255 return this.dom.getAttribute(name);
11262 var ep = El.prototype;
11265 * Appends an event handler (Shorthand for addListener)
11266 * @param {String} eventName The type of event to append
11267 * @param {Function} fn The method the event invokes
11268 * @param {Object} scope (optional) The scope (this object) of the fn
11269 * @param {Object} options (optional)An object with standard {@link Roo.EventManager#addListener} options
11272 ep.on = ep.addListener;
11273 // backwards compat
11274 ep.mon = ep.addListener;
11277 * Removes an event handler from this element (shorthand for removeListener)
11278 * @param {String} eventName the type of event to remove
11279 * @param {Function} fn the method the event invokes
11280 * @return {Roo.Element} this
11283 ep.un = ep.removeListener;
11286 * true to automatically adjust width and height settings for box-model issues (default to true)
11288 ep.autoBoxAdjust = true;
11291 El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11294 El.addUnits = function(v, defaultUnit){
11295 if(v === "" || v == "auto"){
11298 if(v === undefined){
11301 if(typeof v == "number" || !El.unitPattern.test(v)){
11302 return v + (defaultUnit || 'px');
11307 // special markup used throughout Roo when box wrapping elements
11308 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>';
11310 * Visibility mode constant - Use visibility to hide element
11316 * Visibility mode constant - Use display to hide element
11322 El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11323 El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11324 El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11336 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11337 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11338 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11339 * @return {Element} The Element object
11342 El.get = function(el){
11344 if(!el){ return null; }
11345 if(typeof el == "string"){ // element id
11346 if(!(elm = document.getElementById(el))){
11349 if(ex = El.cache[el]){
11352 ex = El.cache[el] = new El(elm);
11355 }else if(el.tagName){ // dom element
11359 if(ex = El.cache[id]){
11362 ex = El.cache[id] = new El(el);
11365 }else if(el instanceof El){
11367 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11368 // catch case where it hasn't been appended
11369 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11372 }else if(el.isComposite){
11374 }else if(el instanceof Array){
11375 return El.select(el);
11376 }else if(el == document){
11377 // create a bogus element object representing the document object
11379 var f = function(){};
11380 f.prototype = El.prototype;
11382 docEl.dom = document;
11390 El.uncache = function(el){
11391 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11393 delete El.cache[a[i].id || a[i]];
11399 // Garbage collection - uncache elements/purge listeners on orphaned elements
11400 // so we don't hold a reference and cause the browser to retain them
11401 El.garbageCollect = function(){
11402 if(!Roo.enableGarbageCollector){
11403 clearInterval(El.collectorThread);
11406 for(var eid in El.cache){
11407 var el = El.cache[eid], d = el.dom;
11408 // -------------------------------------------------------
11409 // Determining what is garbage:
11410 // -------------------------------------------------------
11412 // dom node is null, definitely garbage
11413 // -------------------------------------------------------
11415 // no parentNode == direct orphan, definitely garbage
11416 // -------------------------------------------------------
11417 // !d.offsetParent && !document.getElementById(eid)
11418 // display none elements have no offsetParent so we will
11419 // also try to look it up by it's id. However, check
11420 // offsetParent first so we don't do unneeded lookups.
11421 // This enables collection of elements that are not orphans
11422 // directly, but somewhere up the line they have an orphan
11424 // -------------------------------------------------------
11425 if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11426 delete El.cache[eid];
11427 if(d && Roo.enableListenerCollection){
11433 El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11437 El.Flyweight = function(dom){
11440 El.Flyweight.prototype = El.prototype;
11442 El._flyweights = {};
11444 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11445 * the dom node can be overwritten by other code.
11446 * @param {String/HTMLElement} el The dom node or id
11447 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11448 * prevent conflicts (e.g. internally Roo uses "_internal")
11450 * @return {Element} The shared Element object
11452 El.fly = function(el, named){
11453 named = named || '_global';
11454 el = Roo.getDom(el);
11458 if(!El._flyweights[named]){
11459 El._flyweights[named] = new El.Flyweight();
11461 El._flyweights[named].dom = el;
11462 return El._flyweights[named];
11466 * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11467 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11468 * Shorthand of {@link Roo.Element#get}
11469 * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11470 * @return {Element} The Element object
11476 * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11477 * the dom node can be overwritten by other code.
11478 * Shorthand of {@link Roo.Element#fly}
11479 * @param {String/HTMLElement} el The dom node or id
11480 * @param {String} named (optional) Allows for creation of named reusable flyweights to
11481 * prevent conflicts (e.g. internally Roo uses "_internal")
11483 * @return {Element} The shared Element object
11489 // speedy lookup for elements never to box adjust
11490 var noBoxAdjust = Roo.isStrict ? {
11493 input:1, select:1, textarea:1
11495 if(Roo.isIE || Roo.isGecko){
11496 noBoxAdjust['button'] = 1;
11500 Roo.EventManager.on(window, 'unload', function(){
11502 delete El._flyweights;
11510 Roo.Element.selectorFunction = Roo.DomQuery.select;
11513 Roo.Element.select = function(selector, unique, root){
11515 if(typeof selector == "string"){
11516 els = Roo.Element.selectorFunction(selector, root);
11517 }else if(selector.length !== undefined){
11520 throw "Invalid selector";
11522 if(unique === true){
11523 return new Roo.CompositeElement(els);
11525 return new Roo.CompositeElementLite(els);
11529 * Selects elements based on the passed CSS selector to enable working on them as 1.
11530 * @param {String/Array} selector The CSS selector or an array of elements
11531 * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11532 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11533 * @return {CompositeElementLite/CompositeElement}
11537 Roo.select = Roo.Element.select;
11554 * Ext JS Library 1.1.1
11555 * Copyright(c) 2006-2007, Ext JS, LLC.
11557 * Originally Released Under LGPL - original licence link has changed is not relivant.
11560 * <script type="text/javascript">
11565 //Notifies Element that fx methods are available
11566 Roo.enableFx = true;
11570 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
11571 * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11572 * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the
11573 * Element effects to work.</p><br/>
11575 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11576 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11577 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11578 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
11579 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11580 * expected results and should be done with care.</p><br/>
11582 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11583 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
11586 ----- -----------------------------
11587 tl The top left corner
11588 t The center of the top edge
11589 tr The top right corner
11590 l The center of the left edge
11591 r The center of the right edge
11592 bl The bottom left corner
11593 b The center of the bottom edge
11594 br The bottom right corner
11596 * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11597 * below are common options that can be passed to any Fx method.</b>
11598 * @cfg {Function} callback A function called when the effect is finished
11599 * @cfg {Object} scope The scope of the effect function
11600 * @cfg {String} easing A valid Easing value for the effect
11601 * @cfg {String} afterCls A css class to apply after the effect
11602 * @cfg {Number} duration The length of time (in seconds) that the effect should last
11603 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11604 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
11605 * effects that end with the element being visually hidden, ignored otherwise)
11606 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11607 * a function which returns such a specification that will be applied to the Element after the effect finishes
11608 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11609 * @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
11610 * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11614 * Slides the element into view. An anchor point can be optionally passed to set the point of
11615 * origin for the slide effect. This function automatically handles wrapping the element with
11616 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11619 // default: slide the element in from the top
11622 // custom: slide the element in from the right with a 2-second duration
11623 el.slideIn('r', { duration: 2 });
11625 // common config options shown with default values
11631 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11632 * @param {Object} options (optional) Object literal with any of the Fx config options
11633 * @return {Roo.Element} The Element
11635 slideIn : function(anchor, o){
11636 var el = this.getFxEl();
11639 el.queueFx(o, function(){
11641 anchor = anchor || "t";
11643 // fix display to visibility
11646 // restore values after effect
11647 var r = this.getFxRestore();
11648 var b = this.getBox();
11649 // fixed size for slide
11653 var wrap = this.fxWrap(r.pos, o, "hidden");
11655 var st = this.dom.style;
11656 st.visibility = "visible";
11657 st.position = "absolute";
11659 // clear out temp styles after slide and unwrap
11660 var after = function(){
11661 el.fxUnwrap(wrap, r.pos, o);
11662 st.width = r.width;
11663 st.height = r.height;
11666 // time to calc the positions
11667 var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11669 switch(anchor.toLowerCase()){
11671 wrap.setSize(b.width, 0);
11672 st.left = st.bottom = "0";
11676 wrap.setSize(0, b.height);
11677 st.right = st.top = "0";
11681 wrap.setSize(0, b.height);
11682 wrap.setX(b.right);
11683 st.left = st.top = "0";
11684 a = {width: bw, points: pt};
11687 wrap.setSize(b.width, 0);
11688 wrap.setY(b.bottom);
11689 st.left = st.top = "0";
11690 a = {height: bh, points: pt};
11693 wrap.setSize(0, 0);
11694 st.right = st.bottom = "0";
11695 a = {width: bw, height: bh};
11698 wrap.setSize(0, 0);
11699 wrap.setY(b.y+b.height);
11700 st.right = st.top = "0";
11701 a = {width: bw, height: bh, points: pt};
11704 wrap.setSize(0, 0);
11705 wrap.setXY([b.right, b.bottom]);
11706 st.left = st.top = "0";
11707 a = {width: bw, height: bh, points: pt};
11710 wrap.setSize(0, 0);
11711 wrap.setX(b.x+b.width);
11712 st.left = st.bottom = "0";
11713 a = {width: bw, height: bh, points: pt};
11716 this.dom.style.visibility = "visible";
11719 arguments.callee.anim = wrap.fxanim(a,
11729 * Slides the element out of view. An anchor point can be optionally passed to set the end point
11730 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
11731 * 'hidden') but block elements will still take up space in the document. The element must be removed
11732 * from the DOM using the 'remove' config option if desired. This function automatically handles
11733 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
11736 // default: slide the element out to the top
11739 // custom: slide the element out to the right with a 2-second duration
11740 el.slideOut('r', { duration: 2 });
11742 // common config options shown with default values
11750 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11751 * @param {Object} options (optional) Object literal with any of the Fx config options
11752 * @return {Roo.Element} The Element
11754 slideOut : function(anchor, o){
11755 var el = this.getFxEl();
11758 el.queueFx(o, function(){
11760 anchor = anchor || "t";
11762 // restore values after effect
11763 var r = this.getFxRestore();
11765 var b = this.getBox();
11766 // fixed size for slide
11770 var wrap = this.fxWrap(r.pos, o, "visible");
11772 var st = this.dom.style;
11773 st.visibility = "visible";
11774 st.position = "absolute";
11778 var after = function(){
11780 el.setDisplayed(false);
11785 el.fxUnwrap(wrap, r.pos, o);
11787 st.width = r.width;
11788 st.height = r.height;
11793 var a, zero = {to: 0};
11794 switch(anchor.toLowerCase()){
11796 st.left = st.bottom = "0";
11797 a = {height: zero};
11800 st.right = st.top = "0";
11804 st.left = st.top = "0";
11805 a = {width: zero, points: {to:[b.right, b.y]}};
11808 st.left = st.top = "0";
11809 a = {height: zero, points: {to:[b.x, b.bottom]}};
11812 st.right = st.bottom = "0";
11813 a = {width: zero, height: zero};
11816 st.right = st.top = "0";
11817 a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11820 st.left = st.top = "0";
11821 a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11824 st.left = st.bottom = "0";
11825 a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11829 arguments.callee.anim = wrap.fxanim(a,
11839 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
11840 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
11841 * The element must be removed from the DOM using the 'remove' config option if desired.
11847 // common config options shown with default values
11855 * @param {Object} options (optional) Object literal with any of the Fx config options
11856 * @return {Roo.Element} The Element
11858 puff : function(o){
11859 var el = this.getFxEl();
11862 el.queueFx(o, function(){
11863 this.clearOpacity();
11866 // restore values after effect
11867 var r = this.getFxRestore();
11868 var st = this.dom.style;
11870 var after = function(){
11872 el.setDisplayed(false);
11879 el.setPositioning(r.pos);
11880 st.width = r.width;
11881 st.height = r.height;
11886 var width = this.getWidth();
11887 var height = this.getHeight();
11889 arguments.callee.anim = this.fxanim({
11890 width : {to: this.adjustWidth(width * 2)},
11891 height : {to: this.adjustHeight(height * 2)},
11892 points : {by: [-(width * .5), -(height * .5)]},
11894 fontSize: {to:200, unit: "%"}
11905 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11906 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
11907 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11913 // all config options shown with default values
11921 * @param {Object} options (optional) Object literal with any of the Fx config options
11922 * @return {Roo.Element} The Element
11924 switchOff : function(o){
11925 var el = this.getFxEl();
11928 el.queueFx(o, function(){
11929 this.clearOpacity();
11932 // restore values after effect
11933 var r = this.getFxRestore();
11934 var st = this.dom.style;
11936 var after = function(){
11938 el.setDisplayed(false);
11944 el.setPositioning(r.pos);
11945 st.width = r.width;
11946 st.height = r.height;
11951 this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11952 this.clearOpacity();
11956 points:{by:[0, this.getHeight() * .5]}
11957 }, o, 'motion', 0.3, 'easeIn', after);
11958 }).defer(100, this);
11965 * Highlights the Element by setting a color (applies to the background-color by default, but can be
11966 * changed using the "attr" config option) and then fading back to the original color. If no original
11967 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11970 // default: highlight background to yellow
11973 // custom: highlight foreground text to blue for 2 seconds
11974 el.highlight("0000ff", { attr: 'color', duration: 2 });
11976 // common config options shown with default values
11977 el.highlight("ffff9c", {
11978 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11979 endColor: (current color) or "ffffff",
11984 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11985 * @param {Object} options (optional) Object literal with any of the Fx config options
11986 * @return {Roo.Element} The Element
11988 highlight : function(color, o){
11989 var el = this.getFxEl();
11992 el.queueFx(o, function(){
11993 color = color || "ffff9c";
11994 attr = o.attr || "backgroundColor";
11996 this.clearOpacity();
11999 var origColor = this.getColor(attr);
12000 var restoreColor = this.dom.style[attr];
12001 endColor = (o.endColor || origColor) || "ffffff";
12003 var after = function(){
12004 el.dom.style[attr] = restoreColor;
12009 a[attr] = {from: color, to: endColor};
12010 arguments.callee.anim = this.fxanim(a,
12020 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12023 // default: a single light blue ripple
12026 // custom: 3 red ripples lasting 3 seconds total
12027 el.frame("ff0000", 3, { duration: 3 });
12029 // common config options shown with default values
12030 el.frame("C3DAF9", 1, {
12031 duration: 1 //duration of entire animation (not each individual ripple)
12032 // Note: Easing is not configurable and will be ignored if included
12035 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12036 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12037 * @param {Object} options (optional) Object literal with any of the Fx config options
12038 * @return {Roo.Element} The Element
12040 frame : function(color, count, o){
12041 var el = this.getFxEl();
12044 el.queueFx(o, function(){
12045 color = color || "#C3DAF9";
12046 if(color.length == 6){
12047 color = "#" + color;
12049 count = count || 1;
12050 duration = o.duration || 1;
12053 var b = this.getBox();
12054 var animFn = function(){
12055 var proxy = this.createProxy({
12058 visbility:"hidden",
12059 position:"absolute",
12060 "z-index":"35000", // yee haw
12061 border:"0px solid " + color
12064 var scale = Roo.isBorderBox ? 2 : 1;
12066 top:{from:b.y, to:b.y - 20},
12067 left:{from:b.x, to:b.x - 20},
12068 borderWidth:{from:0, to:10},
12069 opacity:{from:1, to:0},
12070 height:{from:b.height, to:(b.height + (20*scale))},
12071 width:{from:b.width, to:(b.width + (20*scale))}
12072 }, duration, function(){
12076 animFn.defer((duration/2)*1000, this);
12087 * Creates a pause before any subsequent queued effects begin. If there are
12088 * no effects queued after the pause it will have no effect.
12093 * @param {Number} seconds The length of time to pause (in seconds)
12094 * @return {Roo.Element} The Element
12096 pause : function(seconds){
12097 var el = this.getFxEl();
12100 el.queueFx(o, function(){
12101 setTimeout(function(){
12103 }, seconds * 1000);
12109 * Fade an element in (from transparent to opaque). The ending opacity can be specified
12110 * using the "endOpacity" config option.
12113 // default: fade in from opacity 0 to 100%
12116 // custom: fade in from opacity 0 to 75% over 2 seconds
12117 el.fadeIn({ endOpacity: .75, duration: 2});
12119 // common config options shown with default values
12121 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12126 * @param {Object} options (optional) Object literal with any of the Fx config options
12127 * @return {Roo.Element} The Element
12129 fadeIn : function(o){
12130 var el = this.getFxEl();
12132 el.queueFx(o, function(){
12133 this.setOpacity(0);
12135 this.dom.style.visibility = 'visible';
12136 var to = o.endOpacity || 1;
12137 arguments.callee.anim = this.fxanim({opacity:{to:to}},
12138 o, null, .5, "easeOut", function(){
12140 this.clearOpacity();
12149 * Fade an element out (from opaque to transparent). The ending opacity can be specified
12150 * using the "endOpacity" config option.
12153 // default: fade out from the element's current opacity to 0
12156 // custom: fade out from the element's current opacity to 25% over 2 seconds
12157 el.fadeOut({ endOpacity: .25, duration: 2});
12159 // common config options shown with default values
12161 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12168 * @param {Object} options (optional) Object literal with any of the Fx config options
12169 * @return {Roo.Element} The Element
12171 fadeOut : function(o){
12172 var el = this.getFxEl();
12174 el.queueFx(o, function(){
12175 arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12176 o, null, .5, "easeOut", function(){
12177 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12178 this.dom.style.display = "none";
12180 this.dom.style.visibility = "hidden";
12182 this.clearOpacity();
12190 * Animates the transition of an element's dimensions from a starting height/width
12191 * to an ending height/width.
12194 // change height and width to 100x100 pixels
12195 el.scale(100, 100);
12197 // common config options shown with default values. The height and width will default to
12198 // the element's existing values if passed as null.
12201 [element's height], {
12206 * @param {Number} width The new width (pass undefined to keep the original width)
12207 * @param {Number} height The new height (pass undefined to keep the original height)
12208 * @param {Object} options (optional) Object literal with any of the Fx config options
12209 * @return {Roo.Element} The Element
12211 scale : function(w, h, o){
12212 this.shift(Roo.apply({}, o, {
12220 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12221 * Any of these properties not specified in the config object will not be changed. This effect
12222 * requires that at least one new dimension, position or opacity setting must be passed in on
12223 * the config object in order for the function to have any effect.
12226 // slide the element horizontally to x position 200 while changing the height and opacity
12227 el.shift({ x: 200, height: 50, opacity: .8 });
12229 // common config options shown with default values.
12231 width: [element's width],
12232 height: [element's height],
12233 x: [element's x position],
12234 y: [element's y position],
12235 opacity: [element's opacity],
12240 * @param {Object} options Object literal with any of the Fx config options
12241 * @return {Roo.Element} The Element
12243 shift : function(o){
12244 var el = this.getFxEl();
12246 el.queueFx(o, function(){
12247 var a = {}, w = o.width, h = o.height, x = o.x, y = o.y, op = o.opacity;
12248 if(w !== undefined){
12249 a.width = {to: this.adjustWidth(w)};
12251 if(h !== undefined){
12252 a.height = {to: this.adjustHeight(h)};
12254 if(x !== undefined || y !== undefined){
12256 x !== undefined ? x : this.getX(),
12257 y !== undefined ? y : this.getY()
12260 if(op !== undefined){
12261 a.opacity = {to: op};
12263 if(o.xy !== undefined){
12264 a.points = {to: o.xy};
12266 arguments.callee.anim = this.fxanim(a,
12267 o, 'motion', .35, "easeOut", function(){
12275 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
12276 * ending point of the effect.
12279 // default: slide the element downward while fading out
12282 // custom: slide the element out to the right with a 2-second duration
12283 el.ghost('r', { duration: 2 });
12285 // common config options shown with default values
12293 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12294 * @param {Object} options (optional) Object literal with any of the Fx config options
12295 * @return {Roo.Element} The Element
12297 ghost : function(anchor, o){
12298 var el = this.getFxEl();
12301 el.queueFx(o, function(){
12302 anchor = anchor || "b";
12304 // restore values after effect
12305 var r = this.getFxRestore();
12306 var w = this.getWidth(),
12307 h = this.getHeight();
12309 var st = this.dom.style;
12311 var after = function(){
12313 el.setDisplayed(false);
12319 el.setPositioning(r.pos);
12320 st.width = r.width;
12321 st.height = r.height;
12326 var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12327 switch(anchor.toLowerCase()){
12354 arguments.callee.anim = this.fxanim(a,
12364 * Ensures that all effects queued after syncFx is called on the element are
12365 * run concurrently. This is the opposite of {@link #sequenceFx}.
12366 * @return {Roo.Element} The Element
12368 syncFx : function(){
12369 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12378 * Ensures that all effects queued after sequenceFx is called on the element are
12379 * run in sequence. This is the opposite of {@link #syncFx}.
12380 * @return {Roo.Element} The Element
12382 sequenceFx : function(){
12383 this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12385 concurrent : false,
12392 nextFx : function(){
12393 var ef = this.fxQueue[0];
12400 * Returns true if the element has any effects actively running or queued, else returns false.
12401 * @return {Boolean} True if element has active effects, else false
12403 hasActiveFx : function(){
12404 return this.fxQueue && this.fxQueue[0];
12408 * Stops any running effects and clears the element's internal effects queue if it contains
12409 * any additional effects that haven't started yet.
12410 * @return {Roo.Element} The Element
12412 stopFx : function(){
12413 if(this.hasActiveFx()){
12414 var cur = this.fxQueue[0];
12415 if(cur && cur.anim && cur.anim.isAnimated()){
12416 this.fxQueue = [cur]; // clear out others
12417 cur.anim.stop(true);
12424 beforeFx : function(o){
12425 if(this.hasActiveFx() && !o.concurrent){
12436 * Returns true if the element is currently blocking so that no other effect can be queued
12437 * until this effect is finished, else returns false if blocking is not set. This is commonly
12438 * used to ensure that an effect initiated by a user action runs to completion prior to the
12439 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12440 * @return {Boolean} True if blocking, else false
12442 hasFxBlock : function(){
12443 var q = this.fxQueue;
12444 return q && q[0] && q[0].block;
12448 queueFx : function(o, fn){
12452 if(!this.hasFxBlock()){
12453 Roo.applyIf(o, this.fxDefaults);
12455 var run = this.beforeFx(o);
12456 fn.block = o.block;
12457 this.fxQueue.push(fn);
12469 fxWrap : function(pos, o, vis){
12471 if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12474 wrapXY = this.getXY();
12476 var div = document.createElement("div");
12477 div.style.visibility = vis;
12478 wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12479 wrap.setPositioning(pos);
12480 if(wrap.getStyle("position") == "static"){
12481 wrap.position("relative");
12483 this.clearPositioning('auto');
12485 wrap.dom.appendChild(this.dom);
12487 wrap.setXY(wrapXY);
12494 fxUnwrap : function(wrap, pos, o){
12495 this.clearPositioning();
12496 this.setPositioning(pos);
12498 wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12504 getFxRestore : function(){
12505 var st = this.dom.style;
12506 return {pos: this.getPositioning(), width: st.width, height : st.height};
12510 afterFx : function(o){
12512 this.applyStyles(o.afterStyle);
12515 this.addClass(o.afterCls);
12517 if(o.remove === true){
12520 Roo.callback(o.callback, o.scope, [this]);
12522 this.fxQueue.shift();
12528 getFxEl : function(){ // support for composite element fx
12529 return Roo.get(this.dom);
12533 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12534 animType = animType || 'run';
12536 var anim = Roo.lib.Anim[animType](
12538 (opt.duration || defaultDur) || .35,
12539 (opt.easing || defaultEase) || 'easeOut',
12541 Roo.callback(cb, this);
12550 // backwords compat
12551 Roo.Fx.resize = Roo.Fx.scale;
12553 //When included, Roo.Fx is automatically applied to Element so that all basic
12554 //effects are available directly via the Element API
12555 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12557 * Ext JS Library 1.1.1
12558 * Copyright(c) 2006-2007, Ext JS, LLC.
12560 * Originally Released Under LGPL - original licence link has changed is not relivant.
12563 * <script type="text/javascript">
12568 * @class Roo.CompositeElement
12569 * Standard composite class. Creates a Roo.Element for every element in the collection.
12571 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12572 * actions will be performed on all the elements in this collection.</b>
12574 * All methods return <i>this</i> and can be chained.
12576 var els = Roo.select("#some-el div.some-class", true);
12577 // or select directly from an existing element
12578 var el = Roo.get('some-el');
12579 el.select('div.some-class', true);
12581 els.setWidth(100); // all elements become 100 width
12582 els.hide(true); // all elements fade out and hide
12584 els.setWidth(100).hide(true);
12587 Roo.CompositeElement = function(els){
12588 this.elements = [];
12589 this.addElements(els);
12591 Roo.CompositeElement.prototype = {
12593 addElements : function(els){
12597 if(typeof els == "string"){
12598 els = Roo.Element.selectorFunction(els);
12600 var yels = this.elements;
12601 var index = yels.length-1;
12602 for(var i = 0, len = els.length; i < len; i++) {
12603 yels[++index] = Roo.get(els[i]);
12609 * Clears this composite and adds the elements returned by the passed selector.
12610 * @param {String/Array} els A string CSS selector, an array of elements or an element
12611 * @return {CompositeElement} this
12613 fill : function(els){
12614 this.elements = [];
12620 * Filters this composite to only elements that match the passed selector.
12621 * @param {String} selector A string CSS selector
12622 * @param {Boolean} inverse return inverse filter (not matches)
12623 * @return {CompositeElement} this
12625 filter : function(selector, inverse){
12627 inverse = inverse || false;
12628 this.each(function(el){
12629 var match = inverse ? !el.is(selector) : el.is(selector);
12631 els[els.length] = el.dom;
12638 invoke : function(fn, args){
12639 var els = this.elements;
12640 for(var i = 0, len = els.length; i < len; i++) {
12641 Roo.Element.prototype[fn].apply(els[i], args);
12646 * Adds elements to this composite.
12647 * @param {String/Array} els A string CSS selector, an array of elements or an element
12648 * @return {CompositeElement} this
12650 add : function(els){
12651 if(typeof els == "string"){
12652 this.addElements(Roo.Element.selectorFunction(els));
12653 }else if(els.length !== undefined){
12654 this.addElements(els);
12656 this.addElements([els]);
12661 * Calls the passed function passing (el, this, index) for each element in this composite.
12662 * @param {Function} fn The function to call
12663 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12664 * @return {CompositeElement} this
12666 each : function(fn, scope){
12667 var els = this.elements;
12668 for(var i = 0, len = els.length; i < len; i++){
12669 if(fn.call(scope || els[i], els[i], this, i) === false) {
12677 * Returns the Element object at the specified index
12678 * @param {Number} index
12679 * @return {Roo.Element}
12681 item : function(index){
12682 return this.elements[index] || null;
12686 * Returns the first Element
12687 * @return {Roo.Element}
12689 first : function(){
12690 return this.item(0);
12694 * Returns the last Element
12695 * @return {Roo.Element}
12698 return this.item(this.elements.length-1);
12702 * Returns the number of elements in this composite
12705 getCount : function(){
12706 return this.elements.length;
12710 * Returns true if this composite contains the passed element
12713 contains : function(el){
12714 return this.indexOf(el) !== -1;
12718 * Returns true if this composite contains the passed element
12721 indexOf : function(el){
12722 return this.elements.indexOf(Roo.get(el));
12727 * Removes the specified element(s).
12728 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12729 * or an array of any of those.
12730 * @param {Boolean} removeDom (optional) True to also remove the element from the document
12731 * @return {CompositeElement} this
12733 removeElement : function(el, removeDom){
12734 if(el instanceof Array){
12735 for(var i = 0, len = el.length; i < len; i++){
12736 this.removeElement(el[i]);
12740 var index = typeof el == 'number' ? el : this.indexOf(el);
12743 var d = this.elements[index];
12747 d.parentNode.removeChild(d);
12750 this.elements.splice(index, 1);
12756 * Replaces the specified element with the passed element.
12757 * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12759 * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12760 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12761 * @return {CompositeElement} this
12763 replaceElement : function(el, replacement, domReplace){
12764 var index = typeof el == 'number' ? el : this.indexOf(el);
12767 this.elements[index].replaceWith(replacement);
12769 this.elements.splice(index, 1, Roo.get(replacement))
12776 * Removes all elements.
12778 clear : function(){
12779 this.elements = [];
12783 Roo.CompositeElement.createCall = function(proto, fnName){
12784 if(!proto[fnName]){
12785 proto[fnName] = function(){
12786 return this.invoke(fnName, arguments);
12790 for(var fnName in Roo.Element.prototype){
12791 if(typeof Roo.Element.prototype[fnName] == "function"){
12792 Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12798 * Ext JS Library 1.1.1
12799 * Copyright(c) 2006-2007, Ext JS, LLC.
12801 * Originally Released Under LGPL - original licence link has changed is not relivant.
12804 * <script type="text/javascript">
12808 * @class Roo.CompositeElementLite
12809 * @extends Roo.CompositeElement
12810 * Flyweight composite class. Reuses the same Roo.Element for element operations.
12812 var els = Roo.select("#some-el div.some-class");
12813 // or select directly from an existing element
12814 var el = Roo.get('some-el');
12815 el.select('div.some-class');
12817 els.setWidth(100); // all elements become 100 width
12818 els.hide(true); // all elements fade out and hide
12820 els.setWidth(100).hide(true);
12821 </code></pre><br><br>
12822 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12823 * actions will be performed on all the elements in this collection.</b>
12825 Roo.CompositeElementLite = function(els){
12826 Roo.CompositeElementLite.superclass.constructor.call(this, els);
12827 this.el = new Roo.Element.Flyweight();
12829 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12830 addElements : function(els){
12832 if(els instanceof Array){
12833 this.elements = this.elements.concat(els);
12835 var yels = this.elements;
12836 var index = yels.length-1;
12837 for(var i = 0, len = els.length; i < len; i++) {
12838 yels[++index] = els[i];
12844 invoke : function(fn, args){
12845 var els = this.elements;
12847 for(var i = 0, len = els.length; i < len; i++) {
12849 Roo.Element.prototype[fn].apply(el, args);
12854 * Returns a flyweight Element of the dom element object at the specified index
12855 * @param {Number} index
12856 * @return {Roo.Element}
12858 item : function(index){
12859 if(!this.elements[index]){
12862 this.el.dom = this.elements[index];
12866 // fixes scope with flyweight
12867 addListener : function(eventName, handler, scope, opt){
12868 var els = this.elements;
12869 for(var i = 0, len = els.length; i < len; i++) {
12870 Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12876 * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12877 * passed is the flyweight (shared) Roo.Element instance, so if you require a
12878 * a reference to the dom node, use el.dom.</b>
12879 * @param {Function} fn The function to call
12880 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12881 * @return {CompositeElement} this
12883 each : function(fn, scope){
12884 var els = this.elements;
12886 for(var i = 0, len = els.length; i < len; i++){
12888 if(fn.call(scope || el, el, this, i) === false){
12895 indexOf : function(el){
12896 return this.elements.indexOf(Roo.getDom(el));
12899 replaceElement : function(el, replacement, domReplace){
12900 var index = typeof el == 'number' ? el : this.indexOf(el);
12902 replacement = Roo.getDom(replacement);
12904 var d = this.elements[index];
12905 d.parentNode.insertBefore(replacement, d);
12906 d.parentNode.removeChild(d);
12908 this.elements.splice(index, 1, replacement);
12913 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12917 * Ext JS Library 1.1.1
12918 * Copyright(c) 2006-2007, Ext JS, LLC.
12920 * Originally Released Under LGPL - original licence link has changed is not relivant.
12923 * <script type="text/javascript">
12929 * @class Roo.data.Connection
12930 * @extends Roo.util.Observable
12931 * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12932 * either to a configured URL, or to a URL specified at request time.
12934 * Requests made by this class are asynchronous, and will return immediately. No data from
12935 * the server will be available to the statement immediately following the {@link #request} call.
12936 * To process returned data, use a callback in the request options object, or an event listener.
12938 * Note: If you are doing a file upload, you will not get a normal response object sent back to
12939 * your callback or event handler. Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12940 * The response object is created using the innerHTML of the IFRAME's document as the responseText
12941 * property and, if present, the IFRAME's XML document as the responseXML property.
12943 * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12944 * that it be placed either inside a <textarea> in an HTML document and retrieved from the responseText
12945 * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12946 * standard DOM methods.
12948 * @param {Object} config a configuration object.
12950 Roo.data.Connection = function(config){
12951 Roo.apply(this, config);
12954 * @event beforerequest
12955 * Fires before a network request is made to retrieve a data object.
12956 * @param {Connection} conn This Connection object.
12957 * @param {Object} options The options config object passed to the {@link #request} method.
12959 "beforerequest" : true,
12961 * @event requestcomplete
12962 * Fires if the request was successfully completed.
12963 * @param {Connection} conn This Connection object.
12964 * @param {Object} response The XHR object containing the response data.
12965 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12966 * @param {Object} options The options config object passed to the {@link #request} method.
12968 "requestcomplete" : true,
12970 * @event requestexception
12971 * Fires if an error HTTP status was returned from the server.
12972 * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12973 * @param {Connection} conn This Connection object.
12974 * @param {Object} response The XHR object containing the response data.
12975 * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12976 * @param {Object} options The options config object passed to the {@link #request} method.
12978 "requestexception" : true
12980 Roo.data.Connection.superclass.constructor.call(this);
12983 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12985 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12988 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12989 * extra parameters to each request made by this object. (defaults to undefined)
12992 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12993 * to each request made by this object. (defaults to undefined)
12996 * @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)
12999 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13003 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13009 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13012 disableCaching: true,
13015 * Sends an HTTP request to a remote server.
13016 * @param {Object} options An object which may contain the following properties:<ul>
13017 * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13018 * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13019 * request, a url encoded string or a function to call to get either.</li>
13020 * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13021 * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13022 * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13023 * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13024 * <li>options {Object} The parameter to the request call.</li>
13025 * <li>success {Boolean} True if the request succeeded.</li>
13026 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13028 * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13029 * The callback is passed the following parameters:<ul>
13030 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13031 * <li>options {Object} The parameter to the request call.</li>
13033 * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13034 * The callback is passed the following parameters:<ul>
13035 * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13036 * <li>options {Object} The parameter to the request call.</li>
13038 * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13039 * for the callback function. Defaults to the browser window.</li>
13040 * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13041 * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13042 * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13043 * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13044 * params for the post data. Any params will be appended to the URL.</li>
13045 * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13047 * @return {Number} transactionId
13049 request : function(o){
13050 if(this.fireEvent("beforerequest", this, o) !== false){
13053 if(typeof p == "function"){
13054 p = p.call(o.scope||window, o);
13056 if(typeof p == "object"){
13057 p = Roo.urlEncode(o.params);
13059 if(this.extraParams){
13060 var extras = Roo.urlEncode(this.extraParams);
13061 p = p ? (p + '&' + extras) : extras;
13064 var url = o.url || this.url;
13065 if(typeof url == 'function'){
13066 url = url.call(o.scope||window, o);
13070 var form = Roo.getDom(o.form);
13071 url = url || form.action;
13073 var enctype = form.getAttribute("enctype");
13076 return this.doFormDataUpload(o, url);
13079 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13080 return this.doFormUpload(o, p, url);
13082 var f = Roo.lib.Ajax.serializeForm(form);
13083 p = p ? (p + '&' + f) : f;
13086 if (!o.form && o.formData) {
13087 o.formData = o.formData === true ? new FormData() : o.formData;
13088 for (var k in o.params) {
13089 o.formData.append(k,o.params[k]);
13092 return this.doFormDataUpload(o, url);
13096 var hs = o.headers;
13097 if(this.defaultHeaders){
13098 hs = Roo.apply(hs || {}, this.defaultHeaders);
13105 success: this.handleResponse,
13106 failure: this.handleFailure,
13108 argument: {options: o},
13109 timeout : o.timeout || this.timeout
13112 var method = o.method||this.method||(p ? "POST" : "GET");
13114 if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13115 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13118 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13122 }else if(this.autoAbort !== false){
13126 if((method == 'GET' && p) || o.xmlData){
13127 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13130 Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13131 this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13132 Roo.lib.Ajax.useDefaultHeader == true;
13133 return this.transId;
13135 Roo.callback(o.callback, o.scope, [o, null, null]);
13141 * Determine whether this object has a request outstanding.
13142 * @param {Number} transactionId (Optional) defaults to the last transaction
13143 * @return {Boolean} True if there is an outstanding request.
13145 isLoading : function(transId){
13147 return Roo.lib.Ajax.isCallInProgress(transId);
13149 return this.transId ? true : false;
13154 * Aborts any outstanding request.
13155 * @param {Number} transactionId (Optional) defaults to the last transaction
13157 abort : function(transId){
13158 if(transId || this.isLoading()){
13159 Roo.lib.Ajax.abort(transId || this.transId);
13164 handleResponse : function(response){
13165 this.transId = false;
13166 var options = response.argument.options;
13167 response.argument = options ? options.argument : null;
13168 this.fireEvent("requestcomplete", this, response, options);
13169 Roo.callback(options.success, options.scope, [response, options]);
13170 Roo.callback(options.callback, options.scope, [options, true, response]);
13174 handleFailure : function(response, e){
13175 this.transId = false;
13176 var options = response.argument.options;
13177 response.argument = options ? options.argument : null;
13178 this.fireEvent("requestexception", this, response, options, e);
13179 Roo.callback(options.failure, options.scope, [response, options]);
13180 Roo.callback(options.callback, options.scope, [options, false, response]);
13184 doFormUpload : function(o, ps, url){
13186 var frame = document.createElement('iframe');
13189 frame.className = 'x-hidden';
13191 frame.src = Roo.SSL_SECURE_URL;
13193 document.body.appendChild(frame);
13196 document.frames[id].name = id;
13199 var form = Roo.getDom(o.form);
13201 form.method = 'POST';
13202 form.enctype = form.encoding = 'multipart/form-data';
13208 if(ps){ // add dynamic params
13210 ps = Roo.urlDecode(ps, false);
13212 if(ps.hasOwnProperty(k)){
13213 hd = document.createElement('input');
13214 hd.type = 'hidden';
13217 form.appendChild(hd);
13224 var r = { // bogus response object
13229 r.argument = o ? o.argument : null;
13234 doc = frame.contentWindow.document;
13236 doc = (frame.contentDocument || window.frames[id].document);
13238 if(doc && doc.body){
13239 r.responseText = doc.body.innerHTML;
13241 if(doc && doc.XMLDocument){
13242 r.responseXML = doc.XMLDocument;
13244 r.responseXML = doc;
13251 Roo.EventManager.removeListener(frame, 'load', cb, this);
13253 this.fireEvent("requestcomplete", this, r, o);
13254 Roo.callback(o.success, o.scope, [r, o]);
13255 Roo.callback(o.callback, o.scope, [o, true, r]);
13257 setTimeout(function(){document.body.removeChild(frame);}, 100);
13260 Roo.EventManager.on(frame, 'load', cb, this);
13263 if(hiddens){ // remove dynamic params
13264 for(var i = 0, len = hiddens.length; i < len; i++){
13265 form.removeChild(hiddens[i]);
13269 // this is a 'formdata version???'
13272 doFormDataUpload : function(o, url)
13276 var form = Roo.getDom(o.form);
13277 form.enctype = form.encoding = 'multipart/form-data';
13278 formData = o.formData === true ? new FormData(form) : o.formData;
13280 formData = o.formData === true ? new FormData() : o.formData;
13285 success: this.handleResponse,
13286 failure: this.handleFailure,
13288 argument: {options: o},
13289 timeout : o.timeout || this.timeout
13292 if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13296 }else if(this.autoAbort !== false){
13300 //Roo.lib.Ajax.defaultPostHeader = null;
13301 Roo.lib.Ajax.useDefaultHeader = false;
13302 this.transId = Roo.lib.Ajax.request( "POST", url, cb, formData, o);
13303 Roo.lib.Ajax.useDefaultHeader = true;
13311 * Ext JS Library 1.1.1
13312 * Copyright(c) 2006-2007, Ext JS, LLC.
13314 * Originally Released Under LGPL - original licence link has changed is not relivant.
13317 * <script type="text/javascript">
13321 * Global Ajax request class.
13324 * @extends Roo.data.Connection
13327 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
13328 * @cfg {Object} extraParams An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13329 * @cfg {Object} defaultHeaders An object containing request headers which are added to each request made by this object. (defaults to undefined)
13330 * @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)
13331 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13332 * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13333 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13335 Roo.Ajax = new Roo.data.Connection({
13344 * Serialize the passed form into a url encoded string
13346 * @param {String/HTMLElement} form
13349 serializeForm : function(form){
13350 return Roo.lib.Ajax.serializeForm(form);
13354 * Ext JS Library 1.1.1
13355 * Copyright(c) 2006-2007, Ext JS, LLC.
13357 * Originally Released Under LGPL - original licence link has changed is not relivant.
13360 * <script type="text/javascript">
13365 * @class Roo.UpdateManager
13366 * @extends Roo.util.Observable
13367 * Provides AJAX-style update for Element object.<br><br>
13370 * // Get it from a Roo.Element object
13371 * var el = Roo.get("foo");
13372 * var mgr = el.getUpdateManager();
13373 * mgr.update("http://myserver.com/index.php", "param1=1&param2=2");
13375 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13377 * // or directly (returns the same UpdateManager instance)
13378 * var mgr = new Roo.UpdateManager("myElementId");
13379 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13380 * mgr.on("update", myFcnNeedsToKnow);
13382 // short handed call directly from the element object
13383 Roo.get("foo").load({
13387 text: "Loading Foo..."
13391 * Create new UpdateManager directly.
13392 * @param {String/HTMLElement/Roo.Element} el The element to update
13393 * @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).
13395 Roo.UpdateManager = function(el, forceNew){
13397 if(!forceNew && el.updateManager){
13398 return el.updateManager;
13401 * The Element object
13402 * @type Roo.Element
13406 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13409 this.defaultUrl = null;
13413 * @event beforeupdate
13414 * Fired before an update is made, return false from your handler and the update is cancelled.
13415 * @param {Roo.Element} el
13416 * @param {String/Object/Function} url
13417 * @param {String/Object} params
13419 "beforeupdate": true,
13422 * Fired after successful update is made.
13423 * @param {Roo.Element} el
13424 * @param {Object} oResponseObject The response Object
13429 * Fired on update failure.
13430 * @param {Roo.Element} el
13431 * @param {Object} oResponseObject The response Object
13435 var d = Roo.UpdateManager.defaults;
13437 * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13440 this.sslBlankUrl = d.sslBlankUrl;
13442 * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13445 this.disableCaching = d.disableCaching;
13447 * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '<div class="loading-indicator">Loading...</div>').
13450 this.indicatorText = d.indicatorText;
13452 * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13455 this.showLoadIndicator = d.showLoadIndicator;
13457 * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13460 this.timeout = d.timeout;
13463 * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13466 this.loadScripts = d.loadScripts;
13469 * Transaction object of current executing transaction
13471 this.transaction = null;
13476 this.autoRefreshProcId = null;
13478 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13481 this.refreshDelegate = this.refresh.createDelegate(this);
13483 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13486 this.updateDelegate = this.update.createDelegate(this);
13488 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13491 this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13495 this.successDelegate = this.processSuccess.createDelegate(this);
13499 this.failureDelegate = this.processFailure.createDelegate(this);
13501 if(!this.renderer){
13503 * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13505 this.renderer = new Roo.UpdateManager.BasicRenderer();
13508 Roo.UpdateManager.superclass.constructor.call(this);
13511 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13513 * Get the Element this UpdateManager is bound to
13514 * @return {Roo.Element} The element
13516 getEl : function(){
13520 * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13521 * @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:
13524 url: "your-url.php",<br/>
13525 params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13526 callback: yourFunction,<br/>
13527 scope: yourObject, //(optional scope) <br/>
13528 discardUrl: false, <br/>
13529 nocache: false,<br/>
13530 text: "Loading...",<br/>
13532 scripts: false<br/>
13535 * The only required property is url. The optional properties nocache, text and scripts
13536 * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13537 * @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}
13538 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13539 * @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.
13541 update : function(url, params, callback, discardUrl){
13542 if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13543 var method = this.method,
13545 if(typeof url == "object"){ // must be config object
13548 params = params || cfg.params;
13549 callback = callback || cfg.callback;
13550 discardUrl = discardUrl || cfg.discardUrl;
13551 if(callback && cfg.scope){
13552 callback = callback.createDelegate(cfg.scope);
13554 if(typeof cfg.method != "undefined"){method = cfg.method;};
13555 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13556 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13557 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13558 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13560 this.showLoading();
13562 this.defaultUrl = url;
13564 if(typeof url == "function"){
13565 url = url.call(this);
13568 method = method || (params ? "POST" : "GET");
13569 if(method == "GET"){
13570 url = this.prepareUrl(url);
13573 var o = Roo.apply(cfg ||{}, {
13576 success: this.successDelegate,
13577 failure: this.failureDelegate,
13578 callback: undefined,
13579 timeout: (this.timeout*1000),
13580 argument: {"url": url, "form": null, "callback": callback, "params": params}
13582 Roo.log("updated manager called with timeout of " + o.timeout);
13583 this.transaction = Roo.Ajax.request(o);
13588 * 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.
13589 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13590 * @param {String/HTMLElement} form The form Id or form element
13591 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13592 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13593 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13595 formUpdate : function(form, url, reset, callback){
13596 if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13597 if(typeof url == "function"){
13598 url = url.call(this);
13600 form = Roo.getDom(form);
13601 this.transaction = Roo.Ajax.request({
13604 success: this.successDelegate,
13605 failure: this.failureDelegate,
13606 timeout: (this.timeout*1000),
13607 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13609 this.showLoading.defer(1, this);
13614 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13615 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13617 refresh : function(callback){
13618 if(this.defaultUrl == null){
13621 this.update(this.defaultUrl, null, callback, true);
13625 * Set this element to auto refresh.
13626 * @param {Number} interval How often to update (in seconds).
13627 * @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)
13628 * @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}
13629 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13630 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13632 startAutoRefresh : function(interval, url, params, callback, refreshNow){
13634 this.update(url || this.defaultUrl, params, callback, true);
13636 if(this.autoRefreshProcId){
13637 clearInterval(this.autoRefreshProcId);
13639 this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13643 * Stop auto refresh on this element.
13645 stopAutoRefresh : function(){
13646 if(this.autoRefreshProcId){
13647 clearInterval(this.autoRefreshProcId);
13648 delete this.autoRefreshProcId;
13652 isAutoRefreshing : function(){
13653 return this.autoRefreshProcId ? true : false;
13656 * Called to update the element to "Loading" state. Override to perform custom action.
13658 showLoading : function(){
13659 if(this.showLoadIndicator){
13660 this.el.update(this.indicatorText);
13665 * Adds unique parameter to query string if disableCaching = true
13668 prepareUrl : function(url){
13669 if(this.disableCaching){
13670 var append = "_dc=" + (new Date().getTime());
13671 if(url.indexOf("?") !== -1){
13672 url += "&" + append;
13674 url += "?" + append;
13683 processSuccess : function(response){
13684 this.transaction = null;
13685 if(response.argument.form && response.argument.reset){
13686 try{ // put in try/catch since some older FF releases had problems with this
13687 response.argument.form.reset();
13690 if(this.loadScripts){
13691 this.renderer.render(this.el, response, this,
13692 this.updateComplete.createDelegate(this, [response]));
13694 this.renderer.render(this.el, response, this);
13695 this.updateComplete(response);
13699 updateComplete : function(response){
13700 this.fireEvent("update", this.el, response);
13701 if(typeof response.argument.callback == "function"){
13702 response.argument.callback(this.el, true, response);
13709 processFailure : function(response){
13710 this.transaction = null;
13711 this.fireEvent("failure", this.el, response);
13712 if(typeof response.argument.callback == "function"){
13713 response.argument.callback(this.el, false, response);
13718 * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13719 * @param {Object} renderer The object implementing the render() method
13721 setRenderer : function(renderer){
13722 this.renderer = renderer;
13725 getRenderer : function(){
13726 return this.renderer;
13730 * Set the defaultUrl used for updates
13731 * @param {String/Function} defaultUrl The url or a function to call to get the url
13733 setDefaultUrl : function(defaultUrl){
13734 this.defaultUrl = defaultUrl;
13738 * Aborts the executing transaction
13740 abort : function(){
13741 if(this.transaction){
13742 Roo.Ajax.abort(this.transaction);
13747 * Returns true if an update is in progress
13748 * @return {Boolean}
13750 isUpdating : function(){
13751 if(this.transaction){
13752 return Roo.Ajax.isLoading(this.transaction);
13759 * @class Roo.UpdateManager.defaults
13760 * @static (not really - but it helps the doc tool)
13761 * The defaults collection enables customizing the default properties of UpdateManager
13763 Roo.UpdateManager.defaults = {
13765 * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13771 * True to process scripts by default (Defaults to false).
13774 loadScripts : false,
13777 * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13780 sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13782 * Whether to append unique parameter on get request to disable caching (Defaults to false).
13785 disableCaching : false,
13787 * Whether to show indicatorText when loading (Defaults to true).
13790 showLoadIndicator : true,
13792 * Text for loading indicator (Defaults to '<div class="loading-indicator">Loading...</div>').
13795 indicatorText : '<div class="loading-indicator">Loading...</div>'
13799 * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13801 * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13802 * @param {String/HTMLElement/Roo.Element} el The element to update
13803 * @param {String} url The url
13804 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13805 * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13808 * @member Roo.UpdateManager
13810 Roo.UpdateManager.updateElement = function(el, url, params, options){
13811 var um = Roo.get(el, true).getUpdateManager();
13812 Roo.apply(um, options);
13813 um.update(url, params, options ? options.callback : null);
13815 // alias for backwards compat
13816 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13818 * @class Roo.UpdateManager.BasicRenderer
13819 * Default Content renderer. Updates the elements innerHTML with the responseText.
13821 Roo.UpdateManager.BasicRenderer = function(){};
13823 Roo.UpdateManager.BasicRenderer.prototype = {
13825 * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13826 * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13827 * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13828 * @param {Roo.Element} el The element being rendered
13829 * @param {Object} response The YUI Connect response object
13830 * @param {UpdateManager} updateManager The calling update manager
13831 * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13833 render : function(el, response, updateManager, callback){
13834 el.update(response.responseText, updateManager.loadScripts, callback);
13840 * (c)) Alan Knowles
13846 * @class Roo.DomTemplate
13847 * @extends Roo.Template
13848 * An effort at a dom based template engine..
13850 * Similar to XTemplate, except it uses dom parsing to create the template..
13852 * Supported features:
13857 {a_variable} - output encoded.
13858 {a_variable.format:("Y-m-d")} - call a method on the variable
13859 {a_variable:raw} - unencoded output
13860 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13861 {a_variable:this.method_on_template(...)} - call a method on the template object.
13866 <div roo-for="a_variable or condition.."></div>
13867 <div roo-if="a_variable or condition"></div>
13868 <div roo-exec="some javascript"></div>
13869 <div roo-name="named_template"></div>
13874 Roo.DomTemplate = function()
13876 Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13883 Roo.extend(Roo.DomTemplate, Roo.Template, {
13885 * id counter for sub templates.
13889 * flag to indicate if dom parser is inside a pre,
13890 * it will strip whitespace if not.
13895 * The various sub templates
13903 * basic tag replacing syntax
13906 * // you can fake an object call by doing this
13910 re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13911 //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13913 iterChild : function (node, method) {
13915 var oldPre = this.inPre;
13916 if (node.tagName == 'PRE') {
13919 for( var i = 0; i < node.childNodes.length; i++) {
13920 method.call(this, node.childNodes[i]);
13922 this.inPre = oldPre;
13928 * compile the template
13930 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13933 compile: function()
13937 // covert the html into DOM...
13941 doc = document.implementation.createHTMLDocument("");
13942 doc.documentElement.innerHTML = this.html ;
13943 div = doc.documentElement;
13945 // old IE... - nasty -- it causes all sorts of issues.. with
13946 // images getting pulled from server..
13947 div = document.createElement('div');
13948 div.innerHTML = this.html;
13950 //doc.documentElement.innerHTML = htmlBody
13956 this.iterChild(div, function(n) {_t.compileNode(n, true); });
13958 var tpls = this.tpls;
13960 // create a top level template from the snippet..
13962 //Roo.log(div.innerHTML);
13969 body : div.innerHTML,
13982 Roo.each(tpls, function(tp){
13983 this.compileTpl(tp);
13984 this.tpls[tp.id] = tp;
13987 this.master = tpls[0];
13993 compileNode : function(node, istop) {
13998 // skip anything not a tag..
13999 if (node.nodeType != 1) {
14000 if (node.nodeType == 3 && !this.inPre) {
14001 // reduce white space..
14002 node.nodeValue = node.nodeValue.replace(/\s+/g, ' ');
14025 case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14026 case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14027 case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14028 case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14034 // just itterate children..
14035 this.iterChild(node,this.compileNode);
14038 tpl.uid = this.id++;
14039 tpl.value = node.getAttribute('roo-' + tpl.attr);
14040 node.removeAttribute('roo-'+ tpl.attr);
14041 if (tpl.attr != 'name') {
14042 var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14043 node.parentNode.replaceChild(placeholder, node);
14046 var placeholder = document.createElement('span');
14047 placeholder.className = 'roo-tpl-' + tpl.value;
14048 node.parentNode.replaceChild(placeholder, node);
14051 // parent now sees '{domtplXXXX}
14052 this.iterChild(node,this.compileNode);
14054 // we should now have node body...
14055 var div = document.createElement('div');
14056 div.appendChild(node);
14058 // this has the unfortunate side effect of converting tagged attributes
14059 // eg. href="{...}" into %7C...%7D
14060 // this has been fixed by searching for those combo's although it's a bit hacky..
14063 tpl.body = div.innerHTML;
14070 switch (tpl.value) {
14071 case '.': tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14072 case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14073 default: tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14078 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14082 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14086 tpl.id = tpl.value; // replace non characters???
14092 this.tpls.push(tpl);
14102 * Compile a segment of the template into a 'sub-template'
14108 compileTpl : function(tpl)
14110 var fm = Roo.util.Format;
14111 var useF = this.disableFormats !== true;
14113 var sep = Roo.isGecko ? "+\n" : ",\n";
14115 var undef = function(str) {
14116 Roo.debug && Roo.log("Property not found :" + str);
14120 //Roo.log(tpl.body);
14124 var fn = function(m, lbrace, name, format, args)
14127 //Roo.log(arguments);
14128 args = args ? args.replace(/\\'/g,"'") : args;
14129 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14130 if (typeof(format) == 'undefined') {
14131 format = 'htmlEncode';
14133 if (format == 'raw' ) {
14137 if(name.substr(0, 6) == 'domtpl'){
14138 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14141 // build an array of options to determine if value is undefined..
14143 // basically get 'xxxx.yyyy' then do
14144 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14145 // (function () { Roo.log("Property not found"); return ''; })() :
14150 Roo.each(name.split('.'), function(st) {
14151 lookfor += (lookfor.length ? '.': '') + st;
14152 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
14155 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14158 if(format && useF){
14160 args = args ? ',' + args : "";
14162 if(format.substr(0, 5) != "this."){
14163 format = "fm." + format + '(';
14165 format = 'this.call("'+ format.substr(5) + '", ';
14169 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
14172 if (args && args.length) {
14173 // called with xxyx.yuu:(test,test)
14175 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
14177 // raw.. - :raw modifier..
14178 return "'"+ sep + udef_st + name + ")"+sep+"'";
14182 // branched to use + in gecko and [].join() in others
14184 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
14185 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14188 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
14189 body.push(tpl.body.replace(/(\r\n|\n)/g,
14190 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14191 body.push("'].join('');};};");
14192 body = body.join('');
14195 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14197 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
14204 * same as applyTemplate, except it's done to one of the subTemplates
14205 * when using named templates, you can do:
14207 * var str = pl.applySubTemplate('your-name', values);
14210 * @param {Number} id of the template
14211 * @param {Object} values to apply to template
14212 * @param {Object} parent (normaly the instance of this object)
14214 applySubTemplate : function(id, values, parent)
14218 var t = this.tpls[id];
14222 if(t.ifCall && !t.ifCall.call(this, values, parent)){
14223 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14227 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14234 if(t.execCall && t.execCall.call(this, values, parent)){
14238 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14244 var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14245 parent = t.target ? values : parent;
14246 if(t.forCall && vs instanceof Array){
14248 for(var i = 0, len = vs.length; i < len; i++){
14250 buf[buf.length] = t.compiled.call(this, vs[i], parent);
14252 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14254 //Roo.log(t.compiled);
14258 return buf.join('');
14261 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14266 return t.compiled.call(this, vs, parent);
14268 Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14270 //Roo.log(t.compiled);
14278 applyTemplate : function(values){
14279 return this.master.compiled.call(this, values, {});
14280 //var s = this.subs;
14283 apply : function(){
14284 return this.applyTemplate.apply(this, arguments);
14289 Roo.DomTemplate.from = function(el){
14290 el = Roo.getDom(el);
14291 return new Roo.Domtemplate(el.value || el.innerHTML);
14294 * Ext JS Library 1.1.1
14295 * Copyright(c) 2006-2007, Ext JS, LLC.
14297 * Originally Released Under LGPL - original licence link has changed is not relivant.
14300 * <script type="text/javascript">
14304 * @class Roo.util.DelayedTask
14305 * Provides a convenient method of performing setTimeout where a new
14306 * timeout cancels the old timeout. An example would be performing validation on a keypress.
14307 * You can use this class to buffer
14308 * the keypress events for a certain number of milliseconds, and perform only if they stop
14309 * for that amount of time.
14310 * @constructor The parameters to this constructor serve as defaults and are not required.
14311 * @param {Function} fn (optional) The default function to timeout
14312 * @param {Object} scope (optional) The default scope of that timeout
14313 * @param {Array} args (optional) The default Array of arguments
14315 Roo.util.DelayedTask = function(fn, scope, args){
14316 var id = null, d, t;
14318 var call = function(){
14319 var now = new Date().getTime();
14323 fn.apply(scope, args || []);
14327 * Cancels any pending timeout and queues a new one
14328 * @param {Number} delay The milliseconds to delay
14329 * @param {Function} newFn (optional) Overrides function passed to constructor
14330 * @param {Object} newScope (optional) Overrides scope passed to constructor
14331 * @param {Array} newArgs (optional) Overrides args passed to constructor
14333 this.delay = function(delay, newFn, newScope, newArgs){
14334 if(id && delay != d){
14338 t = new Date().getTime();
14340 scope = newScope || scope;
14341 args = newArgs || args;
14343 id = setInterval(call, d);
14348 * Cancel the last queued timeout
14350 this.cancel = function(){
14358 * Ext JS Library 1.1.1
14359 * Copyright(c) 2006-2007, Ext JS, LLC.
14361 * Originally Released Under LGPL - original licence link has changed is not relivant.
14364 * <script type="text/javascript">
14367 * @class Roo.util.TaskRunner
14368 * Manage background tasks - not sure why this is better that setInterval?
14373 Roo.util.TaskRunner = function(interval){
14374 interval = interval || 10;
14375 var tasks = [], removeQueue = [];
14377 var running = false;
14379 var stopThread = function(){
14385 var startThread = function(){
14388 id = setInterval(runTasks, interval);
14392 var removeTask = function(task){
14393 removeQueue.push(task);
14399 var runTasks = function(){
14400 if(removeQueue.length > 0){
14401 for(var i = 0, len = removeQueue.length; i < len; i++){
14402 tasks.remove(removeQueue[i]);
14405 if(tasks.length < 1){
14410 var now = new Date().getTime();
14411 for(var i = 0, len = tasks.length; i < len; ++i){
14413 var itime = now - t.taskRunTime;
14414 if(t.interval <= itime){
14415 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14416 t.taskRunTime = now;
14417 if(rt === false || t.taskRunCount === t.repeat){
14422 if(t.duration && t.duration <= (now - t.taskStartTime)){
14429 * Queues a new task.
14430 * @param {Object} task
14432 * Task property : interval = how frequent to run.
14433 * Task object should implement
14435 * Task object may implement
14436 * function onStop()
14438 this.start = function(task){
14440 task.taskStartTime = new Date().getTime();
14441 task.taskRunTime = 0;
14442 task.taskRunCount = 0;
14448 * @param {Object} task
14450 this.stop = function(task){
14457 this.stopAll = function(){
14459 for(var i = 0, len = tasks.length; i < len; i++){
14460 if(tasks[i].onStop){
14469 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14471 * Ext JS Library 1.1.1
14472 * Copyright(c) 2006-2007, Ext JS, LLC.
14474 * Originally Released Under LGPL - original licence link has changed is not relivant.
14477 * <script type="text/javascript">
14482 * @class Roo.util.MixedCollection
14483 * @extends Roo.util.Observable
14484 * A Collection class that maintains both numeric indexes and keys and exposes events.
14486 * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14487 * collection (defaults to false)
14488 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14489 * and return the key value for that item. This is used when available to look up the key on items that
14490 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
14491 * equivalent to providing an implementation for the {@link #getKey} method.
14493 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14501 * Fires when the collection is cleared.
14506 * Fires when an item is added to the collection.
14507 * @param {Number} index The index at which the item was added.
14508 * @param {Object} o The item added.
14509 * @param {String} key The key associated with the added item.
14514 * Fires when an item is replaced in the collection.
14515 * @param {String} key he key associated with the new added.
14516 * @param {Object} old The item being replaced.
14517 * @param {Object} new The new item.
14522 * Fires when an item is removed from the collection.
14523 * @param {Object} o The item being removed.
14524 * @param {String} key (optional) The key associated with the removed item.
14529 this.allowFunctions = allowFunctions === true;
14531 this.getKey = keyFn;
14533 Roo.util.MixedCollection.superclass.constructor.call(this);
14536 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14537 allowFunctions : false,
14540 * Adds an item to the collection.
14541 * @param {String} key The key to associate with the item
14542 * @param {Object} o The item to add.
14543 * @return {Object} The item added.
14545 add : function(key, o){
14546 if(arguments.length == 1){
14548 key = this.getKey(o);
14550 if(typeof key == "undefined" || key === null){
14552 this.items.push(o);
14553 this.keys.push(null);
14555 var old = this.map[key];
14557 return this.replace(key, o);
14560 this.items.push(o);
14562 this.keys.push(key);
14564 this.fireEvent("add", this.length-1, o, key);
14569 * MixedCollection has a generic way to fetch keys if you implement getKey.
14572 var mc = new Roo.util.MixedCollection();
14573 mc.add(someEl.dom.id, someEl);
14574 mc.add(otherEl.dom.id, otherEl);
14578 var mc = new Roo.util.MixedCollection();
14579 mc.getKey = function(el){
14585 // or via the constructor
14586 var mc = new Roo.util.MixedCollection(false, function(el){
14592 * @param o {Object} The item for which to find the key.
14593 * @return {Object} The key for the passed item.
14595 getKey : function(o){
14600 * Replaces an item in the collection.
14601 * @param {String} key The key associated with the item to replace, or the item to replace.
14602 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14603 * @return {Object} The new item.
14605 replace : function(key, o){
14606 if(arguments.length == 1){
14608 key = this.getKey(o);
14610 var old = this.item(key);
14611 if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14612 return this.add(key, o);
14614 var index = this.indexOfKey(key);
14615 this.items[index] = o;
14617 this.fireEvent("replace", key, old, o);
14622 * Adds all elements of an Array or an Object to the collection.
14623 * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14624 * an Array of values, each of which are added to the collection.
14626 addAll : function(objs){
14627 if(arguments.length > 1 || objs instanceof Array){
14628 var args = arguments.length > 1 ? arguments : objs;
14629 for(var i = 0, len = args.length; i < len; i++){
14633 for(var key in objs){
14634 if(this.allowFunctions || typeof objs[key] != "function"){
14635 this.add(key, objs[key]);
14642 * Executes the specified function once for every item in the collection, passing each
14643 * item as the first and only parameter. returning false from the function will stop the iteration.
14644 * @param {Function} fn The function to execute for each item.
14645 * @param {Object} scope (optional) The scope in which to execute the function.
14647 each : function(fn, scope){
14648 var items = [].concat(this.items); // each safe for removal
14649 for(var i = 0, len = items.length; i < len; i++){
14650 if(fn.call(scope || items[i], items[i], i, len) === false){
14657 * Executes the specified function once for every key in the collection, passing each
14658 * key, and its associated item as the first two parameters.
14659 * @param {Function} fn The function to execute for each item.
14660 * @param {Object} scope (optional) The scope in which to execute the function.
14662 eachKey : function(fn, scope){
14663 for(var i = 0, len = this.keys.length; i < len; i++){
14664 fn.call(scope || window, this.keys[i], this.items[i], i, len);
14669 * Returns the first item in the collection which elicits a true return value from the
14670 * passed selection function.
14671 * @param {Function} fn The selection function to execute for each item.
14672 * @param {Object} scope (optional) The scope in which to execute the function.
14673 * @return {Object} The first item in the collection which returned true from the selection function.
14675 find : function(fn, scope){
14676 for(var i = 0, len = this.items.length; i < len; i++){
14677 if(fn.call(scope || window, this.items[i], this.keys[i])){
14678 return this.items[i];
14685 * Inserts an item at the specified index in the collection.
14686 * @param {Number} index The index to insert the item at.
14687 * @param {String} key The key to associate with the new item, or the item itself.
14688 * @param {Object} o (optional) If the second parameter was a key, the new item.
14689 * @return {Object} The item inserted.
14691 insert : function(index, key, o){
14692 if(arguments.length == 2){
14694 key = this.getKey(o);
14696 if(index >= this.length){
14697 return this.add(key, o);
14700 this.items.splice(index, 0, o);
14701 if(typeof key != "undefined" && key != null){
14704 this.keys.splice(index, 0, key);
14705 this.fireEvent("add", index, o, key);
14710 * Removed an item from the collection.
14711 * @param {Object} o The item to remove.
14712 * @return {Object} The item removed.
14714 remove : function(o){
14715 return this.removeAt(this.indexOf(o));
14719 * Remove an item from a specified index in the collection.
14720 * @param {Number} index The index within the collection of the item to remove.
14722 removeAt : function(index){
14723 if(index < this.length && index >= 0){
14725 var o = this.items[index];
14726 this.items.splice(index, 1);
14727 var key = this.keys[index];
14728 if(typeof key != "undefined"){
14729 delete this.map[key];
14731 this.keys.splice(index, 1);
14732 this.fireEvent("remove", o, key);
14737 * Removed an item associated with the passed key fom the collection.
14738 * @param {String} key The key of the item to remove.
14740 removeKey : function(key){
14741 return this.removeAt(this.indexOfKey(key));
14745 * Returns the number of items in the collection.
14746 * @return {Number} the number of items in the collection.
14748 getCount : function(){
14749 return this.length;
14753 * Returns index within the collection of the passed Object.
14754 * @param {Object} o The item to find the index of.
14755 * @return {Number} index of the item.
14757 indexOf : function(o){
14758 if(!this.items.indexOf){
14759 for(var i = 0, len = this.items.length; i < len; i++){
14760 if(this.items[i] == o) {
14766 return this.items.indexOf(o);
14771 * Returns index within the collection of the passed key.
14772 * @param {String} key The key to find the index of.
14773 * @return {Number} index of the key.
14775 indexOfKey : function(key){
14776 if(!this.keys.indexOf){
14777 for(var i = 0, len = this.keys.length; i < len; i++){
14778 if(this.keys[i] == key) {
14784 return this.keys.indexOf(key);
14789 * Returns the item associated with the passed key OR index. Key has priority over index.
14790 * @param {String/Number} key The key or index of the item.
14791 * @return {Object} The item associated with the passed key.
14793 item : function(key){
14794 if (key === 'length') {
14797 var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14798 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14802 * Returns the item at the specified index.
14803 * @param {Number} index The index of the item.
14806 itemAt : function(index){
14807 return this.items[index];
14811 * Returns the item associated with the passed key.
14812 * @param {String/Number} key The key of the item.
14813 * @return {Object} The item associated with the passed key.
14815 key : function(key){
14816 return this.map[key];
14820 * Returns true if the collection contains the passed Object as an item.
14821 * @param {Object} o The Object to look for in the collection.
14822 * @return {Boolean} True if the collection contains the Object as an item.
14824 contains : function(o){
14825 return this.indexOf(o) != -1;
14829 * Returns true if the collection contains the passed Object as a key.
14830 * @param {String} key The key to look for in the collection.
14831 * @return {Boolean} True if the collection contains the Object as a key.
14833 containsKey : function(key){
14834 return typeof this.map[key] != "undefined";
14838 * Removes all items from the collection.
14840 clear : function(){
14845 this.fireEvent("clear");
14849 * Returns the first item in the collection.
14850 * @return {Object} the first item in the collection..
14852 first : function(){
14853 return this.items[0];
14857 * Returns the last item in the collection.
14858 * @return {Object} the last item in the collection..
14861 return this.items[this.length-1];
14864 _sort : function(property, dir, fn){
14865 var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14866 fn = fn || function(a, b){
14869 var c = [], k = this.keys, items = this.items;
14870 for(var i = 0, len = items.length; i < len; i++){
14871 c[c.length] = {key: k[i], value: items[i], index: i};
14873 c.sort(function(a, b){
14874 var v = fn(a[property], b[property]) * dsc;
14876 v = (a.index < b.index ? -1 : 1);
14880 for(var i = 0, len = c.length; i < len; i++){
14881 items[i] = c[i].value;
14884 this.fireEvent("sort", this);
14888 * Sorts this collection with the passed comparison function
14889 * @param {String} direction (optional) "ASC" or "DESC"
14890 * @param {Function} fn (optional) comparison function
14892 sort : function(dir, fn){
14893 this._sort("value", dir, fn);
14897 * Sorts this collection by keys
14898 * @param {String} direction (optional) "ASC" or "DESC"
14899 * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14901 keySort : function(dir, fn){
14902 this._sort("key", dir, fn || function(a, b){
14903 return String(a).toUpperCase()-String(b).toUpperCase();
14908 * Returns a range of items in this collection
14909 * @param {Number} startIndex (optional) defaults to 0
14910 * @param {Number} endIndex (optional) default to the last item
14911 * @return {Array} An array of items
14913 getRange : function(start, end){
14914 var items = this.items;
14915 if(items.length < 1){
14918 start = start || 0;
14919 end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14922 for(var i = start; i <= end; i++) {
14923 r[r.length] = items[i];
14926 for(var i = start; i >= end; i--) {
14927 r[r.length] = items[i];
14934 * Filter the <i>objects</i> in this collection by a specific property.
14935 * Returns a new collection that has been filtered.
14936 * @param {String} property A property on your objects
14937 * @param {String/RegExp} value Either string that the property values
14938 * should start with or a RegExp to test against the property
14939 * @return {MixedCollection} The new filtered collection
14941 filter : function(property, value){
14942 if(!value.exec){ // not a regex
14943 value = String(value);
14944 if(value.length == 0){
14945 return this.clone();
14947 value = new RegExp("^" + Roo.escapeRe(value), "i");
14949 return this.filterBy(function(o){
14950 return o && value.test(o[property]);
14955 * Filter by a function. * Returns a new collection that has been filtered.
14956 * The passed function will be called with each
14957 * object in the collection. If the function returns true, the value is included
14958 * otherwise it is filtered.
14959 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14960 * @param {Object} scope (optional) The scope of the function (defaults to this)
14961 * @return {MixedCollection} The new filtered collection
14963 filterBy : function(fn, scope){
14964 var r = new Roo.util.MixedCollection();
14965 r.getKey = this.getKey;
14966 var k = this.keys, it = this.items;
14967 for(var i = 0, len = it.length; i < len; i++){
14968 if(fn.call(scope||this, it[i], k[i])){
14969 r.add(k[i], it[i]);
14976 * Creates a duplicate of this collection
14977 * @return {MixedCollection}
14979 clone : function(){
14980 var r = new Roo.util.MixedCollection();
14981 var k = this.keys, it = this.items;
14982 for(var i = 0, len = it.length; i < len; i++){
14983 r.add(k[i], it[i]);
14985 r.getKey = this.getKey;
14990 * Returns the item associated with the passed key or index.
14992 * @param {String/Number} key The key or index of the item.
14993 * @return {Object} The item associated with the passed key.
14995 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14997 * Ext JS Library 1.1.1
14998 * Copyright(c) 2006-2007, Ext JS, LLC.
15000 * Originally Released Under LGPL - original licence link has changed is not relivant.
15003 * <script type="text/javascript">
15006 * @class Roo.util.JSON
15007 * Modified version of Douglas Crockford"s json.js that doesn"t
15008 * mess with the Object prototype
15009 * http://www.json.org/js.html
15012 Roo.util.JSON = new (function(){
15013 var useHasOwn = {}.hasOwnProperty ? true : false;
15015 // crashes Safari in some instances
15016 //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15018 var pad = function(n) {
15019 return n < 10 ? "0" + n : n;
15032 var encodeString = function(s){
15033 if (/["\\\x00-\x1f]/.test(s)) {
15034 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15039 c = b.charCodeAt();
15041 Math.floor(c / 16).toString(16) +
15042 (c % 16).toString(16);
15045 return '"' + s + '"';
15048 var encodeArray = function(o){
15049 var a = ["["], b, i, l = o.length, v;
15050 for (i = 0; i < l; i += 1) {
15052 switch (typeof v) {
15061 a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15069 var encodeDate = function(o){
15070 return '"' + o.getFullYear() + "-" +
15071 pad(o.getMonth() + 1) + "-" +
15072 pad(o.getDate()) + "T" +
15073 pad(o.getHours()) + ":" +
15074 pad(o.getMinutes()) + ":" +
15075 pad(o.getSeconds()) + '"';
15079 * Encodes an Object, Array or other value
15080 * @param {Mixed} o The variable to encode
15081 * @return {String} The JSON string
15083 this.encode = function(o)
15085 // should this be extended to fully wrap stringify..
15087 if(typeof o == "undefined" || o === null){
15089 }else if(o instanceof Array){
15090 return encodeArray(o);
15091 }else if(o instanceof Date){
15092 return encodeDate(o);
15093 }else if(typeof o == "string"){
15094 return encodeString(o);
15095 }else if(typeof o == "number"){
15096 return isFinite(o) ? String(o) : "null";
15097 }else if(typeof o == "boolean"){
15100 var a = ["{"], b, i, v;
15102 if(!useHasOwn || o.hasOwnProperty(i)) {
15104 switch (typeof v) {
15113 a.push(this.encode(i), ":",
15114 v === null ? "null" : this.encode(v));
15125 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15126 * @param {String} json The JSON string
15127 * @return {Object} The resulting object
15129 this.decode = function(json){
15131 return /** eval:var:json */ eval("(" + json + ')');
15135 * Shorthand for {@link Roo.util.JSON#encode}
15136 * @member Roo encode
15138 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15140 * Shorthand for {@link Roo.util.JSON#decode}
15141 * @member Roo decode
15143 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15146 * Ext JS Library 1.1.1
15147 * Copyright(c) 2006-2007, Ext JS, LLC.
15149 * Originally Released Under LGPL - original licence link has changed is not relivant.
15152 * <script type="text/javascript">
15156 * @class Roo.util.Format
15157 * Reusable data formatting functions
15160 Roo.util.Format = function(){
15161 var trimRe = /^\s+|\s+$/g;
15164 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15165 * @param {String} value The string to truncate
15166 * @param {Number} length The maximum length to allow before truncating
15167 * @return {String} The converted text
15169 ellipsis : function(value, len){
15170 if(value && value.length > len){
15171 return value.substr(0, len-3)+"...";
15177 * Checks a reference and converts it to empty string if it is undefined
15178 * @param {Mixed} value Reference to check
15179 * @return {Mixed} Empty string if converted, otherwise the original value
15181 undef : function(value){
15182 return typeof value != "undefined" ? value : "";
15186 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15187 * @param {String} value The string to encode
15188 * @return {String} The encoded text
15190 htmlEncode : function(value){
15191 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
15195 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15196 * @param {String} value The string to decode
15197 * @return {String} The decoded text
15199 htmlDecode : function(value){
15200 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"');
15204 * Trims any whitespace from either side of a string
15205 * @param {String} value The text to trim
15206 * @return {String} The trimmed text
15208 trim : function(value){
15209 return String(value).replace(trimRe, "");
15213 * Returns a substring from within an original string
15214 * @param {String} value The original text
15215 * @param {Number} start The start index of the substring
15216 * @param {Number} length The length of the substring
15217 * @return {String} The substring
15219 substr : function(value, start, length){
15220 return String(value).substr(start, length);
15224 * Converts a string to all lower case letters
15225 * @param {String} value The text to convert
15226 * @return {String} The converted text
15228 lowercase : function(value){
15229 return String(value).toLowerCase();
15233 * Converts a string to all upper case letters
15234 * @param {String} value The text to convert
15235 * @return {String} The converted text
15237 uppercase : function(value){
15238 return String(value).toUpperCase();
15242 * Converts the first character only of a string to upper case
15243 * @param {String} value The text to convert
15244 * @return {String} The converted text
15246 capitalize : function(value){
15247 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15251 call : function(value, fn){
15252 if(arguments.length > 2){
15253 var args = Array.prototype.slice.call(arguments, 2);
15254 args.unshift(value);
15256 return /** eval:var:value */ eval(fn).apply(window, args);
15258 /** eval:var:value */
15259 return /** eval:var:value */ eval(fn).call(window, value);
15265 * safer version of Math.toFixed..??/
15266 * @param {Number/String} value The numeric value to format
15267 * @param {Number/String} value Decimal places
15268 * @return {String} The formatted currency string
15270 toFixed : function(v, n)
15272 // why not use to fixed - precision is buggered???
15274 return Math.round(v-0);
15276 var fact = Math.pow(10,n+1);
15277 v = (Math.round((v-0)*fact))/fact;
15278 var z = (''+fact).substring(2);
15279 if (v == Math.floor(v)) {
15280 return Math.floor(v) + '.' + z;
15283 // now just padd decimals..
15284 var ps = String(v).split('.');
15285 var fd = (ps[1] + z);
15286 var r = fd.substring(0,n);
15287 var rm = fd.substring(n);
15289 return ps[0] + '.' + r;
15291 r*=1; // turn it into a number;
15293 if (String(r).length != n) {
15296 r = String(r).substring(1); // chop the end off.
15299 return ps[0] + '.' + r;
15304 * Format a number as US currency
15305 * @param {Number/String} value The numeric value to format
15306 * @return {String} The formatted currency string
15308 usMoney : function(v){
15309 return '$' + Roo.util.Format.number(v);
15314 * eventually this should probably emulate php's number_format
15315 * @param {Number/String} value The numeric value to format
15316 * @param {Number} decimals number of decimal places
15317 * @param {String} delimiter for thousands (default comma)
15318 * @return {String} The formatted currency string
15320 number : function(v, decimals, thousandsDelimiter)
15322 // multiply and round.
15323 decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15324 thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15326 var mul = Math.pow(10, decimals);
15327 var zero = String(mul).substring(1);
15328 v = (Math.round((v-0)*mul))/mul;
15330 // if it's '0' number.. then
15332 //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15334 var ps = v.split('.');
15337 var r = /(\d+)(\d{3})/;
15340 if(thousandsDelimiter.length != 0) {
15341 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15346 (decimals ? ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15347 // does not have decimals
15348 (decimals ? ('.' + zero) : '');
15351 return whole + sub ;
15355 * Parse a value into a formatted date using the specified format pattern.
15356 * @param {Mixed} value The value to format
15357 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15358 * @return {String} The formatted date string
15360 date : function(v, format){
15364 if(!(v instanceof Date)){
15365 v = new Date(Date.parse(v));
15367 return v.dateFormat(format || Roo.util.Format.defaults.date);
15371 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15372 * @param {String} format Any valid date format string
15373 * @return {Function} The date formatting function
15375 dateRenderer : function(format){
15376 return function(v){
15377 return Roo.util.Format.date(v, format);
15382 stripTagsRE : /<\/?[^>]+>/gi,
15385 * Strips all HTML tags
15386 * @param {Mixed} value The text from which to strip tags
15387 * @return {String} The stripped text
15389 stripTags : function(v){
15390 return !v ? v : String(v).replace(this.stripTagsRE, "");
15394 * Size in Mb,Gb etc.
15395 * @param {Number} value The number to be formated
15396 * @param {number} decimals how many decimal places
15397 * @return {String} the formated string
15399 size : function(value, decimals)
15401 var sizes = ['b', 'k', 'M', 'G', 'T'];
15405 var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15406 return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals) + sizes[i];
15413 Roo.util.Format.defaults = {
15417 * Ext JS Library 1.1.1
15418 * Copyright(c) 2006-2007, Ext JS, LLC.
15420 * Originally Released Under LGPL - original licence link has changed is not relivant.
15423 * <script type="text/javascript">
15430 * @class Roo.MasterTemplate
15431 * @extends Roo.Template
15432 * Provides a template that can have child templates. The syntax is:
15434 var t = new Roo.MasterTemplate(
15435 '<select name="{name}">',
15436 '<tpl name="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
15439 t.add('options', {value: 'foo', text: 'bar'});
15440 // or you can add multiple child elements in one shot
15441 t.addAll('options', [
15442 {value: 'foo', text: 'bar'},
15443 {value: 'foo2', text: 'bar2'},
15444 {value: 'foo3', text: 'bar3'}
15446 // then append, applying the master template values
15447 t.append('my-form', {name: 'my-select'});
15449 * A name attribute for the child template is not required if you have only one child
15450 * template or you want to refer to them by index.
15452 Roo.MasterTemplate = function(){
15453 Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15454 this.originalHtml = this.html;
15456 var m, re = this.subTemplateRe;
15459 while(m = re.exec(this.html)){
15460 var name = m[1], content = m[2];
15465 tpl : new Roo.Template(content)
15468 st[name] = st[subIndex];
15470 st[subIndex].tpl.compile();
15471 st[subIndex].tpl.call = this.call.createDelegate(this);
15474 this.subCount = subIndex;
15477 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15479 * The regular expression used to match sub templates
15483 subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15486 * Applies the passed values to a child template.
15487 * @param {String/Number} name (optional) The name or index of the child template
15488 * @param {Array/Object} values The values to be applied to the template
15489 * @return {MasterTemplate} this
15491 add : function(name, values){
15492 if(arguments.length == 1){
15493 values = arguments[0];
15496 var s = this.subs[name];
15497 s.buffer[s.buffer.length] = s.tpl.apply(values);
15502 * Applies all the passed values to a child template.
15503 * @param {String/Number} name (optional) The name or index of the child template
15504 * @param {Array} values The values to be applied to the template, this should be an array of objects.
15505 * @param {Boolean} reset (optional) True to reset the template first
15506 * @return {MasterTemplate} this
15508 fill : function(name, values, reset){
15510 if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15518 for(var i = 0, len = values.length; i < len; i++){
15519 this.add(name, values[i]);
15525 * Resets the template for reuse
15526 * @return {MasterTemplate} this
15528 reset : function(){
15530 for(var i = 0; i < this.subCount; i++){
15536 applyTemplate : function(values){
15538 var replaceIndex = -1;
15539 this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15540 return s[++replaceIndex].buffer.join("");
15542 return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15545 apply : function(){
15546 return this.applyTemplate.apply(this, arguments);
15549 compile : function(){return this;}
15553 * Alias for fill().
15556 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15558 * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15559 * var tpl = Roo.MasterTemplate.from('element-id');
15560 * @param {String/HTMLElement} el
15561 * @param {Object} config
15564 Roo.MasterTemplate.from = function(el, config){
15565 el = Roo.getDom(el);
15566 return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15569 * Ext JS Library 1.1.1
15570 * Copyright(c) 2006-2007, Ext JS, LLC.
15572 * Originally Released Under LGPL - original licence link has changed is not relivant.
15575 * <script type="text/javascript">
15580 * @class Roo.util.CSS
15581 * Utility class for manipulating CSS rules
15585 Roo.util.CSS = function(){
15587 var doc = document;
15589 var camelRe = /(-[a-z])/gi;
15590 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15594 * Very simple dynamic creation of stylesheets from a text blob of rules. The text will wrapped in a style
15595 * tag and appended to the HEAD of the document.
15596 * @param {String|Object} cssText The text containing the css rules
15597 * @param {String} id An id to add to the stylesheet for later removal
15598 * @return {StyleSheet}
15600 createStyleSheet : function(cssText, id){
15602 var head = doc.getElementsByTagName("head")[0];
15603 var nrules = doc.createElement("style");
15604 nrules.setAttribute("type", "text/css");
15606 nrules.setAttribute("id", id);
15608 if (typeof(cssText) != 'string') {
15609 // support object maps..
15610 // not sure if this a good idea..
15611 // perhaps it should be merged with the general css handling
15612 // and handle js style props.
15613 var cssTextNew = [];
15614 for(var n in cssText) {
15616 for(var k in cssText[n]) {
15617 citems.push( k + ' : ' +cssText[n][k] + ';' );
15619 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15622 cssText = cssTextNew.join("\n");
15628 head.appendChild(nrules);
15629 ss = nrules.styleSheet;
15630 ss.cssText = cssText;
15633 nrules.appendChild(doc.createTextNode(cssText));
15635 nrules.cssText = cssText;
15637 head.appendChild(nrules);
15638 ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15640 this.cacheStyleSheet(ss);
15645 * Removes a style or link tag by id
15646 * @param {String} id The id of the tag
15648 removeStyleSheet : function(id){
15649 var existing = doc.getElementById(id);
15651 existing.parentNode.removeChild(existing);
15656 * Dynamically swaps an existing stylesheet reference for a new one
15657 * @param {String} id The id of an existing link tag to remove
15658 * @param {String} url The href of the new stylesheet to include
15660 swapStyleSheet : function(id, url){
15661 this.removeStyleSheet(id);
15662 var ss = doc.createElement("link");
15663 ss.setAttribute("rel", "stylesheet");
15664 ss.setAttribute("type", "text/css");
15665 ss.setAttribute("id", id);
15666 ss.setAttribute("href", url);
15667 doc.getElementsByTagName("head")[0].appendChild(ss);
15671 * Refresh the rule cache if you have dynamically added stylesheets
15672 * @return {Object} An object (hash) of rules indexed by selector
15674 refreshCache : function(){
15675 return this.getRules(true);
15679 cacheStyleSheet : function(stylesheet){
15683 try{// try catch for cross domain access issue
15684 var ssRules = stylesheet.cssRules || stylesheet.rules;
15685 for(var j = ssRules.length-1; j >= 0; --j){
15686 rules[ssRules[j].selectorText] = ssRules[j];
15692 * Gets all css rules for the document
15693 * @param {Boolean} refreshCache true to refresh the internal cache
15694 * @return {Object} An object (hash) of rules indexed by selector
15696 getRules : function(refreshCache){
15697 if(rules == null || refreshCache){
15699 var ds = doc.styleSheets;
15700 for(var i =0, len = ds.length; i < len; i++){
15702 this.cacheStyleSheet(ds[i]);
15710 * Gets an an individual CSS rule by selector(s)
15711 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15712 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15713 * @return {CSSRule} The CSS rule or null if one is not found
15715 getRule : function(selector, refreshCache){
15716 var rs = this.getRules(refreshCache);
15717 if(!(selector instanceof Array)){
15718 return rs[selector];
15720 for(var i = 0; i < selector.length; i++){
15721 if(rs[selector[i]]){
15722 return rs[selector[i]];
15730 * Updates a rule property
15731 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15732 * @param {String} property The css property
15733 * @param {String} value The new value for the property
15734 * @return {Boolean} true If a rule was found and updated
15736 updateRule : function(selector, property, value){
15737 if(!(selector instanceof Array)){
15738 var rule = this.getRule(selector);
15740 rule.style[property.replace(camelRe, camelFn)] = value;
15744 for(var i = 0; i < selector.length; i++){
15745 if(this.updateRule(selector[i], property, value)){
15755 * Ext JS Library 1.1.1
15756 * Copyright(c) 2006-2007, Ext JS, LLC.
15758 * Originally Released Under LGPL - original licence link has changed is not relivant.
15761 * <script type="text/javascript">
15767 * @class Roo.util.ClickRepeater
15768 * @extends Roo.util.Observable
15770 * A wrapper class which can be applied to any element. Fires a "click" event while the
15771 * mouse is pressed. The interval between firings may be specified in the config but
15772 * defaults to 10 milliseconds.
15774 * Optionally, a CSS class may be applied to the element during the time it is pressed.
15776 * @cfg {String/HTMLElement/Element} el The element to act as a button.
15777 * @cfg {Number} delay The initial delay before the repeating event begins firing.
15778 * Similar to an autorepeat key delay.
15779 * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15780 * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15781 * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15782 * "interval" and "delay" are ignored. "immediate" is honored.
15783 * @cfg {Boolean} preventDefault True to prevent the default click event
15784 * @cfg {Boolean} stopDefault True to stop the default click event
15787 * 2007-02-02 jvs Original code contributed by Nige "Animal" White
15788 * 2007-02-02 jvs Renamed to ClickRepeater
15789 * 2007-02-03 jvs Modifications for FF Mac and Safari
15792 * @param {String/HTMLElement/Element} el The element to listen on
15793 * @param {Object} config
15795 Roo.util.ClickRepeater = function(el, config)
15797 this.el = Roo.get(el);
15798 this.el.unselectable();
15800 Roo.apply(this, config);
15805 * Fires when the mouse button is depressed.
15806 * @param {Roo.util.ClickRepeater} this
15808 "mousedown" : true,
15811 * Fires on a specified interval during the time the element is pressed.
15812 * @param {Roo.util.ClickRepeater} this
15817 * Fires when the mouse key is released.
15818 * @param {Roo.util.ClickRepeater} this
15823 this.el.on("mousedown", this.handleMouseDown, this);
15824 if(this.preventDefault || this.stopDefault){
15825 this.el.on("click", function(e){
15826 if(this.preventDefault){
15827 e.preventDefault();
15829 if(this.stopDefault){
15835 // allow inline handler
15837 this.on("click", this.handler, this.scope || this);
15840 Roo.util.ClickRepeater.superclass.constructor.call(this);
15843 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15846 preventDefault : true,
15847 stopDefault : false,
15851 handleMouseDown : function(){
15852 clearTimeout(this.timer);
15854 if(this.pressClass){
15855 this.el.addClass(this.pressClass);
15857 this.mousedownTime = new Date();
15859 Roo.get(document).on("mouseup", this.handleMouseUp, this);
15860 this.el.on("mouseout", this.handleMouseOut, this);
15862 this.fireEvent("mousedown", this);
15863 this.fireEvent("click", this);
15865 this.timer = this.click.defer(this.delay || this.interval, this);
15869 click : function(){
15870 this.fireEvent("click", this);
15871 this.timer = this.click.defer(this.getInterval(), this);
15875 getInterval: function(){
15876 if(!this.accelerate){
15877 return this.interval;
15879 var pressTime = this.mousedownTime.getElapsed();
15880 if(pressTime < 500){
15882 }else if(pressTime < 1700){
15884 }else if(pressTime < 2600){
15886 }else if(pressTime < 3500){
15888 }else if(pressTime < 4400){
15890 }else if(pressTime < 5300){
15892 }else if(pressTime < 6200){
15900 handleMouseOut : function(){
15901 clearTimeout(this.timer);
15902 if(this.pressClass){
15903 this.el.removeClass(this.pressClass);
15905 this.el.on("mouseover", this.handleMouseReturn, this);
15909 handleMouseReturn : function(){
15910 this.el.un("mouseover", this.handleMouseReturn);
15911 if(this.pressClass){
15912 this.el.addClass(this.pressClass);
15918 handleMouseUp : function(){
15919 clearTimeout(this.timer);
15920 this.el.un("mouseover", this.handleMouseReturn);
15921 this.el.un("mouseout", this.handleMouseOut);
15922 Roo.get(document).un("mouseup", this.handleMouseUp);
15923 this.el.removeClass(this.pressClass);
15924 this.fireEvent("mouseup", this);
15927 * @class Roo.util.Clipboard
15933 Roo.util.Clipboard = {
15935 * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15936 * @param {String} text to copy to clipboard
15938 write : function(text) {
15939 // navigator clipboard api needs a secure context (https)
15940 if (navigator.clipboard && window.isSecureContext) {
15941 // navigator clipboard api method'
15942 navigator.clipboard.writeText(text);
15945 // text area method
15946 var ta = document.createElement("textarea");
15948 // make the textarea out of viewport
15949 ta.style.position = "fixed";
15950 ta.style.left = "-999999px";
15951 ta.style.top = "-999999px";
15952 document.body.appendChild(ta);
15955 document.execCommand('copy');
15965 * Ext JS Library 1.1.1
15966 * Copyright(c) 2006-2007, Ext JS, LLC.
15968 * Originally Released Under LGPL - original licence link has changed is not relivant.
15971 * <script type="text/javascript">
15976 * @class Roo.KeyNav
15977 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
15978 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15979 * way to implement custom navigation schemes for any UI component.</p>
15980 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15981 * pageUp, pageDown, del, home, end. Usage:</p>
15983 var nav = new Roo.KeyNav("my-element", {
15984 "left" : function(e){
15985 this.moveLeft(e.ctrlKey);
15987 "right" : function(e){
15988 this.moveRight(e.ctrlKey);
15990 "enter" : function(e){
15997 * @param {String/HTMLElement/Roo.Element} el The element to bind to
15998 * @param {Object} config The config
16000 Roo.KeyNav = function(el, config){
16001 this.el = Roo.get(el);
16002 Roo.apply(this, config);
16003 if(!this.disabled){
16004 this.disabled = true;
16009 Roo.KeyNav.prototype = {
16011 * @cfg {Boolean} disabled
16012 * True to disable this KeyNav instance (defaults to false)
16016 * @cfg {String} defaultEventAction
16017 * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key. Valid values are
16018 * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16019 * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16021 defaultEventAction: "stopEvent",
16023 * @cfg {Boolean} forceKeyDown
16024 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
16025 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16026 * handle keydown instead of keypress.
16028 forceKeyDown : false,
16031 prepareEvent : function(e){
16032 var k = e.getKey();
16033 var h = this.keyToHandler[k];
16034 //if(h && this[h]){
16035 // e.stopPropagation();
16037 if(Roo.isSafari && h && k >= 37 && k <= 40){
16043 relay : function(e){
16044 var k = e.getKey();
16045 var h = this.keyToHandler[k];
16047 if(this.doRelay(e, this[h], h) !== true){
16048 e[this.defaultEventAction]();
16054 doRelay : function(e, h, hname){
16055 return h.call(this.scope || this, e);
16058 // possible handlers
16072 // quick lookup hash
16089 * Enable this KeyNav
16091 enable: function(){
16093 // ie won't do special keys on keypress, no one else will repeat keys with keydown
16094 // the EventObject will normalize Safari automatically
16095 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16096 this.el.on("keydown", this.relay, this);
16098 this.el.on("keydown", this.prepareEvent, this);
16099 this.el.on("keypress", this.relay, this);
16101 this.disabled = false;
16106 * Disable this KeyNav
16108 disable: function(){
16109 if(!this.disabled){
16110 if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16111 this.el.un("keydown", this.relay);
16113 this.el.un("keydown", this.prepareEvent);
16114 this.el.un("keypress", this.relay);
16116 this.disabled = true;
16121 * Ext JS Library 1.1.1
16122 * Copyright(c) 2006-2007, Ext JS, LLC.
16124 * Originally Released Under LGPL - original licence link has changed is not relivant.
16127 * <script type="text/javascript">
16132 * @class Roo.KeyMap
16133 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16134 * The constructor accepts the same config object as defined by {@link #addBinding}.
16135 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16136 * combination it will call the function with this signature (if the match is a multi-key
16137 * combination the callback will still be called only once): (String key, Roo.EventObject e)
16138 * A KeyMap can also handle a string representation of keys.<br />
16141 // map one key by key code
16142 var map = new Roo.KeyMap("my-element", {
16143 key: 13, // or Roo.EventObject.ENTER
16148 // map multiple keys to one action by string
16149 var map = new Roo.KeyMap("my-element", {
16155 // map multiple keys to multiple actions by strings and array of codes
16156 var map = new Roo.KeyMap("my-element", [
16159 fn: function(){ alert("Return was pressed"); }
16162 fn: function(){ alert('a, b or c was pressed'); }
16167 fn: function(){ alert('Control + shift + tab was pressed.'); }
16171 * <b>Note: A KeyMap starts enabled</b>
16173 * @param {String/HTMLElement/Roo.Element} el The element to bind to
16174 * @param {Object} config The config (see {@link #addBinding})
16175 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16177 Roo.KeyMap = function(el, config, eventName){
16178 this.el = Roo.get(el);
16179 this.eventName = eventName || "keydown";
16180 this.bindings = [];
16182 this.addBinding(config);
16187 Roo.KeyMap.prototype = {
16189 * True to stop the event from bubbling and prevent the default browser action if the
16190 * key was handled by the KeyMap (defaults to false)
16196 * Add a new binding to this KeyMap. The following config object properties are supported:
16198 Property Type Description
16199 ---------- --------------- ----------------------------------------------------------------------
16200 key String/Array A single keycode or an array of keycodes to handle
16201 shift Boolean True to handle key only when shift is pressed (defaults to false)
16202 ctrl Boolean True to handle key only when ctrl is pressed (defaults to false)
16203 alt Boolean True to handle key only when alt is pressed (defaults to false)
16204 fn Function The function to call when KeyMap finds the expected key combination
16205 scope Object The scope of the callback function
16211 var map = new Roo.KeyMap(document, {
16212 key: Roo.EventObject.ENTER,
16217 //Add a new binding to the existing KeyMap later
16225 * @param {Object/Array} config A single KeyMap config or an array of configs
16227 addBinding : function(config){
16228 if(config instanceof Array){
16229 for(var i = 0, len = config.length; i < len; i++){
16230 this.addBinding(config[i]);
16234 var keyCode = config.key,
16235 shift = config.shift,
16236 ctrl = config.ctrl,
16239 scope = config.scope;
16240 if(typeof keyCode == "string"){
16242 var keyString = keyCode.toUpperCase();
16243 for(var j = 0, len = keyString.length; j < len; j++){
16244 ks.push(keyString.charCodeAt(j));
16248 var keyArray = keyCode instanceof Array;
16249 var handler = function(e){
16250 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
16251 var k = e.getKey();
16253 for(var i = 0, len = keyCode.length; i < len; i++){
16254 if(keyCode[i] == k){
16255 if(this.stopEvent){
16258 fn.call(scope || window, k, e);
16264 if(this.stopEvent){
16267 fn.call(scope || window, k, e);
16272 this.bindings.push(handler);
16276 * Shorthand for adding a single key listener
16277 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16278 * following options:
16279 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16280 * @param {Function} fn The function to call
16281 * @param {Object} scope (optional) The scope of the function
16283 on : function(key, fn, scope){
16284 var keyCode, shift, ctrl, alt;
16285 if(typeof key == "object" && !(key instanceof Array)){
16304 handleKeyDown : function(e){
16305 if(this.enabled){ //just in case
16306 var b = this.bindings;
16307 for(var i = 0, len = b.length; i < len; i++){
16308 b[i].call(this, e);
16314 * Returns true if this KeyMap is enabled
16315 * @return {Boolean}
16317 isEnabled : function(){
16318 return this.enabled;
16322 * Enables this KeyMap
16324 enable: function(){
16326 this.el.on(this.eventName, this.handleKeyDown, this);
16327 this.enabled = true;
16332 * Disable this KeyMap
16334 disable: function(){
16336 this.el.removeListener(this.eventName, this.handleKeyDown, this);
16337 this.enabled = false;
16342 * Ext JS Library 1.1.1
16343 * Copyright(c) 2006-2007, Ext JS, LLC.
16345 * Originally Released Under LGPL - original licence link has changed is not relivant.
16348 * <script type="text/javascript">
16353 * @class Roo.util.TextMetrics
16354 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16355 * wide, in pixels, a given block of text will be.
16358 Roo.util.TextMetrics = function(){
16362 * Measures the size of the specified text
16363 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16364 * that can affect the size of the rendered text
16365 * @param {String} text The text to measure
16366 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16367 * in order to accurately measure the text height
16368 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16370 measure : function(el, text, fixedWidth){
16372 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16375 shared.setFixedWidth(fixedWidth || 'auto');
16376 return shared.getSize(text);
16380 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
16381 * the overhead of multiple calls to initialize the style properties on each measurement.
16382 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16383 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16384 * in order to accurately measure the text height
16385 * @return {Roo.util.TextMetrics.Instance} instance The new instance
16387 createInstance : function(el, fixedWidth){
16388 return Roo.util.TextMetrics.Instance(el, fixedWidth);
16394 * @class Roo.util.TextMetrics.Instance
16395 * Instance of TextMetrics Calcuation
16397 * Create a new TextMetrics Instance
16398 * @param {Object} bindto
16399 * @param {Boolean} fixedWidth
16402 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16404 var ml = new Roo.Element(document.createElement('div'));
16405 document.body.appendChild(ml.dom);
16406 ml.position('absolute');
16407 ml.setLeftTop(-1000, -1000);
16411 ml.setWidth(fixedWidth);
16416 * Returns the size of the specified text based on the internal element's style and width properties
16417 * @param {String} text The text to measure
16418 * @return {Object} An object containing the text's size {width: (width), height: (height)}
16420 getSize : function(text){
16422 var s = ml.getSize();
16428 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16429 * that can affect the size of the rendered text
16430 * @param {String/HTMLElement} el The element, dom node or id
16432 bind : function(el){
16434 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16439 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
16440 * to set a fixed width in order to accurately measure the text height.
16441 * @param {Number} width The width to set on the element
16443 setFixedWidth : function(width){
16444 ml.setWidth(width);
16448 * Returns the measured width of the specified text
16449 * @param {String} text The text to measure
16450 * @return {Number} width The width in pixels
16452 getWidth : function(text){
16453 ml.dom.style.width = 'auto';
16454 return this.getSize(text).width;
16458 * Returns the measured height of the specified text. For multiline text, be sure to call
16459 * {@link #setFixedWidth} if necessary.
16460 * @param {String} text The text to measure
16461 * @return {Number} height The height in pixels
16463 getHeight : function(text){
16464 return this.getSize(text).height;
16468 instance.bind(bindTo);
16473 // backwards compat
16474 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16476 * Ext JS Library 1.1.1
16477 * Copyright(c) 2006-2007, Ext JS, LLC.
16479 * Originally Released Under LGPL - original licence link has changed is not relivant.
16482 * <script type="text/javascript">
16486 * @class Roo.state.Provider
16487 * Abstract base class for state provider implementations. This class provides methods
16488 * for encoding and decoding <b>typed</b> variables including dates and defines the
16489 * Provider interface.
16491 Roo.state.Provider = function(){
16493 * @event statechange
16494 * Fires when a state change occurs.
16495 * @param {Provider} this This state provider
16496 * @param {String} key The state key which was changed
16497 * @param {String} value The encoded value for the state
16500 "statechange": true
16503 Roo.state.Provider.superclass.constructor.call(this);
16505 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16507 * Returns the current value for a key
16508 * @param {String} name The key name
16509 * @param {Mixed} defaultValue A default value to return if the key's value is not found
16510 * @return {Mixed} The state data
16512 get : function(name, defaultValue){
16513 return typeof this.state[name] == "undefined" ?
16514 defaultValue : this.state[name];
16518 * Clears a value from the state
16519 * @param {String} name The key name
16521 clear : function(name){
16522 delete this.state[name];
16523 this.fireEvent("statechange", this, name, null);
16527 * Sets the value for a key
16528 * @param {String} name The key name
16529 * @param {Mixed} value The value to set
16531 set : function(name, value){
16532 this.state[name] = value;
16533 this.fireEvent("statechange", this, name, value);
16537 * Decodes a string previously encoded with {@link #encodeValue}.
16538 * @param {String} value The value to decode
16539 * @return {Mixed} The decoded value
16541 decodeValue : function(cookie){
16542 var re = /^(a|n|d|b|s|o)\:(.*)$/;
16543 var matches = re.exec(unescape(cookie));
16544 if(!matches || !matches[1]) {
16545 return; // non state cookie
16547 var type = matches[1];
16548 var v = matches[2];
16551 return parseFloat(v);
16553 return new Date(Date.parse(v));
16558 var values = v.split("^");
16559 for(var i = 0, len = values.length; i < len; i++){
16560 all.push(this.decodeValue(values[i]));
16565 var values = v.split("^");
16566 for(var i = 0, len = values.length; i < len; i++){
16567 var kv = values[i].split("=");
16568 all[kv[0]] = this.decodeValue(kv[1]);
16577 * Encodes a value including type information. Decode with {@link #decodeValue}.
16578 * @param {Mixed} value The value to encode
16579 * @return {String} The encoded value
16581 encodeValue : function(v){
16583 if(typeof v == "number"){
16585 }else if(typeof v == "boolean"){
16586 enc = "b:" + (v ? "1" : "0");
16587 }else if(v instanceof Date){
16588 enc = "d:" + v.toGMTString();
16589 }else if(v instanceof Array){
16591 for(var i = 0, len = v.length; i < len; i++){
16592 flat += this.encodeValue(v[i]);
16598 }else if(typeof v == "object"){
16601 if(typeof v[key] != "function"){
16602 flat += key + "=" + this.encodeValue(v[key]) + "^";
16605 enc = "o:" + flat.substring(0, flat.length-1);
16609 return escape(enc);
16615 * Ext JS Library 1.1.1
16616 * Copyright(c) 2006-2007, Ext JS, LLC.
16618 * Originally Released Under LGPL - original licence link has changed is not relivant.
16621 * <script type="text/javascript">
16624 * @class Roo.state.Manager
16625 * This is the global state manager. By default all components that are "state aware" check this class
16626 * for state information if you don't pass them a custom state provider. In order for this class
16627 * to be useful, it must be initialized with a provider when your application initializes.
16629 // in your initialization function
16631 Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16633 // supposed you have a {@link Roo.BorderLayout}
16634 var layout = new Roo.BorderLayout(...);
16635 layout.restoreState();
16636 // or a {Roo.BasicDialog}
16637 var dialog = new Roo.BasicDialog(...);
16638 dialog.restoreState();
16642 Roo.state.Manager = function(){
16643 var provider = new Roo.state.Provider();
16647 * Configures the default state provider for your application
16648 * @param {Provider} stateProvider The state provider to set
16650 setProvider : function(stateProvider){
16651 provider = stateProvider;
16655 * Returns the current value for a key
16656 * @param {String} name The key name
16657 * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16658 * @return {Mixed} The state data
16660 get : function(key, defaultValue){
16661 return provider.get(key, defaultValue);
16665 * Sets the value for a key
16666 * @param {String} name The key name
16667 * @param {Mixed} value The state data
16669 set : function(key, value){
16670 provider.set(key, value);
16674 * Clears a value from the state
16675 * @param {String} name The key name
16677 clear : function(key){
16678 provider.clear(key);
16682 * Gets the currently configured state provider
16683 * @return {Provider} The state provider
16685 getProvider : function(){
16692 * Ext JS Library 1.1.1
16693 * Copyright(c) 2006-2007, Ext JS, LLC.
16695 * Originally Released Under LGPL - original licence link has changed is not relivant.
16698 * <script type="text/javascript">
16701 * @class Roo.state.CookieProvider
16702 * @extends Roo.state.Provider
16703 * The default Provider implementation which saves state via cookies.
16706 var cp = new Roo.state.CookieProvider({
16708 expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16709 domain: "roojs.com"
16711 Roo.state.Manager.setProvider(cp);
16713 * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16714 * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16715 * @cfg {String} domain The domain to save the cookie for. Note that you cannot specify a different domain than
16716 * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16717 * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16718 * domain the page is running on including the 'www' like 'www.roojs.com')
16719 * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16721 * Create a new CookieProvider
16722 * @param {Object} config The configuration object
16724 Roo.state.CookieProvider = function(config){
16725 Roo.state.CookieProvider.superclass.constructor.call(this);
16727 this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16728 this.domain = null;
16729 this.secure = false;
16730 Roo.apply(this, config);
16731 this.state = this.readCookies();
16734 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16736 set : function(name, value){
16737 if(typeof value == "undefined" || value === null){
16741 this.setCookie(name, value);
16742 Roo.state.CookieProvider.superclass.set.call(this, name, value);
16746 clear : function(name){
16747 this.clearCookie(name);
16748 Roo.state.CookieProvider.superclass.clear.call(this, name);
16752 readCookies : function(){
16754 var c = document.cookie + ";";
16755 var re = /\s?(.*?)=(.*?);/g;
16757 while((matches = re.exec(c)) != null){
16758 var name = matches[1];
16759 var value = matches[2];
16760 if(name && name.substring(0,3) == "ys-"){
16761 cookies[name.substr(3)] = this.decodeValue(value);
16768 setCookie : function(name, value){
16769 document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16770 ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16771 ((this.path == null) ? "" : ("; path=" + this.path)) +
16772 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16773 ((this.secure == true) ? "; secure" : "");
16777 clearCookie : function(name){
16778 document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16779 ((this.path == null) ? "" : ("; path=" + this.path)) +
16780 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16781 ((this.secure == true) ? "; secure" : "");
16785 * Ext JS Library 1.1.1
16786 * Copyright(c) 2006-2007, Ext JS, LLC.
16788 * Originally Released Under LGPL - original licence link has changed is not relivant.
16791 * <script type="text/javascript">
16796 * @class Roo.ComponentMgr
16797 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16800 Roo.ComponentMgr = function(){
16801 var all = new Roo.util.MixedCollection();
16805 * Registers a component.
16806 * @param {Roo.Component} c The component
16808 register : function(c){
16813 * Unregisters a component.
16814 * @param {Roo.Component} c The component
16816 unregister : function(c){
16821 * Returns a component by id
16822 * @param {String} id The component id
16824 get : function(id){
16825 return all.get(id);
16829 * Registers a function that will be called when a specified component is added to ComponentMgr
16830 * @param {String} id The component id
16831 * @param {Funtction} fn The callback function
16832 * @param {Object} scope The scope of the callback
16834 onAvailable : function(id, fn, scope){
16835 all.on("add", function(index, o){
16837 fn.call(scope || o, o);
16838 all.un("add", fn, scope);
16845 * Ext JS Library 1.1.1
16846 * Copyright(c) 2006-2007, Ext JS, LLC.
16848 * Originally Released Under LGPL - original licence link has changed is not relivant.
16851 * <script type="text/javascript">
16855 * @class Roo.Component
16856 * @extends Roo.util.Observable
16857 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
16858 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
16859 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16860 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16861 * All visual components (widgets) that require rendering into a layout should subclass Component.
16863 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
16864 * 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
16865 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
16867 Roo.Component = function(config){
16868 config = config || {};
16869 if(config.tagName || config.dom || typeof config == "string"){ // element object
16870 config = {el: config, id: config.id || config};
16872 this.initialConfig = config;
16874 Roo.apply(this, config);
16878 * Fires after the component is disabled.
16879 * @param {Roo.Component} this
16884 * Fires after the component is enabled.
16885 * @param {Roo.Component} this
16889 * @event beforeshow
16890 * Fires before the component is shown. Return false to stop the show.
16891 * @param {Roo.Component} this
16896 * Fires after the component is shown.
16897 * @param {Roo.Component} this
16901 * @event beforehide
16902 * Fires before the component is hidden. Return false to stop the hide.
16903 * @param {Roo.Component} this
16908 * Fires after the component is hidden.
16909 * @param {Roo.Component} this
16913 * @event beforerender
16914 * Fires before the component is rendered. Return false to stop the render.
16915 * @param {Roo.Component} this
16917 beforerender : true,
16920 * Fires after the component is rendered.
16921 * @param {Roo.Component} this
16925 * @event beforedestroy
16926 * Fires before the component is destroyed. Return false to stop the destroy.
16927 * @param {Roo.Component} this
16929 beforedestroy : true,
16932 * Fires after the component is destroyed.
16933 * @param {Roo.Component} this
16938 this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16940 Roo.ComponentMgr.register(this);
16941 Roo.Component.superclass.constructor.call(this);
16942 this.initComponent();
16943 if(this.renderTo){ // not supported by all components yet. use at your own risk!
16944 this.render(this.renderTo);
16945 delete this.renderTo;
16950 Roo.Component.AUTO_ID = 1000;
16952 Roo.extend(Roo.Component, Roo.util.Observable, {
16954 * @scope Roo.Component.prototype
16956 * true if this component is hidden. Read-only.
16961 * true if this component is disabled. Read-only.
16966 * true if this component has been rendered. Read-only.
16970 /** @cfg {String} disableClass
16971 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16973 disabledClass : "x-item-disabled",
16974 /** @cfg {Boolean} allowDomMove
16975 * Whether the component can move the Dom node when rendering (defaults to true).
16977 allowDomMove : true,
16978 /** @cfg {String} hideMode (display|visibility)
16979 * How this component should hidden. Supported values are
16980 * "visibility" (css visibility), "offsets" (negative offset position) and
16981 * "display" (css display) - defaults to "display".
16983 hideMode: 'display',
16986 ctype : "Roo.Component",
16989 * @cfg {String} actionMode
16990 * which property holds the element that used for hide() / show() / disable() / enable()
16991 * default is 'el' for forms you probably want to set this to fieldEl
16996 * @cfg {String} style
16997 * css styles to add to component
16998 * eg. text-align:right;
17003 getActionEl : function(){
17004 return this[this.actionMode];
17007 initComponent : Roo.emptyFn,
17009 * If this is a lazy rendering component, render it to its container element.
17010 * @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.
17012 render : function(container, position){
17018 if(this.fireEvent("beforerender", this) === false){
17022 if(!container && this.el){
17023 this.el = Roo.get(this.el);
17024 container = this.el.dom.parentNode;
17025 this.allowDomMove = false;
17027 this.container = Roo.get(container);
17028 this.rendered = true;
17029 if(position !== undefined){
17030 if(typeof position == 'number'){
17031 position = this.container.dom.childNodes[position];
17033 position = Roo.getDom(position);
17036 this.onRender(this.container, position || null);
17038 this.el.addClass(this.cls);
17042 this.el.applyStyles(this.style);
17045 this.fireEvent("render", this);
17046 this.afterRender(this.container);
17059 // default function is not really useful
17060 onRender : function(ct, position){
17062 this.el = Roo.get(this.el);
17063 if(this.allowDomMove !== false){
17064 ct.dom.insertBefore(this.el.dom, position);
17070 getAutoCreate : function(){
17071 var cfg = typeof this.autoCreate == "object" ?
17072 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17073 if(this.id && !cfg.id){
17080 afterRender : Roo.emptyFn,
17083 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17084 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17086 destroy : function(){
17087 if(this.fireEvent("beforedestroy", this) !== false){
17088 this.purgeListeners();
17089 this.beforeDestroy();
17091 this.el.removeAllListeners();
17093 if(this.actionMode == "container"){
17094 this.container.remove();
17098 Roo.ComponentMgr.unregister(this);
17099 this.fireEvent("destroy", this);
17104 beforeDestroy : function(){
17109 onDestroy : function(){
17114 * Returns the underlying {@link Roo.Element}.
17115 * @return {Roo.Element} The element
17117 getEl : function(){
17122 * Returns the id of this component.
17125 getId : function(){
17130 * Try to focus this component.
17131 * @param {Boolean} selectText True to also select the text in this component (if applicable)
17132 * @return {Roo.Component} this
17134 focus : function(selectText){
17137 if(selectText === true){
17138 this.el.dom.select();
17153 * Disable this component.
17154 * @return {Roo.Component} this
17156 disable : function(){
17160 this.disabled = true;
17161 this.fireEvent("disable", this);
17166 onDisable : function(){
17167 this.getActionEl().addClass(this.disabledClass);
17168 this.el.dom.disabled = true;
17172 * Enable this component.
17173 * @return {Roo.Component} this
17175 enable : function(){
17179 this.disabled = false;
17180 this.fireEvent("enable", this);
17185 onEnable : function(){
17186 this.getActionEl().removeClass(this.disabledClass);
17187 this.el.dom.disabled = false;
17191 * Convenience function for setting disabled/enabled by boolean.
17192 * @param {Boolean} disabled
17194 setDisabled : function(disabled){
17195 this[disabled ? "disable" : "enable"]();
17199 * Show this component.
17200 * @return {Roo.Component} this
17203 if(this.fireEvent("beforeshow", this) !== false){
17204 this.hidden = false;
17208 this.fireEvent("show", this);
17214 onShow : function(){
17215 var ae = this.getActionEl();
17216 if(this.hideMode == 'visibility'){
17217 ae.dom.style.visibility = "visible";
17218 }else if(this.hideMode == 'offsets'){
17219 ae.removeClass('x-hidden');
17221 ae.dom.style.display = "";
17226 * Hide this component.
17227 * @return {Roo.Component} this
17230 if(this.fireEvent("beforehide", this) !== false){
17231 this.hidden = true;
17235 this.fireEvent("hide", this);
17241 onHide : function(){
17242 var ae = this.getActionEl();
17243 if(this.hideMode == 'visibility'){
17244 ae.dom.style.visibility = "hidden";
17245 }else if(this.hideMode == 'offsets'){
17246 ae.addClass('x-hidden');
17248 ae.dom.style.display = "none";
17253 * Convenience function to hide or show this component by boolean.
17254 * @param {Boolean} visible True to show, false to hide
17255 * @return {Roo.Component} this
17257 setVisible: function(visible){
17267 * Returns true if this component is visible.
17269 isVisible : function(){
17270 return this.getActionEl().isVisible();
17273 cloneConfig : function(overrides){
17274 overrides = overrides || {};
17275 var id = overrides.id || Roo.id();
17276 var cfg = Roo.applyIf(overrides, this.initialConfig);
17277 cfg.id = id; // prevent dup id
17278 return new this.constructor(cfg);
17282 * Ext JS Library 1.1.1
17283 * Copyright(c) 2006-2007, Ext JS, LLC.
17285 * Originally Released Under LGPL - original licence link has changed is not relivant.
17288 * <script type="text/javascript">
17292 * @class Roo.BoxComponent
17293 * @extends Roo.Component
17294 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
17295 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
17296 * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17297 * layout containers.
17299 * @param {Roo.Element/String/Object} config The configuration options.
17301 Roo.BoxComponent = function(config){
17302 Roo.Component.call(this, config);
17306 * Fires after the component is resized.
17307 * @param {Roo.Component} this
17308 * @param {Number} adjWidth The box-adjusted width that was set
17309 * @param {Number} adjHeight The box-adjusted height that was set
17310 * @param {Number} rawWidth The width that was originally specified
17311 * @param {Number} rawHeight The height that was originally specified
17316 * Fires after the component is moved.
17317 * @param {Roo.Component} this
17318 * @param {Number} x The new x position
17319 * @param {Number} y The new y position
17325 Roo.extend(Roo.BoxComponent, Roo.Component, {
17326 // private, set in afterRender to signify that the component has been rendered
17328 // private, used to defer height settings to subclasses
17329 deferHeight: false,
17330 /** @cfg {Number} width
17331 * width (optional) size of component
17333 /** @cfg {Number} height
17334 * height (optional) size of component
17338 * Sets the width and height of the component. This method fires the resize event. This method can accept
17339 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17340 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17341 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17342 * @return {Roo.BoxComponent} this
17344 setSize : function(w, h){
17345 // support for standard size objects
17346 if(typeof w == 'object'){
17351 if(!this.boxReady){
17357 // prevent recalcs when not needed
17358 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17361 this.lastSize = {width: w, height: h};
17363 var adj = this.adjustSize(w, h);
17364 var aw = adj.width, ah = adj.height;
17365 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17366 var rz = this.getResizeEl();
17367 if(!this.deferHeight && aw !== undefined && ah !== undefined){
17368 rz.setSize(aw, ah);
17369 }else if(!this.deferHeight && ah !== undefined){
17371 }else if(aw !== undefined){
17374 this.onResize(aw, ah, w, h);
17375 this.fireEvent('resize', this, aw, ah, w, h);
17381 * Gets the current size of the component's underlying element.
17382 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17384 getSize : function(){
17385 return this.el.getSize();
17389 * Gets the current XY position of the component's underlying element.
17390 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17391 * @return {Array} The XY position of the element (e.g., [100, 200])
17393 getPosition : function(local){
17394 if(local === true){
17395 return [this.el.getLeft(true), this.el.getTop(true)];
17397 return this.xy || this.el.getXY();
17401 * Gets the current box measurements of the component's underlying element.
17402 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17403 * @returns {Object} box An object in the format {x, y, width, height}
17405 getBox : function(local){
17406 var s = this.el.getSize();
17408 s.x = this.el.getLeft(true);
17409 s.y = this.el.getTop(true);
17411 var xy = this.xy || this.el.getXY();
17419 * Sets the current box measurements of the component's underlying element.
17420 * @param {Object} box An object in the format {x, y, width, height}
17421 * @returns {Roo.BoxComponent} this
17423 updateBox : function(box){
17424 this.setSize(box.width, box.height);
17425 this.setPagePosition(box.x, box.y);
17430 getResizeEl : function(){
17431 return this.resizeEl || this.el;
17435 getPositionEl : function(){
17436 return this.positionEl || this.el;
17440 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
17441 * This method fires the move event.
17442 * @param {Number} left The new left
17443 * @param {Number} top The new top
17444 * @returns {Roo.BoxComponent} this
17446 setPosition : function(x, y){
17449 if(!this.boxReady){
17452 var adj = this.adjustPosition(x, y);
17453 var ax = adj.x, ay = adj.y;
17455 var el = this.getPositionEl();
17456 if(ax !== undefined || ay !== undefined){
17457 if(ax !== undefined && ay !== undefined){
17458 el.setLeftTop(ax, ay);
17459 }else if(ax !== undefined){
17461 }else if(ay !== undefined){
17464 this.onPosition(ax, ay);
17465 this.fireEvent('move', this, ax, ay);
17471 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
17472 * This method fires the move event.
17473 * @param {Number} x The new x position
17474 * @param {Number} y The new y position
17475 * @returns {Roo.BoxComponent} this
17477 setPagePosition : function(x, y){
17480 if(!this.boxReady){
17483 if(x === undefined || y === undefined){ // cannot translate undefined points
17486 var p = this.el.translatePoints(x, y);
17487 this.setPosition(p.left, p.top);
17492 onRender : function(ct, position){
17493 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17495 this.resizeEl = Roo.get(this.resizeEl);
17497 if(this.positionEl){
17498 this.positionEl = Roo.get(this.positionEl);
17503 afterRender : function(){
17504 Roo.BoxComponent.superclass.afterRender.call(this);
17505 this.boxReady = true;
17506 this.setSize(this.width, this.height);
17507 if(this.x || this.y){
17508 this.setPosition(this.x, this.y);
17510 if(this.pageX || this.pageY){
17511 this.setPagePosition(this.pageX, this.pageY);
17516 * Force the component's size to recalculate based on the underlying element's current height and width.
17517 * @returns {Roo.BoxComponent} this
17519 syncSize : function(){
17520 delete this.lastSize;
17521 this.setSize(this.el.getWidth(), this.el.getHeight());
17526 * Called after the component is resized, this method is empty by default but can be implemented by any
17527 * subclass that needs to perform custom logic after a resize occurs.
17528 * @param {Number} adjWidth The box-adjusted width that was set
17529 * @param {Number} adjHeight The box-adjusted height that was set
17530 * @param {Number} rawWidth The width that was originally specified
17531 * @param {Number} rawHeight The height that was originally specified
17533 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17538 * Called after the component is moved, this method is empty by default but can be implemented by any
17539 * subclass that needs to perform custom logic after a move occurs.
17540 * @param {Number} x The new x position
17541 * @param {Number} y The new y position
17543 onPosition : function(x, y){
17548 adjustSize : function(w, h){
17549 if(this.autoWidth){
17552 if(this.autoHeight){
17555 return {width : w, height: h};
17559 adjustPosition : function(x, y){
17560 return {x : x, y: y};
17564 * Ext JS Library 1.1.1
17565 * Copyright(c) 2006-2007, Ext JS, LLC.
17567 * Originally Released Under LGPL - original licence link has changed is not relivant.
17570 * <script type="text/javascript">
17575 * @extends Roo.Element
17576 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17577 * automatic maintaining of shadow/shim positions.
17578 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17579 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17580 * you can pass a string with a CSS class name. False turns off the shadow.
17581 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17582 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17583 * @cfg {String} cls CSS class to add to the element
17584 * @cfg {Number} zindex Starting z-index (defaults to 11000)
17585 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17587 * @param {Object} config An object with config options.
17588 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17591 Roo.Layer = function(config, existingEl){
17592 config = config || {};
17593 var dh = Roo.DomHelper;
17594 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17596 this.dom = Roo.getDom(existingEl);
17599 var o = config.dh || {tag: "div", cls: "x-layer"};
17600 this.dom = dh.append(pel, o);
17603 this.addClass(config.cls);
17605 this.constrain = config.constrain !== false;
17606 this.visibilityMode = Roo.Element.VISIBILITY;
17608 this.id = this.dom.id = config.id;
17610 this.id = Roo.id(this.dom);
17612 this.zindex = config.zindex || this.getZIndex();
17613 this.position("absolute", this.zindex);
17615 this.shadowOffset = config.shadowOffset || 4;
17616 this.shadow = new Roo.Shadow({
17617 offset : this.shadowOffset,
17618 mode : config.shadow
17621 this.shadowOffset = 0;
17623 this.useShim = config.shim !== false && Roo.useShims;
17624 this.useDisplay = config.useDisplay;
17628 var supr = Roo.Element.prototype;
17630 // shims are shared among layer to keep from having 100 iframes
17633 Roo.extend(Roo.Layer, Roo.Element, {
17635 getZIndex : function(){
17636 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17639 getShim : function(){
17646 var shim = shims.shift();
17648 shim = this.createShim();
17649 shim.enableDisplayMode('block');
17650 shim.dom.style.display = 'none';
17651 shim.dom.style.visibility = 'visible';
17653 var pn = this.dom.parentNode;
17654 if(shim.dom.parentNode != pn){
17655 pn.insertBefore(shim.dom, this.dom);
17657 shim.setStyle('z-index', this.getZIndex()-2);
17662 hideShim : function(){
17664 this.shim.setDisplayed(false);
17665 shims.push(this.shim);
17670 disableShadow : function(){
17672 this.shadowDisabled = true;
17673 this.shadow.hide();
17674 this.lastShadowOffset = this.shadowOffset;
17675 this.shadowOffset = 0;
17679 enableShadow : function(show){
17681 this.shadowDisabled = false;
17682 this.shadowOffset = this.lastShadowOffset;
17683 delete this.lastShadowOffset;
17691 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17692 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17693 sync : function(doShow){
17694 var sw = this.shadow;
17695 if(!this.updating && this.isVisible() && (sw || this.useShim)){
17696 var sh = this.getShim();
17698 var w = this.getWidth(),
17699 h = this.getHeight();
17701 var l = this.getLeft(true),
17702 t = this.getTop(true);
17704 if(sw && !this.shadowDisabled){
17705 if(doShow && !sw.isVisible()){
17708 sw.realign(l, t, w, h);
17714 // fit the shim behind the shadow, so it is shimmed too
17715 var a = sw.adjusts, s = sh.dom.style;
17716 s.left = (Math.min(l, l+a.l))+"px";
17717 s.top = (Math.min(t, t+a.t))+"px";
17718 s.width = (w+a.w)+"px";
17719 s.height = (h+a.h)+"px";
17726 sh.setLeftTop(l, t);
17733 destroy : function(){
17736 this.shadow.hide();
17738 this.removeAllListeners();
17739 var pn = this.dom.parentNode;
17741 pn.removeChild(this.dom);
17743 Roo.Element.uncache(this.id);
17746 remove : function(){
17751 beginUpdate : function(){
17752 this.updating = true;
17756 endUpdate : function(){
17757 this.updating = false;
17762 hideUnders : function(negOffset){
17764 this.shadow.hide();
17770 constrainXY : function(){
17771 if(this.constrain){
17772 var vw = Roo.lib.Dom.getViewWidth(),
17773 vh = Roo.lib.Dom.getViewHeight();
17774 var s = Roo.get(document).getScroll();
17776 var xy = this.getXY();
17777 var x = xy[0], y = xy[1];
17778 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17779 // only move it if it needs it
17781 // first validate right/bottom
17782 if((x + w) > vw+s.left){
17783 x = vw - w - this.shadowOffset;
17786 if((y + h) > vh+s.top){
17787 y = vh - h - this.shadowOffset;
17790 // then make sure top/left isn't negative
17801 var ay = this.avoidY;
17802 if(y <= ay && (y+h) >= ay){
17808 supr.setXY.call(this, xy);
17814 isVisible : function(){
17815 return this.visible;
17819 showAction : function(){
17820 this.visible = true; // track visibility to prevent getStyle calls
17821 if(this.useDisplay === true){
17822 this.setDisplayed("");
17823 }else if(this.lastXY){
17824 supr.setXY.call(this, this.lastXY);
17825 }else if(this.lastLT){
17826 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17831 hideAction : function(){
17832 this.visible = false;
17833 if(this.useDisplay === true){
17834 this.setDisplayed(false);
17836 this.setLeftTop(-10000,-10000);
17840 // overridden Element method
17841 setVisible : function(v, a, d, c, e){
17846 var cb = function(){
17851 }.createDelegate(this);
17852 supr.setVisible.call(this, true, true, d, cb, e);
17855 this.hideUnders(true);
17864 }.createDelegate(this);
17866 supr.setVisible.call(this, v, a, d, cb, e);
17875 storeXY : function(xy){
17876 delete this.lastLT;
17880 storeLeftTop : function(left, top){
17881 delete this.lastXY;
17882 this.lastLT = [left, top];
17886 beforeFx : function(){
17887 this.beforeAction();
17888 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17892 afterFx : function(){
17893 Roo.Layer.superclass.afterFx.apply(this, arguments);
17894 this.sync(this.isVisible());
17898 beforeAction : function(){
17899 if(!this.updating && this.shadow){
17900 this.shadow.hide();
17904 // overridden Element method
17905 setLeft : function(left){
17906 this.storeLeftTop(left, this.getTop(true));
17907 supr.setLeft.apply(this, arguments);
17911 setTop : function(top){
17912 this.storeLeftTop(this.getLeft(true), top);
17913 supr.setTop.apply(this, arguments);
17917 setLeftTop : function(left, top){
17918 this.storeLeftTop(left, top);
17919 supr.setLeftTop.apply(this, arguments);
17923 setXY : function(xy, a, d, c, e){
17925 this.beforeAction();
17927 var cb = this.createCB(c);
17928 supr.setXY.call(this, xy, a, d, cb, e);
17935 createCB : function(c){
17946 // overridden Element method
17947 setX : function(x, a, d, c, e){
17948 this.setXY([x, this.getY()], a, d, c, e);
17951 // overridden Element method
17952 setY : function(y, a, d, c, e){
17953 this.setXY([this.getX(), y], a, d, c, e);
17956 // overridden Element method
17957 setSize : function(w, h, a, d, c, e){
17958 this.beforeAction();
17959 var cb = this.createCB(c);
17960 supr.setSize.call(this, w, h, a, d, cb, e);
17966 // overridden Element method
17967 setWidth : function(w, a, d, c, e){
17968 this.beforeAction();
17969 var cb = this.createCB(c);
17970 supr.setWidth.call(this, w, a, d, cb, e);
17976 // overridden Element method
17977 setHeight : function(h, a, d, c, e){
17978 this.beforeAction();
17979 var cb = this.createCB(c);
17980 supr.setHeight.call(this, h, a, d, cb, e);
17986 // overridden Element method
17987 setBounds : function(x, y, w, h, a, d, c, e){
17988 this.beforeAction();
17989 var cb = this.createCB(c);
17991 this.storeXY([x, y]);
17992 supr.setXY.call(this, [x, y]);
17993 supr.setSize.call(this, w, h, a, d, cb, e);
17996 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
18002 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18003 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18004 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18005 * @param {Number} zindex The new z-index to set
18006 * @return {this} The Layer
18008 setZIndex : function(zindex){
18009 this.zindex = zindex;
18010 this.setStyle("z-index", zindex + 2);
18012 this.shadow.setZIndex(zindex + 1);
18015 this.shim.setStyle("z-index", zindex);
18020 * Original code for Roojs - LGPL
18021 * <script type="text/javascript">
18025 * @class Roo.XComponent
18026 * A delayed Element creator...
18027 * Or a way to group chunks of interface together.
18028 * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18029 * used in conjunction with XComponent.build() it will create an instance of each element,
18030 * then call addxtype() to build the User interface.
18032 * Mypart.xyx = new Roo.XComponent({
18034 parent : 'Mypart.xyz', // empty == document.element.!!
18038 disabled : function() {}
18040 tree : function() { // return an tree of xtype declared components
18044 xtype : 'NestedLayoutPanel',
18051 * It can be used to build a big heiracy, with parent etc.
18052 * or you can just use this to render a single compoent to a dom element
18053 * MYPART.render(Roo.Element | String(id) | dom_element )
18060 * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18061 * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18063 * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18065 * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18066 * - if mulitple topModules exist, the last one is defined as the top module.
18070 * When the top level or multiple modules are to embedded into a existing HTML page,
18071 * the parent element can container '#id' of the element where the module will be drawn.
18075 * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18076 * it relies more on a include mechanism, where sub modules are included into an outer page.
18077 * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18079 * Bootstrap Roo Included elements
18081 * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18082 * hence confusing the component builder as it thinks there are multiple top level elements.
18084 * String Over-ride & Translations
18086 * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18087 * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18088 * are needed. @see Roo.XComponent.overlayString
18092 * @extends Roo.util.Observable
18094 * @param cfg {Object} configuration of component
18097 Roo.XComponent = function(cfg) {
18098 Roo.apply(this, cfg);
18102 * Fires when this the componnt is built
18103 * @param {Roo.XComponent} c the component
18108 this.region = this.region || 'center'; // default..
18109 Roo.XComponent.register(this);
18110 this.modules = false;
18111 this.el = false; // where the layout goes..
18115 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18118 * The created element (with Roo.factory())
18119 * @type {Roo.Layout}
18125 * for BC - use el in new code
18126 * @type {Roo.Layout}
18132 * for BC - use el in new code
18133 * @type {Roo.Layout}
18138 * @cfg {Function|boolean} disabled
18139 * If this module is disabled by some rule, return true from the funtion
18144 * @cfg {String} parent
18145 * Name of parent element which it get xtype added to..
18150 * @cfg {String} order
18151 * Used to set the order in which elements are created (usefull for multiple tabs)
18156 * @cfg {String} name
18157 * String to display while loading.
18161 * @cfg {String} region
18162 * Region to render component to (defaults to center)
18167 * @cfg {Array} items
18168 * A single item array - the first element is the root of the tree..
18169 * It's done this way to stay compatible with the Xtype system...
18175 * The method that retuns the tree of parts that make up this compoennt
18182 * render element to dom or tree
18183 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18186 render : function(el)
18190 var hp = this.parent ? 1 : 0;
18191 Roo.debug && Roo.log(this);
18193 var tree = this._tree ? this._tree() : this.tree();
18196 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18197 // if parent is a '#.....' string, then let's use that..
18198 var ename = this.parent.substr(1);
18199 this.parent = false;
18200 Roo.debug && Roo.log(ename);
18202 case 'bootstrap-body':
18203 if (typeof(tree.el) != 'undefined' && tree.el == document.body) {
18204 // this is the BorderLayout standard?
18205 this.parent = { el : true };
18208 if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype) > -1) {
18209 // need to insert stuff...
18211 el : new Roo.bootstrap.layout.Border({
18212 el : document.body,
18218 tabPosition: 'top',
18219 //resizeTabs: true,
18220 alwaysShowTabs: true,
18230 if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18231 this.parent = { el : new Roo.bootstrap.Body() };
18232 Roo.debug && Roo.log("setting el to doc body");
18235 throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18239 this.parent = { el : true};
18242 el = Roo.get(ename);
18243 if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18244 this.parent = { el : true};
18251 if (!el && !this.parent) {
18252 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18257 Roo.debug && Roo.log("EL:");
18258 Roo.debug && Roo.log(el);
18259 Roo.debug && Roo.log("this.parent.el:");
18260 Roo.debug && Roo.log(this.parent.el);
18263 // altertive root elements ??? - we need a better way to indicate these.
18264 var is_alt = Roo.XComponent.is_alt ||
18265 (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18266 (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18267 (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18271 if (!this.parent && is_alt) {
18272 //el = Roo.get(document.body);
18273 this.parent = { el : true };
18278 if (!this.parent) {
18280 Roo.debug && Roo.log("no parent - creating one");
18282 el = el ? Roo.get(el) : false;
18284 if (typeof(Roo.BorderLayout) == 'undefined' ) {
18287 el : new Roo.bootstrap.layout.Border({
18288 el: el || document.body,
18294 tabPosition: 'top',
18295 //resizeTabs: true,
18296 alwaysShowTabs: false,
18299 overflow: 'visible'
18305 // it's a top level one..
18307 el : new Roo.BorderLayout(el || document.body, {
18312 tabPosition: 'top',
18313 //resizeTabs: true,
18314 alwaysShowTabs: el && hp? false : true,
18315 hideTabs: el || !hp ? true : false,
18323 if (!this.parent.el) {
18324 // probably an old style ctor, which has been disabled.
18328 // The 'tree' method is '_tree now'
18330 tree.region = tree.region || this.region;
18331 var is_body = false;
18332 if (this.parent.el === true) {
18333 // bootstrap... - body..
18337 this.parent.el = Roo.factory(tree);
18341 this.el = this.parent.el.addxtype(tree, undefined, is_body);
18342 this.fireEvent('built', this);
18344 this.panel = this.el;
18345 this.layout = this.panel.layout;
18346 this.parentLayout = this.parent.layout || false;
18352 Roo.apply(Roo.XComponent, {
18354 * @property hideProgress
18355 * true to disable the building progress bar.. usefull on single page renders.
18358 hideProgress : false,
18360 * @property buildCompleted
18361 * True when the builder has completed building the interface.
18364 buildCompleted : false,
18367 * @property topModule
18368 * the upper most module - uses document.element as it's constructor.
18375 * @property modules
18376 * array of modules to be created by registration system.
18377 * @type {Array} of Roo.XComponent
18382 * @property elmodules
18383 * array of modules to be created by which use #ID
18384 * @type {Array} of Roo.XComponent
18391 * Is an alternative Root - normally used by bootstrap or other systems,
18392 * where the top element in the tree can wrap 'body'
18393 * @type {boolean} (default false)
18398 * @property build_from_html
18399 * Build elements from html - used by bootstrap HTML stuff
18400 * - this is cleared after build is completed
18401 * @type {boolean} (default false)
18404 build_from_html : false,
18406 * Register components to be built later.
18408 * This solves the following issues
18409 * - Building is not done on page load, but after an authentication process has occured.
18410 * - Interface elements are registered on page load
18411 * - Parent Interface elements may not be loaded before child, so this handles that..
18418 module : 'Pman.Tab.projectMgr',
18420 parent : 'Pman.layout',
18421 disabled : false, // or use a function..
18424 * * @param {Object} details about module
18426 register : function(obj) {
18428 Roo.XComponent.event.fireEvent('register', obj);
18429 switch(typeof(obj.disabled) ) {
18435 if ( obj.disabled() ) {
18441 if (obj.disabled || obj.region == '#disabled') {
18447 this.modules.push(obj);
18451 * convert a string to an object..
18452 * eg. 'AAA.BBB' -> finds AAA.BBB
18456 toObject : function(str)
18458 if (!str || typeof(str) == 'object') {
18461 if (str.substring(0,1) == '#') {
18465 var ar = str.split('.');
18470 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18472 throw "Module not found : " + str;
18476 throw "Module not found : " + str;
18478 Roo.each(ar, function(e) {
18479 if (typeof(o[e]) == 'undefined') {
18480 throw "Module not found : " + str;
18491 * move modules into their correct place in the tree..
18494 preBuild : function ()
18497 Roo.each(this.modules , function (obj)
18499 Roo.XComponent.event.fireEvent('beforebuild', obj);
18501 var opar = obj.parent;
18503 obj.parent = this.toObject(opar);
18505 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18510 Roo.debug && Roo.log("GOT top level module");
18511 Roo.debug && Roo.log(obj);
18512 obj.modules = new Roo.util.MixedCollection(false,
18513 function(o) { return o.order + '' }
18515 this.topModule = obj;
18518 // parent is a string (usually a dom element name..)
18519 if (typeof(obj.parent) == 'string') {
18520 this.elmodules.push(obj);
18523 if (obj.parent.constructor != Roo.XComponent) {
18524 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18526 if (!obj.parent.modules) {
18527 obj.parent.modules = new Roo.util.MixedCollection(false,
18528 function(o) { return o.order + '' }
18531 if (obj.parent.disabled) {
18532 obj.disabled = true;
18534 obj.parent.modules.add(obj);
18539 * make a list of modules to build.
18540 * @return {Array} list of modules.
18543 buildOrder : function()
18546 var cmp = function(a,b) {
18547 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18549 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18550 throw "No top level modules to build";
18553 // make a flat list in order of modules to build.
18554 var mods = this.topModule ? [ this.topModule ] : [];
18557 // elmodules (is a list of DOM based modules )
18558 Roo.each(this.elmodules, function(e) {
18560 if (!this.topModule &&
18561 typeof(e.parent) == 'string' &&
18562 e.parent.substring(0,1) == '#' &&
18563 Roo.get(e.parent.substr(1))
18566 _this.topModule = e;
18572 // add modules to their parents..
18573 var addMod = function(m) {
18574 Roo.debug && Roo.log("build Order: add: " + m.name);
18577 if (m.modules && !m.disabled) {
18578 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18579 m.modules.keySort('ASC', cmp );
18580 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18582 m.modules.each(addMod);
18584 Roo.debug && Roo.log("build Order: no child modules");
18586 // not sure if this is used any more..
18588 m.finalize.name = m.name + " (clean up) ";
18589 mods.push(m.finalize);
18593 if (this.topModule && this.topModule.modules) {
18594 this.topModule.modules.keySort('ASC', cmp );
18595 this.topModule.modules.each(addMod);
18601 * Build the registered modules.
18602 * @param {Object} parent element.
18603 * @param {Function} optional method to call after module has been added.
18607 build : function(opts)
18610 if (typeof(opts) != 'undefined') {
18611 Roo.apply(this,opts);
18615 var mods = this.buildOrder();
18617 //this.allmods = mods;
18618 //Roo.debug && Roo.log(mods);
18620 if (!mods.length) { // should not happen
18621 throw "NO modules!!!";
18625 var msg = "Building Interface...";
18626 // flash it up as modal - so we store the mask!?
18627 if (!this.hideProgress && Roo.MessageBox) {
18628 Roo.MessageBox.show({ title: 'loading' });
18629 Roo.MessageBox.show({
18630 title: "Please wait...",
18640 var total = mods.length;
18643 var progressRun = function() {
18644 if (!mods.length) {
18645 Roo.debug && Roo.log('hide?');
18646 if (!this.hideProgress && Roo.MessageBox) {
18647 Roo.MessageBox.hide();
18649 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18651 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18657 var m = mods.shift();
18660 Roo.debug && Roo.log(m);
18661 // not sure if this is supported any more.. - modules that are are just function
18662 if (typeof(m) == 'function') {
18664 return progressRun.defer(10, _this);
18668 msg = "Building Interface " + (total - mods.length) +
18670 (m.name ? (' - ' + m.name) : '');
18671 Roo.debug && Roo.log(msg);
18672 if (!_this.hideProgress && Roo.MessageBox) {
18673 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
18677 // is the module disabled?
18678 var disabled = (typeof(m.disabled) == 'function') ?
18679 m.disabled.call(m.module.disabled) : m.disabled;
18683 return progressRun(); // we do not update the display!
18691 // it's 10 on top level, and 1 on others??? why...
18692 return progressRun.defer(10, _this);
18695 progressRun.defer(1, _this);
18701 * Overlay a set of modified strings onto a component
18702 * This is dependant on our builder exporting the strings and 'named strings' elements.
18704 * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18705 * @param {Object} associative array of 'named' string and it's new value.
18708 overlayStrings : function( component, strings )
18710 if (typeof(component['_named_strings']) == 'undefined') {
18711 throw "ERROR: component does not have _named_strings";
18713 for ( var k in strings ) {
18714 var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18715 if (md !== false) {
18716 component['_strings'][md] = strings[k];
18718 Roo.log('could not find named string: ' + k + ' in');
18719 Roo.log(component);
18734 * wrapper for event.on - aliased later..
18735 * Typically use to register a event handler for register:
18737 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18746 Roo.XComponent.event = new Roo.util.Observable({
18750 * Fires when an Component is registered,
18751 * set the disable property on the Component to stop registration.
18752 * @param {Roo.XComponent} c the component being registerd.
18757 * @event beforebuild
18758 * Fires before each Component is built
18759 * can be used to apply permissions.
18760 * @param {Roo.XComponent} c the component being registerd.
18763 'beforebuild' : true,
18765 * @event buildcomplete
18766 * Fires on the top level element when all elements have been built
18767 * @param {Roo.XComponent} the top level component.
18769 'buildcomplete' : true
18774 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
18777 * marked - a markdown parser
18778 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18779 * https://github.com/chjj/marked
18785 * Roo.Markdown - is a very crude wrapper around marked..
18789 * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18791 * Note: move the sample code to the bottom of this
18792 * file before uncommenting it.
18797 Roo.Markdown.toHtml = function(text) {
18799 var c = new Roo.Markdown.marked.setOptions({
18800 renderer: new Roo.Markdown.marked.Renderer(),
18811 text = text.replace(/\\\n/g,' ');
18812 return Roo.Markdown.marked(text);
18817 // Wraps all "globals" so that the only thing
18818 // exposed is makeHtml().
18824 * eval:var:unescape
18832 var escape = function (html, encode) {
18834 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
18835 .replace(/</g, '<')
18836 .replace(/>/g, '>')
18837 .replace(/"/g, '"')
18838 .replace(/'/g, ''');
18841 var unescape = function (html) {
18842 // explicitly match decimal, hex, and named HTML entities
18843 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18844 n = n.toLowerCase();
18845 if (n === 'colon') { return ':'; }
18846 if (n.charAt(0) === '#') {
18847 return n.charAt(1) === 'x'
18848 ? String.fromCharCode(parseInt(n.substring(2), 16))
18849 : String.fromCharCode(+n.substring(1));
18855 var replace = function (regex, opt) {
18856 regex = regex.source;
18858 return function self(name, val) {
18859 if (!name) { return new RegExp(regex, opt); }
18860 val = val.source || val;
18861 val = val.replace(/(^|[^\[])\^/g, '$1');
18862 regex = regex.replace(name, val);
18871 var noop = function () {}
18877 var merge = function (obj) {
18882 for (; i < arguments.length; i++) {
18883 target = arguments[i];
18884 for (key in target) {
18885 if (Object.prototype.hasOwnProperty.call(target, key)) {
18886 obj[key] = target[key];
18896 * Block-Level Grammar
18904 code: /^( {4}[^\n]+\n*)+/,
18906 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18907 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18909 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18910 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18911 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18912 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18913 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18915 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18919 block.bullet = /(?:[*+-]|\d+\.)/;
18920 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18921 block.item = replace(block.item, 'gm')
18922 (/bull/g, block.bullet)
18925 block.list = replace(block.list)
18926 (/bull/g, block.bullet)
18927 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18928 ('def', '\\n+(?=' + block.def.source + ')')
18931 block.blockquote = replace(block.blockquote)
18935 block._tag = '(?!(?:'
18936 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18937 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18938 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18940 block.html = replace(block.html)
18941 ('comment', /<!--[\s\S]*?-->/)
18942 ('closed', /<(tag)[\s\S]+?<\/\1>/)
18943 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18944 (/tag/g, block._tag)
18947 block.paragraph = replace(block.paragraph)
18949 ('heading', block.heading)
18950 ('lheading', block.lheading)
18951 ('blockquote', block.blockquote)
18952 ('tag', '<' + block._tag)
18957 * Normal Block Grammar
18960 block.normal = merge({}, block);
18963 * GFM Block Grammar
18966 block.gfm = merge({}, block.normal, {
18967 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18969 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18972 block.gfm.paragraph = replace(block.paragraph)
18974 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18975 + block.list.source.replace('\\1', '\\3') + '|')
18979 * GFM + Tables Block Grammar
18982 block.tables = merge({}, block.gfm, {
18983 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18984 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18991 var Lexer = function (options) {
18993 this.tokens.links = {};
18994 this.options = options || marked.defaults;
18995 this.rules = block.normal;
18997 if (this.options.gfm) {
18998 if (this.options.tables) {
18999 this.rules = block.tables;
19001 this.rules = block.gfm;
19007 * Expose Block Rules
19010 Lexer.rules = block;
19013 * Static Lex Method
19016 Lexer.lex = function(src, options) {
19017 var lexer = new Lexer(options);
19018 return lexer.lex(src);
19025 Lexer.prototype.lex = function(src) {
19027 .replace(/\r\n|\r/g, '\n')
19028 .replace(/\t/g, ' ')
19029 .replace(/\u00a0/g, ' ')
19030 .replace(/\u2424/g, '\n');
19032 return this.token(src, true);
19039 Lexer.prototype.token = function(src, top, bq) {
19040 var src = src.replace(/^ +$/gm, '')
19053 if (cap = this.rules.newline.exec(src)) {
19054 src = src.substring(cap[0].length);
19055 if (cap[0].length > 1) {
19063 if (cap = this.rules.code.exec(src)) {
19064 src = src.substring(cap[0].length);
19065 cap = cap[0].replace(/^ {4}/gm, '');
19068 text: !this.options.pedantic
19069 ? cap.replace(/\n+$/, '')
19076 if (cap = this.rules.fences.exec(src)) {
19077 src = src.substring(cap[0].length);
19087 if (cap = this.rules.heading.exec(src)) {
19088 src = src.substring(cap[0].length);
19091 depth: cap[1].length,
19097 // table no leading pipe (gfm)
19098 if (top && (cap = this.rules.nptable.exec(src))) {
19099 src = src.substring(cap[0].length);
19103 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19104 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19105 cells: cap[3].replace(/\n$/, '').split('\n')
19108 for (i = 0; i < item.align.length; i++) {
19109 if (/^ *-+: *$/.test(item.align[i])) {
19110 item.align[i] = 'right';
19111 } else if (/^ *:-+: *$/.test(item.align[i])) {
19112 item.align[i] = 'center';
19113 } else if (/^ *:-+ *$/.test(item.align[i])) {
19114 item.align[i] = 'left';
19116 item.align[i] = null;
19120 for (i = 0; i < item.cells.length; i++) {
19121 item.cells[i] = item.cells[i].split(/ *\| */);
19124 this.tokens.push(item);
19130 if (cap = this.rules.lheading.exec(src)) {
19131 src = src.substring(cap[0].length);
19134 depth: cap[2] === '=' ? 1 : 2,
19141 if (cap = this.rules.hr.exec(src)) {
19142 src = src.substring(cap[0].length);
19150 if (cap = this.rules.blockquote.exec(src)) {
19151 src = src.substring(cap[0].length);
19154 type: 'blockquote_start'
19157 cap = cap[0].replace(/^ *> ?/gm, '');
19159 // Pass `top` to keep the current
19160 // "toplevel" state. This is exactly
19161 // how markdown.pl works.
19162 this.token(cap, top, true);
19165 type: 'blockquote_end'
19172 if (cap = this.rules.list.exec(src)) {
19173 src = src.substring(cap[0].length);
19177 type: 'list_start',
19178 ordered: bull.length > 1
19181 // Get each top-level item.
19182 cap = cap[0].match(this.rules.item);
19188 for (; i < l; i++) {
19191 // Remove the list item's bullet
19192 // so it is seen as the next token.
19193 space = item.length;
19194 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19196 // Outdent whatever the
19197 // list item contains. Hacky.
19198 if (~item.indexOf('\n ')) {
19199 space -= item.length;
19200 item = !this.options.pedantic
19201 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19202 : item.replace(/^ {1,4}/gm, '');
19205 // Determine whether the next list item belongs here.
19206 // Backpedal if it does not belong in this list.
19207 if (this.options.smartLists && i !== l - 1) {
19208 b = block.bullet.exec(cap[i + 1])[0];
19209 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19210 src = cap.slice(i + 1).join('\n') + src;
19215 // Determine whether item is loose or not.
19216 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19217 // for discount behavior.
19218 loose = next || /\n\n(?!\s*$)/.test(item);
19220 next = item.charAt(item.length - 1) === '\n';
19221 if (!loose) { loose = next; }
19226 ? 'loose_item_start'
19227 : 'list_item_start'
19231 this.token(item, false, bq);
19234 type: 'list_item_end'
19246 if (cap = this.rules.html.exec(src)) {
19247 src = src.substring(cap[0].length);
19249 type: this.options.sanitize
19252 pre: !this.options.sanitizer
19253 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19260 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19261 src = src.substring(cap[0].length);
19262 this.tokens.links[cap[1].toLowerCase()] = {
19270 if (top && (cap = this.rules.table.exec(src))) {
19271 src = src.substring(cap[0].length);
19275 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19276 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19277 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19280 for (i = 0; i < item.align.length; i++) {
19281 if (/^ *-+: *$/.test(item.align[i])) {
19282 item.align[i] = 'right';
19283 } else if (/^ *:-+: *$/.test(item.align[i])) {
19284 item.align[i] = 'center';
19285 } else if (/^ *:-+ *$/.test(item.align[i])) {
19286 item.align[i] = 'left';
19288 item.align[i] = null;
19292 for (i = 0; i < item.cells.length; i++) {
19293 item.cells[i] = item.cells[i]
19294 .replace(/^ *\| *| *\| *$/g, '')
19298 this.tokens.push(item);
19303 // top-level paragraph
19304 if (top && (cap = this.rules.paragraph.exec(src))) {
19305 src = src.substring(cap[0].length);
19308 text: cap[1].charAt(cap[1].length - 1) === '\n'
19309 ? cap[1].slice(0, -1)
19316 if (cap = this.rules.text.exec(src)) {
19317 // Top-level should never reach here.
19318 src = src.substring(cap[0].length);
19328 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19332 return this.tokens;
19336 * Inline-Level Grammar
19340 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19341 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19343 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19344 link: /^!?\[(inside)\]\(href\)/,
19345 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19346 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19347 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19348 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19349 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19350 br: /^ {2,}\n(?!\s*$)/,
19352 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19355 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19356 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19358 inline.link = replace(inline.link)
19359 ('inside', inline._inside)
19360 ('href', inline._href)
19363 inline.reflink = replace(inline.reflink)
19364 ('inside', inline._inside)
19368 * Normal Inline Grammar
19371 inline.normal = merge({}, inline);
19374 * Pedantic Inline Grammar
19377 inline.pedantic = merge({}, inline.normal, {
19378 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19379 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19383 * GFM Inline Grammar
19386 inline.gfm = merge({}, inline.normal, {
19387 escape: replace(inline.escape)('])', '~|])')(),
19388 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19389 del: /^~~(?=\S)([\s\S]*?\S)~~/,
19390 text: replace(inline.text)
19392 ('|', '|https?://|')
19397 * GFM + Line Breaks Inline Grammar
19400 inline.breaks = merge({}, inline.gfm, {
19401 br: replace(inline.br)('{2,}', '*')(),
19402 text: replace(inline.gfm.text)('{2,}', '*')()
19406 * Inline Lexer & Compiler
19409 var InlineLexer = function (links, options) {
19410 this.options = options || marked.defaults;
19411 this.links = links;
19412 this.rules = inline.normal;
19413 this.renderer = this.options.renderer || new Renderer;
19414 this.renderer.options = this.options;
19418 Error('Tokens array requires a `links` property.');
19421 if (this.options.gfm) {
19422 if (this.options.breaks) {
19423 this.rules = inline.breaks;
19425 this.rules = inline.gfm;
19427 } else if (this.options.pedantic) {
19428 this.rules = inline.pedantic;
19433 * Expose Inline Rules
19436 InlineLexer.rules = inline;
19439 * Static Lexing/Compiling Method
19442 InlineLexer.output = function(src, links, options) {
19443 var inline = new InlineLexer(links, options);
19444 return inline.output(src);
19451 InlineLexer.prototype.output = function(src) {
19460 if (cap = this.rules.escape.exec(src)) {
19461 src = src.substring(cap[0].length);
19467 if (cap = this.rules.autolink.exec(src)) {
19468 src = src.substring(cap[0].length);
19469 if (cap[2] === '@') {
19470 text = cap[1].charAt(6) === ':'
19471 ? this.mangle(cap[1].substring(7))
19472 : this.mangle(cap[1]);
19473 href = this.mangle('mailto:') + text;
19475 text = escape(cap[1]);
19478 out += this.renderer.link(href, null, text);
19483 if (!this.inLink && (cap = this.rules.url.exec(src))) {
19484 src = src.substring(cap[0].length);
19485 text = escape(cap[1]);
19487 out += this.renderer.link(href, null, text);
19492 if (cap = this.rules.tag.exec(src)) {
19493 if (!this.inLink && /^<a /i.test(cap[0])) {
19494 this.inLink = true;
19495 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19496 this.inLink = false;
19498 src = src.substring(cap[0].length);
19499 out += this.options.sanitize
19500 ? this.options.sanitizer
19501 ? this.options.sanitizer(cap[0])
19508 if (cap = this.rules.link.exec(src)) {
19509 src = src.substring(cap[0].length);
19510 this.inLink = true;
19511 out += this.outputLink(cap, {
19515 this.inLink = false;
19520 if ((cap = this.rules.reflink.exec(src))
19521 || (cap = this.rules.nolink.exec(src))) {
19522 src = src.substring(cap[0].length);
19523 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19524 link = this.links[link.toLowerCase()];
19525 if (!link || !link.href) {
19526 out += cap[0].charAt(0);
19527 src = cap[0].substring(1) + src;
19530 this.inLink = true;
19531 out += this.outputLink(cap, link);
19532 this.inLink = false;
19537 if (cap = this.rules.strong.exec(src)) {
19538 src = src.substring(cap[0].length);
19539 out += this.renderer.strong(this.output(cap[2] || cap[1]));
19544 if (cap = this.rules.em.exec(src)) {
19545 src = src.substring(cap[0].length);
19546 out += this.renderer.em(this.output(cap[2] || cap[1]));
19551 if (cap = this.rules.code.exec(src)) {
19552 src = src.substring(cap[0].length);
19553 out += this.renderer.codespan(escape(cap[2], true));
19558 if (cap = this.rules.br.exec(src)) {
19559 src = src.substring(cap[0].length);
19560 out += this.renderer.br();
19565 if (cap = this.rules.del.exec(src)) {
19566 src = src.substring(cap[0].length);
19567 out += this.renderer.del(this.output(cap[1]));
19572 if (cap = this.rules.text.exec(src)) {
19573 src = src.substring(cap[0].length);
19574 out += this.renderer.text(escape(this.smartypants(cap[0])));
19580 Error('Infinite loop on byte: ' + src.charCodeAt(0));
19591 InlineLexer.prototype.outputLink = function(cap, link) {
19592 var href = escape(link.href)
19593 , title = link.title ? escape(link.title) : null;
19595 return cap[0].charAt(0) !== '!'
19596 ? this.renderer.link(href, title, this.output(cap[1]))
19597 : this.renderer.image(href, title, escape(cap[1]));
19601 * Smartypants Transformations
19604 InlineLexer.prototype.smartypants = function(text) {
19605 if (!this.options.smartypants) { return text; }
19608 .replace(/---/g, '\u2014')
19610 .replace(/--/g, '\u2013')
19612 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19613 // closing singles & apostrophes
19614 .replace(/'/g, '\u2019')
19616 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19618 .replace(/"/g, '\u201d')
19620 .replace(/\.{3}/g, '\u2026');
19627 InlineLexer.prototype.mangle = function(text) {
19628 if (!this.options.mangle) { return text; }
19634 for (; i < l; i++) {
19635 ch = text.charCodeAt(i);
19636 if (Math.random() > 0.5) {
19637 ch = 'x' + ch.toString(16);
19639 out += '&#' + ch + ';';
19650 * eval:var:Renderer
19653 var Renderer = function (options) {
19654 this.options = options || {};
19657 Renderer.prototype.code = function(code, lang, escaped) {
19658 if (this.options.highlight) {
19659 var out = this.options.highlight(code, lang);
19660 if (out != null && out !== code) {
19665 // hack!!! - it's already escapeD?
19670 return '<pre><code>'
19671 + (escaped ? code : escape(code, true))
19672 + '\n</code></pre>';
19675 return '<pre><code class="'
19676 + this.options.langPrefix
19677 + escape(lang, true)
19679 + (escaped ? code : escape(code, true))
19680 + '\n</code></pre>\n';
19683 Renderer.prototype.blockquote = function(quote) {
19684 return '<blockquote>\n' + quote + '</blockquote>\n';
19687 Renderer.prototype.html = function(html) {
19691 Renderer.prototype.heading = function(text, level, raw) {
19695 + this.options.headerPrefix
19696 + raw.toLowerCase().replace(/[^\w]+/g, '-')
19704 Renderer.prototype.hr = function() {
19705 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19708 Renderer.prototype.list = function(body, ordered) {
19709 var type = ordered ? 'ol' : 'ul';
19710 return '<' + type + '>\n' + body + '</' + type + '>\n';
19713 Renderer.prototype.listitem = function(text) {
19714 return '<li>' + text + '</li>\n';
19717 Renderer.prototype.paragraph = function(text) {
19718 return '<p>' + text + '</p>\n';
19721 Renderer.prototype.table = function(header, body) {
19722 return '<table class="table table-striped">\n'
19732 Renderer.prototype.tablerow = function(content) {
19733 return '<tr>\n' + content + '</tr>\n';
19736 Renderer.prototype.tablecell = function(content, flags) {
19737 var type = flags.header ? 'th' : 'td';
19738 var tag = flags.align
19739 ? '<' + type + ' style="text-align:' + flags.align + '">'
19740 : '<' + type + '>';
19741 return tag + content + '</' + type + '>\n';
19744 // span level renderer
19745 Renderer.prototype.strong = function(text) {
19746 return '<strong>' + text + '</strong>';
19749 Renderer.prototype.em = function(text) {
19750 return '<em>' + text + '</em>';
19753 Renderer.prototype.codespan = function(text) {
19754 return '<code>' + text + '</code>';
19757 Renderer.prototype.br = function() {
19758 return this.options.xhtml ? '<br/>' : '<br>';
19761 Renderer.prototype.del = function(text) {
19762 return '<del>' + text + '</del>';
19765 Renderer.prototype.link = function(href, title, text) {
19766 if (this.options.sanitize) {
19768 var prot = decodeURIComponent(unescape(href))
19769 .replace(/[^\w:]/g, '')
19774 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19778 var out = '<a href="' + href + '"';
19780 out += ' title="' + title + '"';
19782 out += '>' + text + '</a>';
19786 Renderer.prototype.image = function(href, title, text) {
19787 var out = '<img src="' + href + '" alt="' + text + '"';
19789 out += ' title="' + title + '"';
19791 out += this.options.xhtml ? '/>' : '>';
19795 Renderer.prototype.text = function(text) {
19800 * Parsing & Compiling
19806 var Parser= function (options) {
19809 this.options = options || marked.defaults;
19810 this.options.renderer = this.options.renderer || new Renderer;
19811 this.renderer = this.options.renderer;
19812 this.renderer.options = this.options;
19816 * Static Parse Method
19819 Parser.parse = function(src, options, renderer) {
19820 var parser = new Parser(options, renderer);
19821 return parser.parse(src);
19828 Parser.prototype.parse = function(src) {
19829 this.inline = new InlineLexer(src.links, this.options, this.renderer);
19830 this.tokens = src.reverse();
19833 while (this.next()) {
19844 Parser.prototype.next = function() {
19845 return this.token = this.tokens.pop();
19849 * Preview Next Token
19852 Parser.prototype.peek = function() {
19853 return this.tokens[this.tokens.length - 1] || 0;
19857 * Parse Text Tokens
19860 Parser.prototype.parseText = function() {
19861 var body = this.token.text;
19863 while (this.peek().type === 'text') {
19864 body += '\n' + this.next().text;
19867 return this.inline.output(body);
19871 * Parse Current Token
19874 Parser.prototype.tok = function() {
19875 switch (this.token.type) {
19880 return this.renderer.hr();
19883 return this.renderer.heading(
19884 this.inline.output(this.token.text),
19889 return this.renderer.code(this.token.text,
19891 this.token.escaped);
19904 for (i = 0; i < this.token.header.length; i++) {
19905 flags = { header: true, align: this.token.align[i] };
19906 cell += this.renderer.tablecell(
19907 this.inline.output(this.token.header[i]),
19908 { header: true, align: this.token.align[i] }
19911 header += this.renderer.tablerow(cell);
19913 for (i = 0; i < this.token.cells.length; i++) {
19914 row = this.token.cells[i];
19917 for (j = 0; j < row.length; j++) {
19918 cell += this.renderer.tablecell(
19919 this.inline.output(row[j]),
19920 { header: false, align: this.token.align[j] }
19924 body += this.renderer.tablerow(cell);
19926 return this.renderer.table(header, body);
19928 case 'blockquote_start': {
19931 while (this.next().type !== 'blockquote_end') {
19932 body += this.tok();
19935 return this.renderer.blockquote(body);
19937 case 'list_start': {
19939 , ordered = this.token.ordered;
19941 while (this.next().type !== 'list_end') {
19942 body += this.tok();
19945 return this.renderer.list(body, ordered);
19947 case 'list_item_start': {
19950 while (this.next().type !== 'list_item_end') {
19951 body += this.token.type === 'text'
19956 return this.renderer.listitem(body);
19958 case 'loose_item_start': {
19961 while (this.next().type !== 'list_item_end') {
19962 body += this.tok();
19965 return this.renderer.listitem(body);
19968 var html = !this.token.pre && !this.options.pedantic
19969 ? this.inline.output(this.token.text)
19971 return this.renderer.html(html);
19973 case 'paragraph': {
19974 return this.renderer.paragraph(this.inline.output(this.token.text));
19977 return this.renderer.paragraph(this.parseText());
19989 var marked = function (src, opt, callback) {
19990 if (callback || typeof opt === 'function') {
19996 opt = merge({}, marked.defaults, opt || {});
19998 var highlight = opt.highlight
20004 tokens = Lexer.lex(src, opt)
20006 return callback(e);
20009 pending = tokens.length;
20013 var done = function(err) {
20015 opt.highlight = highlight;
20016 return callback(err);
20022 out = Parser.parse(tokens, opt);
20027 opt.highlight = highlight;
20031 : callback(null, out);
20034 if (!highlight || highlight.length < 3) {
20038 delete opt.highlight;
20040 if (!pending) { return done(); }
20042 for (; i < tokens.length; i++) {
20044 if (token.type !== 'code') {
20045 return --pending || done();
20047 return highlight(token.text, token.lang, function(err, code) {
20048 if (err) { return done(err); }
20049 if (code == null || code === token.text) {
20050 return --pending || done();
20053 token.escaped = true;
20054 --pending || done();
20062 if (opt) { opt = merge({}, marked.defaults, opt); }
20063 return Parser.parse(Lexer.lex(src, opt), opt);
20065 e.message += '\nPlease report this to https://github.com/chjj/marked.';
20066 if ((opt || marked.defaults).silent) {
20067 return '<p>An error occured:</p><pre>'
20068 + escape(e.message + '', true)
20080 marked.setOptions = function(opt) {
20081 merge(marked.defaults, opt);
20085 marked.defaults = {
20096 langPrefix: 'lang-',
20097 smartypants: false,
20099 renderer: new Renderer,
20107 marked.Parser = Parser;
20108 marked.parser = Parser.parse;
20110 marked.Renderer = Renderer;
20112 marked.Lexer = Lexer;
20113 marked.lexer = Lexer.lex;
20115 marked.InlineLexer = InlineLexer;
20116 marked.inlineLexer = InlineLexer.output;
20118 marked.parse = marked;
20120 Roo.Markdown.marked = marked;
20124 * Ext JS Library 1.1.1
20125 * Copyright(c) 2006-2007, Ext JS, LLC.
20127 * Originally Released Under LGPL - original licence link has changed is not relivant.
20130 * <script type="text/javascript">
20136 * These classes are derivatives of the similarly named classes in the YUI Library.
20137 * The original license:
20138 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20139 * Code licensed under the BSD License:
20140 * http://developer.yahoo.net/yui/license.txt
20145 var Event=Roo.EventManager;
20146 var Dom=Roo.lib.Dom;
20149 * @class Roo.dd.DragDrop
20150 * @extends Roo.util.Observable
20151 * Defines the interface and base operation of items that that can be
20152 * dragged or can be drop targets. It was designed to be extended, overriding
20153 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20154 * Up to three html elements can be associated with a DragDrop instance:
20156 * <li>linked element: the element that is passed into the constructor.
20157 * This is the element which defines the boundaries for interaction with
20158 * other DragDrop objects.</li>
20159 * <li>handle element(s): The drag operation only occurs if the element that
20160 * was clicked matches a handle element. By default this is the linked
20161 * element, but there are times that you will want only a portion of the
20162 * linked element to initiate the drag operation, and the setHandleElId()
20163 * method provides a way to define this.</li>
20164 * <li>drag element: this represents the element that would be moved along
20165 * with the cursor during a drag operation. By default, this is the linked
20166 * element itself as in {@link Roo.dd.DD}. setDragElId() lets you define
20167 * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20170 * This class should not be instantiated until the onload event to ensure that
20171 * the associated elements are available.
20172 * The following would define a DragDrop obj that would interact with any
20173 * other DragDrop obj in the "group1" group:
20175 * dd = new Roo.dd.DragDrop("div1", "group1");
20177 * Since none of the event handlers have been implemented, nothing would
20178 * actually happen if you were to run the code above. Normally you would
20179 * override this class or one of the default implementations, but you can
20180 * also override the methods you want on an instance of the class...
20182 * dd.onDragDrop = function(e, id) {
20183 * alert("dd was dropped on " + id);
20187 * @param {String} id of the element that is linked to this instance
20188 * @param {String} sGroup the group of related DragDrop objects
20189 * @param {object} config an object containing configurable attributes
20190 * Valid properties for DragDrop:
20191 * padding, isTarget, maintainOffset, primaryButtonOnly
20193 Roo.dd.DragDrop = function(id, sGroup, config) {
20195 this.init(id, sGroup, config);
20200 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20203 * The id of the element associated with this object. This is what we
20204 * refer to as the "linked element" because the size and position of
20205 * this element is used to determine when the drag and drop objects have
20213 * Configuration attributes passed into the constructor
20220 * The id of the element that will be dragged. By default this is same
20221 * as the linked element , but could be changed to another element. Ex:
20223 * @property dragElId
20230 * the id of the element that initiates the drag operation. By default
20231 * this is the linked element, but could be changed to be a child of this
20232 * element. This lets us do things like only starting the drag when the
20233 * header element within the linked html element is clicked.
20234 * @property handleElId
20241 * An associative array of HTML tags that will be ignored if clicked.
20242 * @property invalidHandleTypes
20243 * @type {string: string}
20245 invalidHandleTypes: null,
20248 * An associative array of ids for elements that will be ignored if clicked
20249 * @property invalidHandleIds
20250 * @type {string: string}
20252 invalidHandleIds: null,
20255 * An indexted array of css class names for elements that will be ignored
20257 * @property invalidHandleClasses
20260 invalidHandleClasses: null,
20263 * The linked element's absolute X position at the time the drag was
20265 * @property startPageX
20272 * The linked element's absolute X position at the time the drag was
20274 * @property startPageY
20281 * The group defines a logical collection of DragDrop objects that are
20282 * related. Instances only get events when interacting with other
20283 * DragDrop object in the same group. This lets us define multiple
20284 * groups using a single DragDrop subclass if we want.
20286 * @type {string: string}
20291 * Individual drag/drop instances can be locked. This will prevent
20292 * onmousedown start drag.
20300 * Lock this instance
20303 lock: function() { this.locked = true; },
20306 * Unlock this instace
20309 unlock: function() { this.locked = false; },
20312 * By default, all insances can be a drop target. This can be disabled by
20313 * setting isTarget to false.
20320 * The padding configured for this drag and drop object for calculating
20321 * the drop zone intersection with this object.
20328 * Cached reference to the linked element
20329 * @property _domRef
20335 * Internal typeof flag
20336 * @property __ygDragDrop
20339 __ygDragDrop: true,
20342 * Set to true when horizontal contraints are applied
20343 * @property constrainX
20350 * Set to true when vertical contraints are applied
20351 * @property constrainY
20358 * The left constraint
20366 * The right constraint
20374 * The up constraint
20383 * The down constraint
20391 * Maintain offsets when we resetconstraints. Set to true when you want
20392 * the position of the element relative to its parent to stay the same
20393 * when the page changes
20395 * @property maintainOffset
20398 maintainOffset: false,
20401 * Array of pixel locations the element will snap to if we specified a
20402 * horizontal graduation/interval. This array is generated automatically
20403 * when you define a tick interval.
20410 * Array of pixel locations the element will snap to if we specified a
20411 * vertical graduation/interval. This array is generated automatically
20412 * when you define a tick interval.
20419 * By default the drag and drop instance will only respond to the primary
20420 * button click (left button for a right-handed mouse). Set to true to
20421 * allow drag and drop to start with any mouse click that is propogated
20423 * @property primaryButtonOnly
20426 primaryButtonOnly: true,
20429 * The availabe property is false until the linked dom element is accessible.
20430 * @property available
20436 * By default, drags can only be initiated if the mousedown occurs in the
20437 * region the linked element is. This is done in part to work around a
20438 * bug in some browsers that mis-report the mousedown if the previous
20439 * mouseup happened outside of the window. This property is set to true
20440 * if outer handles are defined.
20442 * @property hasOuterHandles
20446 hasOuterHandles: false,
20449 * Code that executes immediately before the startDrag event
20450 * @method b4StartDrag
20453 b4StartDrag: function(x, y) { },
20456 * Abstract method called after a drag/drop object is clicked
20457 * and the drag or mousedown time thresholds have beeen met.
20458 * @method startDrag
20459 * @param {int} X click location
20460 * @param {int} Y click location
20462 startDrag: function(x, y) { /* override this */ },
20465 * Code that executes immediately before the onDrag event
20469 b4Drag: function(e) { },
20472 * Abstract method called during the onMouseMove event while dragging an
20475 * @param {Event} e the mousemove event
20477 onDrag: function(e) { /* override this */ },
20480 * Abstract method called when this element fist begins hovering over
20481 * another DragDrop obj
20482 * @method onDragEnter
20483 * @param {Event} e the mousemove event
20484 * @param {String|DragDrop[]} id In POINT mode, the element
20485 * id this is hovering over. In INTERSECT mode, an array of one or more
20486 * dragdrop items being hovered over.
20488 onDragEnter: function(e, id) { /* override this */ },
20491 * Code that executes immediately before the onDragOver event
20492 * @method b4DragOver
20495 b4DragOver: function(e) { },
20498 * Abstract method called when this element is hovering over another
20500 * @method onDragOver
20501 * @param {Event} e the mousemove event
20502 * @param {String|DragDrop[]} id In POINT mode, the element
20503 * id this is hovering over. In INTERSECT mode, an array of dd items
20504 * being hovered over.
20506 onDragOver: function(e, id) { /* override this */ },
20509 * Code that executes immediately before the onDragOut event
20510 * @method b4DragOut
20513 b4DragOut: function(e) { },
20516 * Abstract method called when we are no longer hovering over an element
20517 * @method onDragOut
20518 * @param {Event} e the mousemove event
20519 * @param {String|DragDrop[]} id In POINT mode, the element
20520 * id this was hovering over. In INTERSECT mode, an array of dd items
20521 * that the mouse is no longer over.
20523 onDragOut: function(e, id) { /* override this */ },
20526 * Code that executes immediately before the onDragDrop event
20527 * @method b4DragDrop
20530 b4DragDrop: function(e) { },
20533 * Abstract method called when this item is dropped on another DragDrop
20535 * @method onDragDrop
20536 * @param {Event} e the mouseup event
20537 * @param {String|DragDrop[]} id In POINT mode, the element
20538 * id this was dropped on. In INTERSECT mode, an array of dd items this
20541 onDragDrop: function(e, id) { /* override this */ },
20544 * Abstract method called when this item is dropped on an area with no
20546 * @method onInvalidDrop
20547 * @param {Event} e the mouseup event
20549 onInvalidDrop: function(e) { /* override this */ },
20552 * Code that executes immediately before the endDrag event
20553 * @method b4EndDrag
20556 b4EndDrag: function(e) { },
20559 * Fired when we are done dragging the object
20561 * @param {Event} e the mouseup event
20563 endDrag: function(e) { /* override this */ },
20566 * Code executed immediately before the onMouseDown event
20567 * @method b4MouseDown
20568 * @param {Event} e the mousedown event
20571 b4MouseDown: function(e) { },
20574 * Event handler that fires when a drag/drop obj gets a mousedown
20575 * @method onMouseDown
20576 * @param {Event} e the mousedown event
20578 onMouseDown: function(e) { /* override this */ },
20581 * Event handler that fires when a drag/drop obj gets a mouseup
20582 * @method onMouseUp
20583 * @param {Event} e the mouseup event
20585 onMouseUp: function(e) { /* override this */ },
20588 * Override the onAvailable method to do what is needed after the initial
20589 * position was determined.
20590 * @method onAvailable
20592 onAvailable: function () {
20596 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20599 defaultPadding : {left:0, right:0, top:0, bottom:0},
20602 * Initializes the drag drop object's constraints to restrict movement to a certain element.
20606 var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20607 { dragElId: "existingProxyDiv" });
20608 dd.startDrag = function(){
20609 this.constrainTo("parent-id");
20612 * Or you can initalize it using the {@link Roo.Element} object:
20614 Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20615 startDrag : function(){
20616 this.constrainTo("parent-id");
20620 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20621 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20622 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20623 * an object containing the sides to pad. For example: {right:10, bottom:10}
20624 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20626 constrainTo : function(constrainTo, pad, inContent){
20627 if(typeof pad == "number"){
20628 pad = {left: pad, right:pad, top:pad, bottom:pad};
20630 pad = pad || this.defaultPadding;
20631 var b = Roo.get(this.getEl()).getBox();
20632 var ce = Roo.get(constrainTo);
20633 var s = ce.getScroll();
20634 var c, cd = ce.dom;
20635 if(cd == document.body){
20636 c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20639 c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20643 var topSpace = b.y - c.y;
20644 var leftSpace = b.x - c.x;
20646 this.resetConstraints();
20647 this.setXConstraint(leftSpace - (pad.left||0), // left
20648 c.width - leftSpace - b.width - (pad.right||0) //right
20650 this.setYConstraint(topSpace - (pad.top||0), //top
20651 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20656 * Returns a reference to the linked element
20658 * @return {HTMLElement} the html element
20660 getEl: function() {
20661 if (!this._domRef) {
20662 this._domRef = Roo.getDom(this.id);
20665 return this._domRef;
20669 * Returns a reference to the actual element to drag. By default this is
20670 * the same as the html element, but it can be assigned to another
20671 * element. An example of this can be found in Roo.dd.DDProxy
20672 * @method getDragEl
20673 * @return {HTMLElement} the html element
20675 getDragEl: function() {
20676 return Roo.getDom(this.dragElId);
20680 * Sets up the DragDrop object. Must be called in the constructor of any
20681 * Roo.dd.DragDrop subclass
20683 * @param id the id of the linked element
20684 * @param {String} sGroup the group of related items
20685 * @param {object} config configuration attributes
20687 init: function(id, sGroup, config) {
20688 this.initTarget(id, sGroup, config);
20689 if (!Roo.isTouch) {
20690 Event.on(this.id, "mousedown", this.handleMouseDown, this);
20692 Event.on(this.id, "touchstart", this.handleMouseDown, this);
20693 // Event.on(this.id, "selectstart", Event.preventDefault);
20697 * Initializes Targeting functionality only... the object does not
20698 * get a mousedown handler.
20699 * @method initTarget
20700 * @param id the id of the linked element
20701 * @param {String} sGroup the group of related items
20702 * @param {object} config configuration attributes
20704 initTarget: function(id, sGroup, config) {
20706 // configuration attributes
20707 this.config = config || {};
20709 // create a local reference to the drag and drop manager
20710 this.DDM = Roo.dd.DDM;
20711 // initialize the groups array
20714 // assume that we have an element reference instead of an id if the
20715 // parameter is not a string
20716 if (typeof id !== "string") {
20723 // add to an interaction group
20724 this.addToGroup((sGroup) ? sGroup : "default");
20726 // We don't want to register this as the handle with the manager
20727 // so we just set the id rather than calling the setter.
20728 this.handleElId = id;
20730 // the linked element is the element that gets dragged by default
20731 this.setDragElId(id);
20733 // by default, clicked anchors will not start drag operations.
20734 this.invalidHandleTypes = { A: "A" };
20735 this.invalidHandleIds = {};
20736 this.invalidHandleClasses = [];
20738 this.applyConfig();
20740 this.handleOnAvailable();
20744 * Applies the configuration parameters that were passed into the constructor.
20745 * This is supposed to happen at each level through the inheritance chain. So
20746 * a DDProxy implentation will execute apply config on DDProxy, DD, and
20747 * DragDrop in order to get all of the parameters that are available in
20749 * @method applyConfig
20751 applyConfig: function() {
20753 // configurable properties:
20754 // padding, isTarget, maintainOffset, primaryButtonOnly
20755 this.padding = this.config.padding || [0, 0, 0, 0];
20756 this.isTarget = (this.config.isTarget !== false);
20757 this.maintainOffset = (this.config.maintainOffset);
20758 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20763 * Executed when the linked element is available
20764 * @method handleOnAvailable
20767 handleOnAvailable: function() {
20768 this.available = true;
20769 this.resetConstraints();
20770 this.onAvailable();
20774 * Configures the padding for the target zone in px. Effectively expands
20775 * (or reduces) the virtual object size for targeting calculations.
20776 * Supports css-style shorthand; if only one parameter is passed, all sides
20777 * will have that padding, and if only two are passed, the top and bottom
20778 * will have the first param, the left and right the second.
20779 * @method setPadding
20780 * @param {int} iTop Top pad
20781 * @param {int} iRight Right pad
20782 * @param {int} iBot Bot pad
20783 * @param {int} iLeft Left pad
20785 setPadding: function(iTop, iRight, iBot, iLeft) {
20786 // this.padding = [iLeft, iRight, iTop, iBot];
20787 if (!iRight && 0 !== iRight) {
20788 this.padding = [iTop, iTop, iTop, iTop];
20789 } else if (!iBot && 0 !== iBot) {
20790 this.padding = [iTop, iRight, iTop, iRight];
20792 this.padding = [iTop, iRight, iBot, iLeft];
20797 * Stores the initial placement of the linked element.
20798 * @method setInitialPosition
20799 * @param {int} diffX the X offset, default 0
20800 * @param {int} diffY the Y offset, default 0
20802 setInitPosition: function(diffX, diffY) {
20803 var el = this.getEl();
20805 if (!this.DDM.verifyEl(el)) {
20809 var dx = diffX || 0;
20810 var dy = diffY || 0;
20812 var p = Dom.getXY( el );
20814 this.initPageX = p[0] - dx;
20815 this.initPageY = p[1] - dy;
20817 this.lastPageX = p[0];
20818 this.lastPageY = p[1];
20821 this.setStartPosition(p);
20825 * Sets the start position of the element. This is set when the obj
20826 * is initialized, the reset when a drag is started.
20827 * @method setStartPosition
20828 * @param pos current position (from previous lookup)
20831 setStartPosition: function(pos) {
20832 var p = pos || Dom.getXY( this.getEl() );
20833 this.deltaSetXY = null;
20835 this.startPageX = p[0];
20836 this.startPageY = p[1];
20840 * Add this instance to a group of related drag/drop objects. All
20841 * instances belong to at least one group, and can belong to as many
20842 * groups as needed.
20843 * @method addToGroup
20844 * @param sGroup {string} the name of the group
20846 addToGroup: function(sGroup) {
20847 this.groups[sGroup] = true;
20848 this.DDM.regDragDrop(this, sGroup);
20852 * Remove's this instance from the supplied interaction group
20853 * @method removeFromGroup
20854 * @param {string} sGroup The group to drop
20856 removeFromGroup: function(sGroup) {
20857 if (this.groups[sGroup]) {
20858 delete this.groups[sGroup];
20861 this.DDM.removeDDFromGroup(this, sGroup);
20865 * Allows you to specify that an element other than the linked element
20866 * will be moved with the cursor during a drag
20867 * @method setDragElId
20868 * @param id {string} the id of the element that will be used to initiate the drag
20870 setDragElId: function(id) {
20871 this.dragElId = id;
20875 * Allows you to specify a child of the linked element that should be
20876 * used to initiate the drag operation. An example of this would be if
20877 * you have a content div with text and links. Clicking anywhere in the
20878 * content area would normally start the drag operation. Use this method
20879 * to specify that an element inside of the content div is the element
20880 * that starts the drag operation.
20881 * @method setHandleElId
20882 * @param id {string} the id of the element that will be used to
20883 * initiate the drag.
20885 setHandleElId: function(id) {
20886 if (typeof id !== "string") {
20889 this.handleElId = id;
20890 this.DDM.regHandle(this.id, id);
20894 * Allows you to set an element outside of the linked element as a drag
20896 * @method setOuterHandleElId
20897 * @param id the id of the element that will be used to initiate the drag
20899 setOuterHandleElId: function(id) {
20900 if (typeof id !== "string") {
20903 Event.on(id, "mousedown",
20904 this.handleMouseDown, this);
20905 this.setHandleElId(id);
20907 this.hasOuterHandles = true;
20911 * Remove all drag and drop hooks for this element
20914 unreg: function() {
20915 Event.un(this.id, "mousedown",
20916 this.handleMouseDown);
20917 Event.un(this.id, "touchstart",
20918 this.handleMouseDown);
20919 this._domRef = null;
20920 this.DDM._remove(this);
20923 destroy : function(){
20928 * Returns true if this instance is locked, or the drag drop mgr is locked
20929 * (meaning that all drag/drop is disabled on the page.)
20931 * @return {boolean} true if this obj or all drag/drop is locked, else
20934 isLocked: function() {
20935 return (this.DDM.isLocked() || this.locked);
20939 * Fired when this object is clicked
20940 * @method handleMouseDown
20942 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20945 handleMouseDown: function(e, oDD){
20947 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20948 //Roo.log('not touch/ button !=0');
20951 if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20952 return; // double touch..
20956 if (this.isLocked()) {
20957 //Roo.log('locked');
20961 this.DDM.refreshCache(this.groups);
20962 // Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20963 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20964 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
20965 //Roo.log('no outer handes or not over target');
20968 // Roo.log('check validator');
20969 if (this.clickValidator(e)) {
20970 // Roo.log('validate success');
20971 // set the initial element position
20972 this.setStartPosition();
20975 this.b4MouseDown(e);
20976 this.onMouseDown(e);
20978 this.DDM.handleMouseDown(e, this);
20980 this.DDM.stopEvent(e);
20988 clickValidator: function(e) {
20989 var target = e.getTarget();
20990 return ( this.isValidHandleChild(target) &&
20991 (this.id == this.handleElId ||
20992 this.DDM.handleWasClicked(target, this.id)) );
20996 * Allows you to specify a tag name that should not start a drag operation
20997 * when clicked. This is designed to facilitate embedding links within a
20998 * drag handle that do something other than start the drag.
20999 * @method addInvalidHandleType
21000 * @param {string} tagName the type of element to exclude
21002 addInvalidHandleType: function(tagName) {
21003 var type = tagName.toUpperCase();
21004 this.invalidHandleTypes[type] = type;
21008 * Lets you to specify an element id for a child of a drag handle
21009 * that should not initiate a drag
21010 * @method addInvalidHandleId
21011 * @param {string} id the element id of the element you wish to ignore
21013 addInvalidHandleId: function(id) {
21014 if (typeof id !== "string") {
21017 this.invalidHandleIds[id] = id;
21021 * Lets you specify a css class of elements that will not initiate a drag
21022 * @method addInvalidHandleClass
21023 * @param {string} cssClass the class of the elements you wish to ignore
21025 addInvalidHandleClass: function(cssClass) {
21026 this.invalidHandleClasses.push(cssClass);
21030 * Unsets an excluded tag name set by addInvalidHandleType
21031 * @method removeInvalidHandleType
21032 * @param {string} tagName the type of element to unexclude
21034 removeInvalidHandleType: function(tagName) {
21035 var type = tagName.toUpperCase();
21036 // this.invalidHandleTypes[type] = null;
21037 delete this.invalidHandleTypes[type];
21041 * Unsets an invalid handle id
21042 * @method removeInvalidHandleId
21043 * @param {string} id the id of the element to re-enable
21045 removeInvalidHandleId: function(id) {
21046 if (typeof id !== "string") {
21049 delete this.invalidHandleIds[id];
21053 * Unsets an invalid css class
21054 * @method removeInvalidHandleClass
21055 * @param {string} cssClass the class of the element(s) you wish to
21058 removeInvalidHandleClass: function(cssClass) {
21059 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21060 if (this.invalidHandleClasses[i] == cssClass) {
21061 delete this.invalidHandleClasses[i];
21067 * Checks the tag exclusion list to see if this click should be ignored
21068 * @method isValidHandleChild
21069 * @param {HTMLElement} node the HTMLElement to evaluate
21070 * @return {boolean} true if this is a valid tag type, false if not
21072 isValidHandleChild: function(node) {
21075 // var n = (node.nodeName == "#text") ? node.parentNode : node;
21078 nodeName = node.nodeName.toUpperCase();
21080 nodeName = node.nodeName;
21082 valid = valid && !this.invalidHandleTypes[nodeName];
21083 valid = valid && !this.invalidHandleIds[node.id];
21085 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21086 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21095 * Create the array of horizontal tick marks if an interval was specified
21096 * in setXConstraint().
21097 * @method setXTicks
21100 setXTicks: function(iStartX, iTickSize) {
21102 this.xTickSize = iTickSize;
21106 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21108 this.xTicks[this.xTicks.length] = i;
21113 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21115 this.xTicks[this.xTicks.length] = i;
21120 this.xTicks.sort(this.DDM.numericSort) ;
21124 * Create the array of vertical tick marks if an interval was specified in
21125 * setYConstraint().
21126 * @method setYTicks
21129 setYTicks: function(iStartY, iTickSize) {
21131 this.yTickSize = iTickSize;
21135 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21137 this.yTicks[this.yTicks.length] = i;
21142 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21144 this.yTicks[this.yTicks.length] = i;
21149 this.yTicks.sort(this.DDM.numericSort) ;
21153 * By default, the element can be dragged any place on the screen. Use
21154 * this method to limit the horizontal travel of the element. Pass in
21155 * 0,0 for the parameters if you want to lock the drag to the y axis.
21156 * @method setXConstraint
21157 * @param {int} iLeft the number of pixels the element can move to the left
21158 * @param {int} iRight the number of pixels the element can move to the
21160 * @param {int} iTickSize optional parameter for specifying that the
21162 * should move iTickSize pixels at a time.
21164 setXConstraint: function(iLeft, iRight, iTickSize) {
21165 this.leftConstraint = iLeft;
21166 this.rightConstraint = iRight;
21168 this.minX = this.initPageX - iLeft;
21169 this.maxX = this.initPageX + iRight;
21170 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21172 this.constrainX = true;
21176 * Clears any constraints applied to this instance. Also clears ticks
21177 * since they can't exist independent of a constraint at this time.
21178 * @method clearConstraints
21180 clearConstraints: function() {
21181 this.constrainX = false;
21182 this.constrainY = false;
21187 * Clears any tick interval defined for this instance
21188 * @method clearTicks
21190 clearTicks: function() {
21191 this.xTicks = null;
21192 this.yTicks = null;
21193 this.xTickSize = 0;
21194 this.yTickSize = 0;
21198 * By default, the element can be dragged any place on the screen. Set
21199 * this to limit the vertical travel of the element. Pass in 0,0 for the
21200 * parameters if you want to lock the drag to the x axis.
21201 * @method setYConstraint
21202 * @param {int} iUp the number of pixels the element can move up
21203 * @param {int} iDown the number of pixels the element can move down
21204 * @param {int} iTickSize optional parameter for specifying that the
21205 * element should move iTickSize pixels at a time.
21207 setYConstraint: function(iUp, iDown, iTickSize) {
21208 this.topConstraint = iUp;
21209 this.bottomConstraint = iDown;
21211 this.minY = this.initPageY - iUp;
21212 this.maxY = this.initPageY + iDown;
21213 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21215 this.constrainY = true;
21220 * resetConstraints must be called if you manually reposition a dd element.
21221 * @method resetConstraints
21222 * @param {boolean} maintainOffset
21224 resetConstraints: function() {
21227 // Maintain offsets if necessary
21228 if (this.initPageX || this.initPageX === 0) {
21229 // figure out how much this thing has moved
21230 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21231 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21233 this.setInitPosition(dx, dy);
21235 // This is the first time we have detected the element's position
21237 this.setInitPosition();
21240 if (this.constrainX) {
21241 this.setXConstraint( this.leftConstraint,
21242 this.rightConstraint,
21246 if (this.constrainY) {
21247 this.setYConstraint( this.topConstraint,
21248 this.bottomConstraint,
21254 * Normally the drag element is moved pixel by pixel, but we can specify
21255 * that it move a number of pixels at a time. This method resolves the
21256 * location when we have it set up like this.
21258 * @param {int} val where we want to place the object
21259 * @param {int[]} tickArray sorted array of valid points
21260 * @return {int} the closest tick
21263 getTick: function(val, tickArray) {
21266 // If tick interval is not defined, it is effectively 1 pixel,
21267 // so we return the value passed to us.
21269 } else if (tickArray[0] >= val) {
21270 // The value is lower than the first tick, so we return the first
21272 return tickArray[0];
21274 for (var i=0, len=tickArray.length; i<len; ++i) {
21276 if (tickArray[next] && tickArray[next] >= val) {
21277 var diff1 = val - tickArray[i];
21278 var diff2 = tickArray[next] - val;
21279 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21283 // The value is larger than the last tick, so we return the last
21285 return tickArray[tickArray.length - 1];
21292 * @return {string} string representation of the dd obj
21294 toString: function() {
21295 return ("DragDrop " + this.id);
21303 * Ext JS Library 1.1.1
21304 * Copyright(c) 2006-2007, Ext JS, LLC.
21306 * Originally Released Under LGPL - original licence link has changed is not relivant.
21309 * <script type="text/javascript">
21314 * The drag and drop utility provides a framework for building drag and drop
21315 * applications. In addition to enabling drag and drop for specific elements,
21316 * the drag and drop elements are tracked by the manager class, and the
21317 * interactions between the various elements are tracked during the drag and
21318 * the implementing code is notified about these important moments.
21321 // Only load the library once. Rewriting the manager class would orphan
21322 // existing drag and drop instances.
21323 if (!Roo.dd.DragDropMgr) {
21326 * @class Roo.dd.DragDropMgr
21327 * DragDropMgr is a singleton that tracks the element interaction for
21328 * all DragDrop items in the window. Generally, you will not call
21329 * this class directly, but it does have helper methods that could
21330 * be useful in your DragDrop implementations.
21333 Roo.dd.DragDropMgr = function() {
21335 var Event = Roo.EventManager;
21340 * Two dimensional Array of registered DragDrop objects. The first
21341 * dimension is the DragDrop item group, the second the DragDrop
21344 * @type {string: string}
21351 * Array of element ids defined as drag handles. Used to determine
21352 * if the element that generated the mousedown event is actually the
21353 * handle and not the html element itself.
21354 * @property handleIds
21355 * @type {string: string}
21362 * the DragDrop object that is currently being dragged
21363 * @property dragCurrent
21371 * the DragDrop object(s) that are being hovered over
21372 * @property dragOvers
21380 * the X distance between the cursor and the object being dragged
21389 * the Y distance between the cursor and the object being dragged
21398 * Flag to determine if we should prevent the default behavior of the
21399 * events we define. By default this is true, but this can be set to
21400 * false if you need the default behavior (not recommended)
21401 * @property preventDefault
21405 preventDefault: true,
21408 * Flag to determine if we should stop the propagation of the events
21409 * we generate. This is true by default but you may want to set it to
21410 * false if the html element contains other features that require the
21412 * @property stopPropagation
21416 stopPropagation: true,
21419 * Internal flag that is set to true when drag and drop has been
21421 * @property initialized
21428 * All drag and drop can be disabled.
21436 * Called the first time an element is registered.
21442 this.initialized = true;
21446 * In point mode, drag and drop interaction is defined by the
21447 * location of the cursor during the drag/drop
21455 * In intersect mode, drag and drop interactio nis defined by the
21456 * overlap of two or more drag and drop objects.
21457 * @property INTERSECT
21464 * The current drag and drop mode. Default: POINT
21472 * Runs method on all drag and drop objects
21473 * @method _execOnAll
21477 _execOnAll: function(sMethod, args) {
21478 for (var i in this.ids) {
21479 for (var j in this.ids[i]) {
21480 var oDD = this.ids[i][j];
21481 if (! this.isTypeOfDD(oDD)) {
21484 oDD[sMethod].apply(oDD, args);
21490 * Drag and drop initialization. Sets up the global event handlers
21495 _onLoad: function() {
21499 if (!Roo.isTouch) {
21500 Event.on(document, "mouseup", this.handleMouseUp, this, true);
21501 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21503 Event.on(document, "touchend", this.handleMouseUp, this, true);
21504 Event.on(document, "touchmove", this.handleMouseMove, this, true);
21506 Event.on(window, "unload", this._onUnload, this, true);
21507 Event.on(window, "resize", this._onResize, this, true);
21508 // Event.on(window, "mouseout", this._test);
21513 * Reset constraints on all drag and drop objs
21514 * @method _onResize
21518 _onResize: function(e) {
21519 this._execOnAll("resetConstraints", []);
21523 * Lock all drag and drop functionality
21527 lock: function() { this.locked = true; },
21530 * Unlock all drag and drop functionality
21534 unlock: function() { this.locked = false; },
21537 * Is drag and drop locked?
21539 * @return {boolean} True if drag and drop is locked, false otherwise.
21542 isLocked: function() { return this.locked; },
21545 * Location cache that is set for all drag drop objects when a drag is
21546 * initiated, cleared when the drag is finished.
21547 * @property locationCache
21554 * Set useCache to false if you want to force object the lookup of each
21555 * drag and drop linked element constantly during a drag.
21556 * @property useCache
21563 * The number of pixels that the mouse needs to move after the
21564 * mousedown before the drag is initiated. Default=3;
21565 * @property clickPixelThresh
21569 clickPixelThresh: 3,
21572 * The number of milliseconds after the mousedown event to initiate the
21573 * drag if we don't get a mouseup event. Default=1000
21574 * @property clickTimeThresh
21578 clickTimeThresh: 350,
21581 * Flag that indicates that either the drag pixel threshold or the
21582 * mousdown time threshold has been met
21583 * @property dragThreshMet
21588 dragThreshMet: false,
21591 * Timeout used for the click time threshold
21592 * @property clickTimeout
21597 clickTimeout: null,
21600 * The X position of the mousedown event stored for later use when a
21601 * drag threshold is met.
21610 * The Y position of the mousedown event stored for later use when a
21611 * drag threshold is met.
21620 * Each DragDrop instance must be registered with the DragDropMgr.
21621 * This is executed in DragDrop.init()
21622 * @method regDragDrop
21623 * @param {DragDrop} oDD the DragDrop object to register
21624 * @param {String} sGroup the name of the group this element belongs to
21627 regDragDrop: function(oDD, sGroup) {
21628 if (!this.initialized) { this.init(); }
21630 if (!this.ids[sGroup]) {
21631 this.ids[sGroup] = {};
21633 this.ids[sGroup][oDD.id] = oDD;
21637 * Removes the supplied dd instance from the supplied group. Executed
21638 * by DragDrop.removeFromGroup, so don't call this function directly.
21639 * @method removeDDFromGroup
21643 removeDDFromGroup: function(oDD, sGroup) {
21644 if (!this.ids[sGroup]) {
21645 this.ids[sGroup] = {};
21648 var obj = this.ids[sGroup];
21649 if (obj && obj[oDD.id]) {
21650 delete obj[oDD.id];
21655 * Unregisters a drag and drop item. This is executed in
21656 * DragDrop.unreg, use that method instead of calling this directly.
21661 _remove: function(oDD) {
21662 for (var g in oDD.groups) {
21663 if (g && this.ids[g][oDD.id]) {
21664 delete this.ids[g][oDD.id];
21667 delete this.handleIds[oDD.id];
21671 * Each DragDrop handle element must be registered. This is done
21672 * automatically when executing DragDrop.setHandleElId()
21673 * @method regHandle
21674 * @param {String} sDDId the DragDrop id this element is a handle for
21675 * @param {String} sHandleId the id of the element that is the drag
21679 regHandle: function(sDDId, sHandleId) {
21680 if (!this.handleIds[sDDId]) {
21681 this.handleIds[sDDId] = {};
21683 this.handleIds[sDDId][sHandleId] = sHandleId;
21687 * Utility function to determine if a given element has been
21688 * registered as a drag drop item.
21689 * @method isDragDrop
21690 * @param {String} id the element id to check
21691 * @return {boolean} true if this element is a DragDrop item,
21695 isDragDrop: function(id) {
21696 return ( this.getDDById(id) ) ? true : false;
21700 * Returns the drag and drop instances that are in all groups the
21701 * passed in instance belongs to.
21702 * @method getRelated
21703 * @param {DragDrop} p_oDD the obj to get related data for
21704 * @param {boolean} bTargetsOnly if true, only return targetable objs
21705 * @return {DragDrop[]} the related instances
21708 getRelated: function(p_oDD, bTargetsOnly) {
21710 for (var i in p_oDD.groups) {
21711 for (j in this.ids[i]) {
21712 var dd = this.ids[i][j];
21713 if (! this.isTypeOfDD(dd)) {
21716 if (!bTargetsOnly || dd.isTarget) {
21717 oDDs[oDDs.length] = dd;
21726 * Returns true if the specified dd target is a legal target for
21727 * the specifice drag obj
21728 * @method isLegalTarget
21729 * @param {DragDrop} the drag obj
21730 * @param {DragDrop} the target
21731 * @return {boolean} true if the target is a legal target for the
21735 isLegalTarget: function (oDD, oTargetDD) {
21736 var targets = this.getRelated(oDD, true);
21737 for (var i=0, len=targets.length;i<len;++i) {
21738 if (targets[i].id == oTargetDD.id) {
21747 * My goal is to be able to transparently determine if an object is
21748 * typeof DragDrop, and the exact subclass of DragDrop. typeof
21749 * returns "object", oDD.constructor.toString() always returns
21750 * "DragDrop" and not the name of the subclass. So for now it just
21751 * evaluates a well-known variable in DragDrop.
21752 * @method isTypeOfDD
21753 * @param {Object} the object to evaluate
21754 * @return {boolean} true if typeof oDD = DragDrop
21757 isTypeOfDD: function (oDD) {
21758 return (oDD && oDD.__ygDragDrop);
21762 * Utility function to determine if a given element has been
21763 * registered as a drag drop handle for the given Drag Drop object.
21765 * @param {String} id the element id to check
21766 * @return {boolean} true if this element is a DragDrop handle, false
21770 isHandle: function(sDDId, sHandleId) {
21771 return ( this.handleIds[sDDId] &&
21772 this.handleIds[sDDId][sHandleId] );
21776 * Returns the DragDrop instance for a given id
21777 * @method getDDById
21778 * @param {String} id the id of the DragDrop object
21779 * @return {DragDrop} the drag drop object, null if it is not found
21782 getDDById: function(id) {
21783 for (var i in this.ids) {
21784 if (this.ids[i][id]) {
21785 return this.ids[i][id];
21792 * Fired after a registered DragDrop object gets the mousedown event.
21793 * Sets up the events required to track the object being dragged
21794 * @method handleMouseDown
21795 * @param {Event} e the event
21796 * @param oDD the DragDrop object being dragged
21800 handleMouseDown: function(e, oDD) {
21802 Roo.QuickTips.disable();
21804 this.currentTarget = e.getTarget();
21806 this.dragCurrent = oDD;
21808 var el = oDD.getEl();
21810 // track start position
21811 this.startX = e.getPageX();
21812 this.startY = e.getPageY();
21814 this.deltaX = this.startX - el.offsetLeft;
21815 this.deltaY = this.startY - el.offsetTop;
21817 this.dragThreshMet = false;
21819 this.clickTimeout = setTimeout(
21821 var DDM = Roo.dd.DDM;
21822 DDM.startDrag(DDM.startX, DDM.startY);
21824 this.clickTimeThresh );
21828 * Fired when either the drag pixel threshol or the mousedown hold
21829 * time threshold has been met.
21830 * @method startDrag
21831 * @param x {int} the X position of the original mousedown
21832 * @param y {int} the Y position of the original mousedown
21835 startDrag: function(x, y) {
21836 clearTimeout(this.clickTimeout);
21837 if (this.dragCurrent) {
21838 this.dragCurrent.b4StartDrag(x, y);
21839 this.dragCurrent.startDrag(x, y);
21841 this.dragThreshMet = true;
21845 * Internal function to handle the mouseup event. Will be invoked
21846 * from the context of the document.
21847 * @method handleMouseUp
21848 * @param {Event} e the event
21852 handleMouseUp: function(e) {
21855 Roo.QuickTips.enable();
21857 if (! this.dragCurrent) {
21861 clearTimeout(this.clickTimeout);
21863 if (this.dragThreshMet) {
21864 this.fireEvents(e, true);
21874 * Utility to stop event propagation and event default, if these
21875 * features are turned on.
21876 * @method stopEvent
21877 * @param {Event} e the event as returned by this.getEvent()
21880 stopEvent: function(e){
21881 if(this.stopPropagation) {
21882 e.stopPropagation();
21885 if (this.preventDefault) {
21886 e.preventDefault();
21891 * Internal function to clean up event handlers after the drag
21892 * operation is complete
21894 * @param {Event} e the event
21898 stopDrag: function(e) {
21899 // Fire the drag end event for the item that was dragged
21900 if (this.dragCurrent) {
21901 if (this.dragThreshMet) {
21902 this.dragCurrent.b4EndDrag(e);
21903 this.dragCurrent.endDrag(e);
21906 this.dragCurrent.onMouseUp(e);
21909 this.dragCurrent = null;
21910 this.dragOvers = {};
21914 * Internal function to handle the mousemove event. Will be invoked
21915 * from the context of the html element.
21917 * @TODO figure out what we can do about mouse events lost when the
21918 * user drags objects beyond the window boundary. Currently we can
21919 * detect this in internet explorer by verifying that the mouse is
21920 * down during the mousemove event. Firefox doesn't give us the
21921 * button state on the mousemove event.
21922 * @method handleMouseMove
21923 * @param {Event} e the event
21927 handleMouseMove: function(e) {
21928 if (! this.dragCurrent) {
21932 // var button = e.which || e.button;
21934 // check for IE mouseup outside of page boundary
21935 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21937 return this.handleMouseUp(e);
21940 if (!this.dragThreshMet) {
21941 var diffX = Math.abs(this.startX - e.getPageX());
21942 var diffY = Math.abs(this.startY - e.getPageY());
21943 if (diffX > this.clickPixelThresh ||
21944 diffY > this.clickPixelThresh) {
21945 this.startDrag(this.startX, this.startY);
21949 if (this.dragThreshMet) {
21950 this.dragCurrent.b4Drag(e);
21951 this.dragCurrent.onDrag(e);
21952 if(!this.dragCurrent.moveOnly){
21953 this.fireEvents(e, false);
21963 * Iterates over all of the DragDrop elements to find ones we are
21964 * hovering over or dropping on
21965 * @method fireEvents
21966 * @param {Event} e the event
21967 * @param {boolean} isDrop is this a drop op or a mouseover op?
21971 fireEvents: function(e, isDrop) {
21972 var dc = this.dragCurrent;
21974 // If the user did the mouse up outside of the window, we could
21975 // get here even though we have ended the drag.
21976 if (!dc || dc.isLocked()) {
21980 var pt = e.getPoint();
21982 // cache the previous dragOver array
21988 var enterEvts = [];
21990 // Check to see if the object(s) we were hovering over is no longer
21991 // being hovered over so we can fire the onDragOut event
21992 for (var i in this.dragOvers) {
21994 var ddo = this.dragOvers[i];
21996 if (! this.isTypeOfDD(ddo)) {
22000 if (! this.isOverTarget(pt, ddo, this.mode)) {
22001 outEvts.push( ddo );
22004 oldOvers[i] = true;
22005 delete this.dragOvers[i];
22008 for (var sGroup in dc.groups) {
22010 if ("string" != typeof sGroup) {
22014 for (i in this.ids[sGroup]) {
22015 var oDD = this.ids[sGroup][i];
22016 if (! this.isTypeOfDD(oDD)) {
22020 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22021 if (this.isOverTarget(pt, oDD, this.mode)) {
22022 // look for drop interactions
22024 dropEvts.push( oDD );
22025 // look for drag enter and drag over interactions
22028 // initial drag over: dragEnter fires
22029 if (!oldOvers[oDD.id]) {
22030 enterEvts.push( oDD );
22031 // subsequent drag overs: dragOver fires
22033 overEvts.push( oDD );
22036 this.dragOvers[oDD.id] = oDD;
22044 if (outEvts.length) {
22045 dc.b4DragOut(e, outEvts);
22046 dc.onDragOut(e, outEvts);
22049 if (enterEvts.length) {
22050 dc.onDragEnter(e, enterEvts);
22053 if (overEvts.length) {
22054 dc.b4DragOver(e, overEvts);
22055 dc.onDragOver(e, overEvts);
22058 if (dropEvts.length) {
22059 dc.b4DragDrop(e, dropEvts);
22060 dc.onDragDrop(e, dropEvts);
22064 // fire dragout events
22066 for (i=0, len=outEvts.length; i<len; ++i) {
22067 dc.b4DragOut(e, outEvts[i].id);
22068 dc.onDragOut(e, outEvts[i].id);
22071 // fire enter events
22072 for (i=0,len=enterEvts.length; i<len; ++i) {
22073 // dc.b4DragEnter(e, oDD.id);
22074 dc.onDragEnter(e, enterEvts[i].id);
22077 // fire over events
22078 for (i=0,len=overEvts.length; i<len; ++i) {
22079 dc.b4DragOver(e, overEvts[i].id);
22080 dc.onDragOver(e, overEvts[i].id);
22083 // fire drop events
22084 for (i=0, len=dropEvts.length; i<len; ++i) {
22085 dc.b4DragDrop(e, dropEvts[i].id);
22086 dc.onDragDrop(e, dropEvts[i].id);
22091 // notify about a drop that did not find a target
22092 if (isDrop && !dropEvts.length) {
22093 dc.onInvalidDrop(e);
22099 * Helper function for getting the best match from the list of drag
22100 * and drop objects returned by the drag and drop events when we are
22101 * in INTERSECT mode. It returns either the first object that the
22102 * cursor is over, or the object that has the greatest overlap with
22103 * the dragged element.
22104 * @method getBestMatch
22105 * @param {DragDrop[]} dds The array of drag and drop objects
22107 * @return {DragDrop} The best single match
22110 getBestMatch: function(dds) {
22112 // Return null if the input is not what we expect
22113 //if (!dds || !dds.length || dds.length == 0) {
22115 // If there is only one item, it wins
22116 //} else if (dds.length == 1) {
22118 var len = dds.length;
22123 // Loop through the targeted items
22124 for (var i=0; i<len; ++i) {
22126 // If the cursor is over the object, it wins. If the
22127 // cursor is over multiple matches, the first one we come
22129 if (dd.cursorIsOver) {
22132 // Otherwise the object with the most overlap wins
22135 winner.overlap.getArea() < dd.overlap.getArea()) {
22146 * Refreshes the cache of the top-left and bottom-right points of the
22147 * drag and drop objects in the specified group(s). This is in the
22148 * format that is stored in the drag and drop instance, so typical
22151 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22155 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22157 * @TODO this really should be an indexed array. Alternatively this
22158 * method could accept both.
22159 * @method refreshCache
22160 * @param {Object} groups an associative array of groups to refresh
22163 refreshCache: function(groups) {
22164 for (var sGroup in groups) {
22165 if ("string" != typeof sGroup) {
22168 for (var i in this.ids[sGroup]) {
22169 var oDD = this.ids[sGroup][i];
22171 if (this.isTypeOfDD(oDD)) {
22172 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22173 var loc = this.getLocation(oDD);
22175 this.locationCache[oDD.id] = loc;
22177 delete this.locationCache[oDD.id];
22178 // this will unregister the drag and drop object if
22179 // the element is not in a usable state
22188 * This checks to make sure an element exists and is in the DOM. The
22189 * main purpose is to handle cases where innerHTML is used to remove
22190 * drag and drop objects from the DOM. IE provides an 'unspecified
22191 * error' when trying to access the offsetParent of such an element
22193 * @param {HTMLElement} el the element to check
22194 * @return {boolean} true if the element looks usable
22197 verifyEl: function(el) {
22202 parent = el.offsetParent;
22205 parent = el.offsetParent;
22216 * Returns a Region object containing the drag and drop element's position
22217 * and size, including the padding configured for it
22218 * @method getLocation
22219 * @param {DragDrop} oDD the drag and drop object to get the
22221 * @return {Roo.lib.Region} a Region object representing the total area
22222 * the element occupies, including any padding
22223 * the instance is configured for.
22226 getLocation: function(oDD) {
22227 if (! this.isTypeOfDD(oDD)) {
22231 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22234 pos= Roo.lib.Dom.getXY(el);
22242 x2 = x1 + el.offsetWidth;
22244 y2 = y1 + el.offsetHeight;
22246 t = y1 - oDD.padding[0];
22247 r = x2 + oDD.padding[1];
22248 b = y2 + oDD.padding[2];
22249 l = x1 - oDD.padding[3];
22251 return new Roo.lib.Region( t, r, b, l );
22255 * Checks the cursor location to see if it over the target
22256 * @method isOverTarget
22257 * @param {Roo.lib.Point} pt The point to evaluate
22258 * @param {DragDrop} oTarget the DragDrop object we are inspecting
22259 * @return {boolean} true if the mouse is over the target
22263 isOverTarget: function(pt, oTarget, intersect) {
22264 // use cache if available
22265 var loc = this.locationCache[oTarget.id];
22266 if (!loc || !this.useCache) {
22267 loc = this.getLocation(oTarget);
22268 this.locationCache[oTarget.id] = loc;
22276 oTarget.cursorIsOver = loc.contains( pt );
22278 // DragDrop is using this as a sanity check for the initial mousedown
22279 // in this case we are done. In POINT mode, if the drag obj has no
22280 // contraints, we are also done. Otherwise we need to evaluate the
22281 // location of the target as related to the actual location of the
22282 // dragged element.
22283 var dc = this.dragCurrent;
22284 if (!dc || !dc.getTargetCoord ||
22285 (!intersect && !dc.constrainX && !dc.constrainY)) {
22286 return oTarget.cursorIsOver;
22289 oTarget.overlap = null;
22291 // Get the current location of the drag element, this is the
22292 // location of the mouse event less the delta that represents
22293 // where the original mousedown happened on the element. We
22294 // need to consider constraints and ticks as well.
22295 var pos = dc.getTargetCoord(pt.x, pt.y);
22297 var el = dc.getDragEl();
22298 var curRegion = new Roo.lib.Region( pos.y,
22299 pos.x + el.offsetWidth,
22300 pos.y + el.offsetHeight,
22303 var overlap = curRegion.intersect(loc);
22306 oTarget.overlap = overlap;
22307 return (intersect) ? true : oTarget.cursorIsOver;
22314 * unload event handler
22315 * @method _onUnload
22319 _onUnload: function(e, me) {
22320 Roo.dd.DragDropMgr.unregAll();
22324 * Cleans up the drag and drop events and objects.
22329 unregAll: function() {
22331 if (this.dragCurrent) {
22333 this.dragCurrent = null;
22336 this._execOnAll("unreg", []);
22338 for (i in this.elementCache) {
22339 delete this.elementCache[i];
22342 this.elementCache = {};
22347 * A cache of DOM elements
22348 * @property elementCache
22355 * Get the wrapper for the DOM element specified
22356 * @method getElWrapper
22357 * @param {String} id the id of the element to get
22358 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22360 * @deprecated This wrapper isn't that useful
22363 getElWrapper: function(id) {
22364 var oWrapper = this.elementCache[id];
22365 if (!oWrapper || !oWrapper.el) {
22366 oWrapper = this.elementCache[id] =
22367 new this.ElementWrapper(Roo.getDom(id));
22373 * Returns the actual DOM element
22374 * @method getElement
22375 * @param {String} id the id of the elment to get
22376 * @return {Object} The element
22377 * @deprecated use Roo.getDom instead
22380 getElement: function(id) {
22381 return Roo.getDom(id);
22385 * Returns the style property for the DOM element (i.e.,
22386 * document.getElById(id).style)
22388 * @param {String} id the id of the elment to get
22389 * @return {Object} The style property of the element
22390 * @deprecated use Roo.getDom instead
22393 getCss: function(id) {
22394 var el = Roo.getDom(id);
22395 return (el) ? el.style : null;
22399 * Inner class for cached elements
22400 * @class DragDropMgr.ElementWrapper
22405 ElementWrapper: function(el) {
22410 this.el = el || null;
22415 this.id = this.el && el.id;
22417 * A reference to the style property
22420 this.css = this.el && el.style;
22424 * Returns the X position of an html element
22426 * @param el the element for which to get the position
22427 * @return {int} the X coordinate
22429 * @deprecated use Roo.lib.Dom.getX instead
22432 getPosX: function(el) {
22433 return Roo.lib.Dom.getX(el);
22437 * Returns the Y position of an html element
22439 * @param el the element for which to get the position
22440 * @return {int} the Y coordinate
22441 * @deprecated use Roo.lib.Dom.getY instead
22444 getPosY: function(el) {
22445 return Roo.lib.Dom.getY(el);
22449 * Swap two nodes. In IE, we use the native method, for others we
22450 * emulate the IE behavior
22452 * @param n1 the first node to swap
22453 * @param n2 the other node to swap
22456 swapNode: function(n1, n2) {
22460 var p = n2.parentNode;
22461 var s = n2.nextSibling;
22464 p.insertBefore(n1, n2);
22465 } else if (n2 == n1.nextSibling) {
22466 p.insertBefore(n2, n1);
22468 n1.parentNode.replaceChild(n2, n1);
22469 p.insertBefore(n1, s);
22475 * Returns the current scroll position
22476 * @method getScroll
22480 getScroll: function () {
22481 var t, l, dde=document.documentElement, db=document.body;
22482 if (dde && (dde.scrollTop || dde.scrollLeft)) {
22484 l = dde.scrollLeft;
22491 return { top: t, left: l };
22495 * Returns the specified element style property
22497 * @param {HTMLElement} el the element
22498 * @param {string} styleProp the style property
22499 * @return {string} The value of the style property
22500 * @deprecated use Roo.lib.Dom.getStyle
22503 getStyle: function(el, styleProp) {
22504 return Roo.fly(el).getStyle(styleProp);
22508 * Gets the scrollTop
22509 * @method getScrollTop
22510 * @return {int} the document's scrollTop
22513 getScrollTop: function () { return this.getScroll().top; },
22516 * Gets the scrollLeft
22517 * @method getScrollLeft
22518 * @return {int} the document's scrollTop
22521 getScrollLeft: function () { return this.getScroll().left; },
22524 * Sets the x/y position of an element to the location of the
22527 * @param {HTMLElement} moveEl The element to move
22528 * @param {HTMLElement} targetEl The position reference element
22531 moveToEl: function (moveEl, targetEl) {
22532 var aCoord = Roo.lib.Dom.getXY(targetEl);
22533 Roo.lib.Dom.setXY(moveEl, aCoord);
22537 * Numeric array sort function
22538 * @method numericSort
22541 numericSort: function(a, b) { return (a - b); },
22545 * @property _timeoutCount
22552 * Trying to make the load order less important. Without this we get
22553 * an error if this file is loaded before the Event Utility.
22554 * @method _addListeners
22558 _addListeners: function() {
22559 var DDM = Roo.dd.DDM;
22560 if ( Roo.lib.Event && document ) {
22563 if (DDM._timeoutCount > 2000) {
22565 setTimeout(DDM._addListeners, 10);
22566 if (document && document.body) {
22567 DDM._timeoutCount += 1;
22574 * Recursively searches the immediate parent and all child nodes for
22575 * the handle element in order to determine wheter or not it was
22577 * @method handleWasClicked
22578 * @param node the html element to inspect
22581 handleWasClicked: function(node, id) {
22582 if (this.isHandle(id, node.id)) {
22585 // check to see if this is a text node child of the one we want
22586 var p = node.parentNode;
22589 if (this.isHandle(id, p.id)) {
22604 // shorter alias, save a few bytes
22605 Roo.dd.DDM = Roo.dd.DragDropMgr;
22606 Roo.dd.DDM._addListeners();
22610 * Ext JS Library 1.1.1
22611 * Copyright(c) 2006-2007, Ext JS, LLC.
22613 * Originally Released Under LGPL - original licence link has changed is not relivant.
22616 * <script type="text/javascript">
22621 * A DragDrop implementation where the linked element follows the
22622 * mouse cursor during a drag.
22623 * @extends Roo.dd.DragDrop
22625 * @param {String} id the id of the linked element
22626 * @param {String} sGroup the group of related DragDrop items
22627 * @param {object} config an object containing configurable attributes
22628 * Valid properties for DD:
22631 Roo.dd.DD = function(id, sGroup, config) {
22633 this.init(id, sGroup, config);
22637 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22640 * When set to true, the utility automatically tries to scroll the browser
22641 * window wehn a drag and drop element is dragged near the viewport boundary.
22642 * Defaults to true.
22649 * Sets the pointer offset to the distance between the linked element's top
22650 * left corner and the location the element was clicked
22651 * @method autoOffset
22652 * @param {int} iPageX the X coordinate of the click
22653 * @param {int} iPageY the Y coordinate of the click
22655 autoOffset: function(iPageX, iPageY) {
22656 var x = iPageX - this.startPageX;
22657 var y = iPageY - this.startPageY;
22658 this.setDelta(x, y);
22662 * Sets the pointer offset. You can call this directly to force the
22663 * offset to be in a particular location (e.g., pass in 0,0 to set it
22664 * to the center of the object)
22666 * @param {int} iDeltaX the distance from the left
22667 * @param {int} iDeltaY the distance from the top
22669 setDelta: function(iDeltaX, iDeltaY) {
22670 this.deltaX = iDeltaX;
22671 this.deltaY = iDeltaY;
22675 * Sets the drag element to the location of the mousedown or click event,
22676 * maintaining the cursor location relative to the location on the element
22677 * that was clicked. Override this if you want to place the element in a
22678 * location other than where the cursor is.
22679 * @method setDragElPos
22680 * @param {int} iPageX the X coordinate of the mousedown or drag event
22681 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22683 setDragElPos: function(iPageX, iPageY) {
22684 // the first time we do this, we are going to check to make sure
22685 // the element has css positioning
22687 var el = this.getDragEl();
22688 this.alignElWithMouse(el, iPageX, iPageY);
22692 * Sets the element to the location of the mousedown or click event,
22693 * maintaining the cursor location relative to the location on the element
22694 * that was clicked. Override this if you want to place the element in a
22695 * location other than where the cursor is.
22696 * @method alignElWithMouse
22697 * @param {HTMLElement} el the element to move
22698 * @param {int} iPageX the X coordinate of the mousedown or drag event
22699 * @param {int} iPageY the Y coordinate of the mousedown or drag event
22701 alignElWithMouse: function(el, iPageX, iPageY) {
22702 var oCoord = this.getTargetCoord(iPageX, iPageY);
22703 var fly = el.dom ? el : Roo.fly(el);
22704 if (!this.deltaSetXY) {
22705 var aCoord = [oCoord.x, oCoord.y];
22707 var newLeft = fly.getLeft(true);
22708 var newTop = fly.getTop(true);
22709 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22711 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22714 this.cachePosition(oCoord.x, oCoord.y);
22715 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22720 * Saves the most recent position so that we can reset the constraints and
22721 * tick marks on-demand. We need to know this so that we can calculate the
22722 * number of pixels the element is offset from its original position.
22723 * @method cachePosition
22724 * @param iPageX the current x position (optional, this just makes it so we
22725 * don't have to look it up again)
22726 * @param iPageY the current y position (optional, this just makes it so we
22727 * don't have to look it up again)
22729 cachePosition: function(iPageX, iPageY) {
22731 this.lastPageX = iPageX;
22732 this.lastPageY = iPageY;
22734 var aCoord = Roo.lib.Dom.getXY(this.getEl());
22735 this.lastPageX = aCoord[0];
22736 this.lastPageY = aCoord[1];
22741 * Auto-scroll the window if the dragged object has been moved beyond the
22742 * visible window boundary.
22743 * @method autoScroll
22744 * @param {int} x the drag element's x position
22745 * @param {int} y the drag element's y position
22746 * @param {int} h the height of the drag element
22747 * @param {int} w the width of the drag element
22750 autoScroll: function(x, y, h, w) {
22753 // The client height
22754 var clientH = Roo.lib.Dom.getViewWidth();
22756 // The client width
22757 var clientW = Roo.lib.Dom.getViewHeight();
22759 // The amt scrolled down
22760 var st = this.DDM.getScrollTop();
22762 // The amt scrolled right
22763 var sl = this.DDM.getScrollLeft();
22765 // Location of the bottom of the element
22768 // Location of the right of the element
22771 // The distance from the cursor to the bottom of the visible area,
22772 // adjusted so that we don't scroll if the cursor is beyond the
22773 // element drag constraints
22774 var toBot = (clientH + st - y - this.deltaY);
22776 // The distance from the cursor to the right of the visible area
22777 var toRight = (clientW + sl - x - this.deltaX);
22780 // How close to the edge the cursor must be before we scroll
22781 // var thresh = (document.all) ? 100 : 40;
22784 // How many pixels to scroll per autoscroll op. This helps to reduce
22785 // clunky scrolling. IE is more sensitive about this ... it needs this
22786 // value to be higher.
22787 var scrAmt = (document.all) ? 80 : 30;
22789 // Scroll down if we are near the bottom of the visible page and the
22790 // obj extends below the crease
22791 if ( bot > clientH && toBot < thresh ) {
22792 window.scrollTo(sl, st + scrAmt);
22795 // Scroll up if the window is scrolled down and the top of the object
22796 // goes above the top border
22797 if ( y < st && st > 0 && y - st < thresh ) {
22798 window.scrollTo(sl, st - scrAmt);
22801 // Scroll right if the obj is beyond the right border and the cursor is
22802 // near the border.
22803 if ( right > clientW && toRight < thresh ) {
22804 window.scrollTo(sl + scrAmt, st);
22807 // Scroll left if the window has been scrolled to the right and the obj
22808 // extends past the left border
22809 if ( x < sl && sl > 0 && x - sl < thresh ) {
22810 window.scrollTo(sl - scrAmt, st);
22816 * Finds the location the element should be placed if we want to move
22817 * it to where the mouse location less the click offset would place us.
22818 * @method getTargetCoord
22819 * @param {int} iPageX the X coordinate of the click
22820 * @param {int} iPageY the Y coordinate of the click
22821 * @return an object that contains the coordinates (Object.x and Object.y)
22824 getTargetCoord: function(iPageX, iPageY) {
22827 var x = iPageX - this.deltaX;
22828 var y = iPageY - this.deltaY;
22830 if (this.constrainX) {
22831 if (x < this.minX) { x = this.minX; }
22832 if (x > this.maxX) { x = this.maxX; }
22835 if (this.constrainY) {
22836 if (y < this.minY) { y = this.minY; }
22837 if (y > this.maxY) { y = this.maxY; }
22840 x = this.getTick(x, this.xTicks);
22841 y = this.getTick(y, this.yTicks);
22848 * Sets up config options specific to this class. Overrides
22849 * Roo.dd.DragDrop, but all versions of this method through the
22850 * inheritance chain are called
22852 applyConfig: function() {
22853 Roo.dd.DD.superclass.applyConfig.call(this);
22854 this.scroll = (this.config.scroll !== false);
22858 * Event that fires prior to the onMouseDown event. Overrides
22861 b4MouseDown: function(e) {
22862 // this.resetConstraints();
22863 this.autoOffset(e.getPageX(),
22868 * Event that fires prior to the onDrag event. Overrides
22871 b4Drag: function(e) {
22872 this.setDragElPos(e.getPageX(),
22876 toString: function() {
22877 return ("DD " + this.id);
22880 //////////////////////////////////////////////////////////////////////////
22881 // Debugging ygDragDrop events that can be overridden
22882 //////////////////////////////////////////////////////////////////////////
22884 startDrag: function(x, y) {
22887 onDrag: function(e) {
22890 onDragEnter: function(e, id) {
22893 onDragOver: function(e, id) {
22896 onDragOut: function(e, id) {
22899 onDragDrop: function(e, id) {
22902 endDrag: function(e) {
22909 * Ext JS Library 1.1.1
22910 * Copyright(c) 2006-2007, Ext JS, LLC.
22912 * Originally Released Under LGPL - original licence link has changed is not relivant.
22915 * <script type="text/javascript">
22919 * @class Roo.dd.DDProxy
22920 * A DragDrop implementation that inserts an empty, bordered div into
22921 * the document that follows the cursor during drag operations. At the time of
22922 * the click, the frame div is resized to the dimensions of the linked html
22923 * element, and moved to the exact location of the linked element.
22925 * References to the "frame" element refer to the single proxy element that
22926 * was created to be dragged in place of all DDProxy elements on the
22929 * @extends Roo.dd.DD
22931 * @param {String} id the id of the linked html element
22932 * @param {String} sGroup the group of related DragDrop objects
22933 * @param {object} config an object containing configurable attributes
22934 * Valid properties for DDProxy in addition to those in DragDrop:
22935 * resizeFrame, centerFrame, dragElId
22937 Roo.dd.DDProxy = function(id, sGroup, config) {
22939 this.init(id, sGroup, config);
22945 * The default drag frame div id
22946 * @property Roo.dd.DDProxy.dragElId
22950 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22952 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22955 * By default we resize the drag frame to be the same size as the element
22956 * we want to drag (this is to get the frame effect). We can turn it off
22957 * if we want a different behavior.
22958 * @property resizeFrame
22964 * By default the frame is positioned exactly where the drag element is, so
22965 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
22966 * you do not have constraints on the obj is to have the drag frame centered
22967 * around the cursor. Set centerFrame to true for this effect.
22968 * @property centerFrame
22971 centerFrame: false,
22974 * Creates the proxy element if it does not yet exist
22975 * @method createFrame
22977 createFrame: function() {
22979 var body = document.body;
22981 if (!body || !body.firstChild) {
22982 setTimeout( function() { self.createFrame(); }, 50 );
22986 var div = this.getDragEl();
22989 div = document.createElement("div");
22990 div.id = this.dragElId;
22993 s.position = "absolute";
22994 s.visibility = "hidden";
22996 s.border = "2px solid #aaa";
22999 // appendChild can blow up IE if invoked prior to the window load event
23000 // while rendering a table. It is possible there are other scenarios
23001 // that would cause this to happen as well.
23002 body.insertBefore(div, body.firstChild);
23007 * Initialization for the drag frame element. Must be called in the
23008 * constructor of all subclasses
23009 * @method initFrame
23011 initFrame: function() {
23012 this.createFrame();
23015 applyConfig: function() {
23016 Roo.dd.DDProxy.superclass.applyConfig.call(this);
23018 this.resizeFrame = (this.config.resizeFrame !== false);
23019 this.centerFrame = (this.config.centerFrame);
23020 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23024 * Resizes the drag frame to the dimensions of the clicked object, positions
23025 * it over the object, and finally displays it
23026 * @method showFrame
23027 * @param {int} iPageX X click position
23028 * @param {int} iPageY Y click position
23031 showFrame: function(iPageX, iPageY) {
23032 var el = this.getEl();
23033 var dragEl = this.getDragEl();
23034 var s = dragEl.style;
23036 this._resizeProxy();
23038 if (this.centerFrame) {
23039 this.setDelta( Math.round(parseInt(s.width, 10)/2),
23040 Math.round(parseInt(s.height, 10)/2) );
23043 this.setDragElPos(iPageX, iPageY);
23045 Roo.fly(dragEl).show();
23049 * The proxy is automatically resized to the dimensions of the linked
23050 * element when a drag is initiated, unless resizeFrame is set to false
23051 * @method _resizeProxy
23054 _resizeProxy: function() {
23055 if (this.resizeFrame) {
23056 var el = this.getEl();
23057 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23061 // overrides Roo.dd.DragDrop
23062 b4MouseDown: function(e) {
23063 var x = e.getPageX();
23064 var y = e.getPageY();
23065 this.autoOffset(x, y);
23066 this.setDragElPos(x, y);
23069 // overrides Roo.dd.DragDrop
23070 b4StartDrag: function(x, y) {
23071 // show the drag frame
23072 this.showFrame(x, y);
23075 // overrides Roo.dd.DragDrop
23076 b4EndDrag: function(e) {
23077 Roo.fly(this.getDragEl()).hide();
23080 // overrides Roo.dd.DragDrop
23081 // By default we try to move the element to the last location of the frame.
23082 // This is so that the default behavior mirrors that of Roo.dd.DD.
23083 endDrag: function(e) {
23085 var lel = this.getEl();
23086 var del = this.getDragEl();
23088 // Show the drag frame briefly so we can get its position
23089 del.style.visibility = "";
23092 // Hide the linked element before the move to get around a Safari
23094 lel.style.visibility = "hidden";
23095 Roo.dd.DDM.moveToEl(lel, del);
23096 del.style.visibility = "hidden";
23097 lel.style.visibility = "";
23102 beforeMove : function(){
23106 afterDrag : function(){
23110 toString: function() {
23111 return ("DDProxy " + this.id);
23117 * Ext JS Library 1.1.1
23118 * Copyright(c) 2006-2007, Ext JS, LLC.
23120 * Originally Released Under LGPL - original licence link has changed is not relivant.
23123 * <script type="text/javascript">
23127 * @class Roo.dd.DDTarget
23128 * A DragDrop implementation that does not move, but can be a drop
23129 * target. You would get the same result by simply omitting implementation
23130 * for the event callbacks, but this way we reduce the processing cost of the
23131 * event listener and the callbacks.
23132 * @extends Roo.dd.DragDrop
23134 * @param {String} id the id of the element that is a drop target
23135 * @param {String} sGroup the group of related DragDrop objects
23136 * @param {object} config an object containing configurable attributes
23137 * Valid properties for DDTarget in addition to those in
23141 Roo.dd.DDTarget = function(id, sGroup, config) {
23143 this.initTarget(id, sGroup, config);
23145 if (config && (config.listeners || config.events)) {
23146 Roo.dd.DragDrop.superclass.constructor.call(this, {
23147 listeners : config.listeners || {},
23148 events : config.events || {}
23153 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23154 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23155 toString: function() {
23156 return ("DDTarget " + this.id);
23161 * Ext JS Library 1.1.1
23162 * Copyright(c) 2006-2007, Ext JS, LLC.
23164 * Originally Released Under LGPL - original licence link has changed is not relivant.
23167 * <script type="text/javascript">
23172 * @class Roo.dd.ScrollManager
23173 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23174 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23177 Roo.dd.ScrollManager = function(){
23178 var ddm = Roo.dd.DragDropMgr;
23185 var onStop = function(e){
23190 var triggerRefresh = function(){
23191 if(ddm.dragCurrent){
23192 ddm.refreshCache(ddm.dragCurrent.groups);
23196 var doScroll = function(){
23197 if(ddm.dragCurrent){
23198 var dds = Roo.dd.ScrollManager;
23200 if(proc.el.scroll(proc.dir, dds.increment)){
23204 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23209 var clearProc = function(){
23211 clearInterval(proc.id);
23218 var startProc = function(el, dir){
23219 Roo.log('scroll startproc');
23223 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23226 var onFire = function(e, isDrop){
23228 if(isDrop || !ddm.dragCurrent){ return; }
23229 var dds = Roo.dd.ScrollManager;
23230 if(!dragEl || dragEl != ddm.dragCurrent){
23231 dragEl = ddm.dragCurrent;
23232 // refresh regions on drag start
23233 dds.refreshCache();
23236 var xy = Roo.lib.Event.getXY(e);
23237 var pt = new Roo.lib.Point(xy[0], xy[1]);
23238 for(var id in els){
23239 var el = els[id], r = el._region;
23240 if(r && r.contains(pt) && el.isScrollable()){
23241 if(r.bottom - pt.y <= dds.thresh){
23243 startProc(el, "down");
23246 }else if(r.right - pt.x <= dds.thresh){
23248 startProc(el, "left");
23251 }else if(pt.y - r.top <= dds.thresh){
23253 startProc(el, "up");
23256 }else if(pt.x - r.left <= dds.thresh){
23258 startProc(el, "right");
23267 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23268 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23272 * Registers new overflow element(s) to auto scroll
23273 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23275 register : function(el){
23276 if(el instanceof Array){
23277 for(var i = 0, len = el.length; i < len; i++) {
23278 this.register(el[i]);
23284 Roo.dd.ScrollManager.els = els;
23288 * Unregisters overflow element(s) so they are no longer scrolled
23289 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23291 unregister : function(el){
23292 if(el instanceof Array){
23293 for(var i = 0, len = el.length; i < len; i++) {
23294 this.unregister(el[i]);
23303 * The number of pixels from the edge of a container the pointer needs to be to
23304 * trigger scrolling (defaults to 25)
23310 * The number of pixels to scroll in each scroll increment (defaults to 50)
23316 * The frequency of scrolls in milliseconds (defaults to 500)
23322 * True to animate the scroll (defaults to true)
23328 * The animation duration in seconds -
23329 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23335 * Manually trigger a cache refresh.
23337 refreshCache : function(){
23338 for(var id in els){
23339 if(typeof els[id] == 'object'){ // for people extending the object prototype
23340 els[id]._region = els[id].getRegion();
23347 * Ext JS Library 1.1.1
23348 * Copyright(c) 2006-2007, Ext JS, LLC.
23350 * Originally Released Under LGPL - original licence link has changed is not relivant.
23353 * <script type="text/javascript">
23358 * @class Roo.dd.Registry
23359 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
23360 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23363 Roo.dd.Registry = function(){
23366 var autoIdSeed = 0;
23368 var getId = function(el, autogen){
23369 if(typeof el == "string"){
23373 if(!id && autogen !== false){
23374 id = "roodd-" + (++autoIdSeed);
23382 * Register a drag drop element
23383 * @param {String|HTMLElement} element The id or DOM node to register
23384 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23385 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
23386 * knows how to interpret, plus there are some specific properties known to the Registry that should be
23387 * populated in the data object (if applicable):
23389 Value Description<br />
23390 --------- ------------------------------------------<br />
23391 handles Array of DOM nodes that trigger dragging<br />
23392 for the element being registered<br />
23393 isHandle True if the element passed in triggers<br />
23394 dragging itself, else false
23397 register : function(el, data){
23399 if(typeof el == "string"){
23400 el = document.getElementById(el);
23403 elements[getId(el)] = data;
23404 if(data.isHandle !== false){
23405 handles[data.ddel.id] = data;
23408 var hs = data.handles;
23409 for(var i = 0, len = hs.length; i < len; i++){
23410 handles[getId(hs[i])] = data;
23416 * Unregister a drag drop element
23417 * @param {String|HTMLElement} element The id or DOM node to unregister
23419 unregister : function(el){
23420 var id = getId(el, false);
23421 var data = elements[id];
23423 delete elements[id];
23425 var hs = data.handles;
23426 for(var i = 0, len = hs.length; i < len; i++){
23427 delete handles[getId(hs[i], false)];
23434 * Returns the handle registered for a DOM Node by id
23435 * @param {String|HTMLElement} id The DOM node or id to look up
23436 * @return {Object} handle The custom handle data
23438 getHandle : function(id){
23439 if(typeof id != "string"){ // must be element?
23442 return handles[id];
23446 * Returns the handle that is registered for the DOM node that is the target of the event
23447 * @param {Event} e The event
23448 * @return {Object} handle The custom handle data
23450 getHandleFromEvent : function(e){
23451 var t = Roo.lib.Event.getTarget(e);
23452 return t ? handles[t.id] : null;
23456 * Returns a custom data object that is registered for a DOM node by id
23457 * @param {String|HTMLElement} id The DOM node or id to look up
23458 * @return {Object} data The custom data
23460 getTarget : function(id){
23461 if(typeof id != "string"){ // must be element?
23464 return elements[id];
23468 * Returns a custom data object that is registered for the DOM node that is the target of the event
23469 * @param {Event} e The event
23470 * @return {Object} data The custom data
23472 getTargetFromEvent : function(e){
23473 var t = Roo.lib.Event.getTarget(e);
23474 return t ? elements[t.id] || handles[t.id] : null;
23479 * Ext JS Library 1.1.1
23480 * Copyright(c) 2006-2007, Ext JS, LLC.
23482 * Originally Released Under LGPL - original licence link has changed is not relivant.
23485 * <script type="text/javascript">
23490 * @class Roo.dd.StatusProxy
23491 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
23492 * default drag proxy used by all Roo.dd components.
23494 * @param {Object} config
23496 Roo.dd.StatusProxy = function(config){
23497 Roo.apply(this, config);
23498 this.id = this.id || Roo.id();
23499 this.el = new Roo.Layer({
23501 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23502 {tag: "div", cls: "x-dd-drop-icon"},
23503 {tag: "div", cls: "x-dd-drag-ghost"}
23506 shadow: !config || config.shadow !== false
23508 this.ghost = Roo.get(this.el.dom.childNodes[1]);
23509 this.dropStatus = this.dropNotAllowed;
23512 Roo.dd.StatusProxy.prototype = {
23514 * @cfg {String} dropAllowed
23515 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23517 dropAllowed : "x-dd-drop-ok",
23519 * @cfg {String} dropNotAllowed
23520 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23522 dropNotAllowed : "x-dd-drop-nodrop",
23525 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23526 * over the current target element.
23527 * @param {String} cssClass The css class for the new drop status indicator image
23529 setStatus : function(cssClass){
23530 cssClass = cssClass || this.dropNotAllowed;
23531 if(this.dropStatus != cssClass){
23532 this.el.replaceClass(this.dropStatus, cssClass);
23533 this.dropStatus = cssClass;
23538 * Resets the status indicator to the default dropNotAllowed value
23539 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23541 reset : function(clearGhost){
23542 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23543 this.dropStatus = this.dropNotAllowed;
23545 this.ghost.update("");
23550 * Updates the contents of the ghost element
23551 * @param {String} html The html that will replace the current innerHTML of the ghost element
23553 update : function(html){
23554 if(typeof html == "string"){
23555 this.ghost.update(html);
23557 this.ghost.update("");
23558 html.style.margin = "0";
23559 this.ghost.dom.appendChild(html);
23561 // ensure float = none set?? cant remember why though.
23562 var el = this.ghost.dom.firstChild;
23564 Roo.fly(el).setStyle('float', 'none');
23569 * Returns the underlying proxy {@link Roo.Layer}
23570 * @return {Roo.Layer} el
23572 getEl : function(){
23577 * Returns the ghost element
23578 * @return {Roo.Element} el
23580 getGhost : function(){
23586 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23588 hide : function(clear){
23596 * Stops the repair animation if it's currently running
23599 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23605 * Displays this proxy
23612 * Force the Layer to sync its shadow and shim positions to the element
23619 * Causes the proxy to return to its position of origin via an animation. Should be called after an
23620 * invalid drop operation by the item being dragged.
23621 * @param {Array} xy The XY position of the element ([x, y])
23622 * @param {Function} callback The function to call after the repair is complete
23623 * @param {Object} scope The scope in which to execute the callback
23625 repair : function(xy, callback, scope){
23626 this.callback = callback;
23627 this.scope = scope;
23628 if(xy && this.animRepair !== false){
23629 this.el.addClass("x-dd-drag-repair");
23630 this.el.hideUnders(true);
23631 this.anim = this.el.shift({
23632 duration: this.repairDuration || .5,
23636 callback: this.afterRepair,
23640 this.afterRepair();
23645 afterRepair : function(){
23647 if(typeof this.callback == "function"){
23648 this.callback.call(this.scope || this);
23650 this.callback = null;
23655 * Ext JS Library 1.1.1
23656 * Copyright(c) 2006-2007, Ext JS, LLC.
23658 * Originally Released Under LGPL - original licence link has changed is not relivant.
23661 * <script type="text/javascript">
23665 * @class Roo.dd.DragSource
23666 * @extends Roo.dd.DDProxy
23667 * A simple class that provides the basic implementation needed to make any element draggable.
23669 * @param {String/HTMLElement/Element} el The container element
23670 * @param {Object} config
23672 Roo.dd.DragSource = function(el, config){
23673 this.el = Roo.get(el);
23674 this.dragData = {};
23676 Roo.apply(this, config);
23679 this.proxy = new Roo.dd.StatusProxy();
23682 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23683 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23685 this.dragging = false;
23688 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23690 * @cfg {String} dropAllowed
23691 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23693 dropAllowed : "x-dd-drop-ok",
23695 * @cfg {String} dropNotAllowed
23696 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23698 dropNotAllowed : "x-dd-drop-nodrop",
23701 * Returns the data object associated with this drag source
23702 * @return {Object} data An object containing arbitrary data
23704 getDragData : function(e){
23705 return this.dragData;
23709 onDragEnter : function(e, id){
23710 var target = Roo.dd.DragDropMgr.getDDById(id);
23711 this.cachedTarget = target;
23712 if(this.beforeDragEnter(target, e, id) !== false){
23713 if(target.isNotifyTarget){
23714 var status = target.notifyEnter(this, e, this.dragData);
23715 this.proxy.setStatus(status);
23717 this.proxy.setStatus(this.dropAllowed);
23720 if(this.afterDragEnter){
23722 * An empty function by default, but provided so that you can perform a custom action
23723 * when the dragged item enters the drop target by providing an implementation.
23724 * @param {Roo.dd.DragDrop} target The drop target
23725 * @param {Event} e The event object
23726 * @param {String} id The id of the dragged element
23727 * @method afterDragEnter
23729 this.afterDragEnter(target, e, id);
23735 * An empty function by default, but provided so that you can perform a custom action
23736 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23737 * @param {Roo.dd.DragDrop} target The drop target
23738 * @param {Event} e The event object
23739 * @param {String} id The id of the dragged element
23740 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23742 beforeDragEnter : function(target, e, id){
23747 alignElWithMouse: function() {
23748 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23753 onDragOver : function(e, id){
23754 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23755 if(this.beforeDragOver(target, e, id) !== false){
23756 if(target.isNotifyTarget){
23757 var status = target.notifyOver(this, e, this.dragData);
23758 this.proxy.setStatus(status);
23761 if(this.afterDragOver){
23763 * An empty function by default, but provided so that you can perform a custom action
23764 * while the dragged item is over the drop target by providing an implementation.
23765 * @param {Roo.dd.DragDrop} target The drop target
23766 * @param {Event} e The event object
23767 * @param {String} id The id of the dragged element
23768 * @method afterDragOver
23770 this.afterDragOver(target, e, id);
23776 * An empty function by default, but provided so that you can perform a custom action
23777 * while the dragged item is over the drop target and optionally cancel the onDragOver.
23778 * @param {Roo.dd.DragDrop} target The drop target
23779 * @param {Event} e The event object
23780 * @param {String} id The id of the dragged element
23781 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23783 beforeDragOver : function(target, e, id){
23788 onDragOut : function(e, id){
23789 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23790 if(this.beforeDragOut(target, e, id) !== false){
23791 if(target.isNotifyTarget){
23792 target.notifyOut(this, e, this.dragData);
23794 this.proxy.reset();
23795 if(this.afterDragOut){
23797 * An empty function by default, but provided so that you can perform a custom action
23798 * after the dragged item is dragged out of the target without dropping.
23799 * @param {Roo.dd.DragDrop} target The drop target
23800 * @param {Event} e The event object
23801 * @param {String} id The id of the dragged element
23802 * @method afterDragOut
23804 this.afterDragOut(target, e, id);
23807 this.cachedTarget = null;
23811 * An empty function by default, but provided so that you can perform a custom action before the dragged
23812 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23813 * @param {Roo.dd.DragDrop} target The drop target
23814 * @param {Event} e The event object
23815 * @param {String} id The id of the dragged element
23816 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23818 beforeDragOut : function(target, e, id){
23823 onDragDrop : function(e, id){
23824 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23825 if(this.beforeDragDrop(target, e, id) !== false){
23826 if(target.isNotifyTarget){
23827 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23828 this.onValidDrop(target, e, id);
23830 this.onInvalidDrop(target, e, id);
23833 this.onValidDrop(target, e, id);
23836 if(this.afterDragDrop){
23838 * An empty function by default, but provided so that you can perform a custom action
23839 * after a valid drag drop has occurred by providing an implementation.
23840 * @param {Roo.dd.DragDrop} target The drop target
23841 * @param {Event} e The event object
23842 * @param {String} id The id of the dropped element
23843 * @method afterDragDrop
23845 this.afterDragDrop(target, e, id);
23848 delete this.cachedTarget;
23852 * An empty function by default, but provided so that you can perform a custom action before the dragged
23853 * item is dropped onto the target and optionally cancel the onDragDrop.
23854 * @param {Roo.dd.DragDrop} target The drop target
23855 * @param {Event} e The event object
23856 * @param {String} id The id of the dragged element
23857 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23859 beforeDragDrop : function(target, e, id){
23864 onValidDrop : function(target, e, id){
23866 if(this.afterValidDrop){
23868 * An empty function by default, but provided so that you can perform a custom action
23869 * after a valid drop has occurred by providing an implementation.
23870 * @param {Object} target The target DD
23871 * @param {Event} e The event object
23872 * @param {String} id The id of the dropped element
23873 * @method afterInvalidDrop
23875 this.afterValidDrop(target, e, id);
23880 getRepairXY : function(e, data){
23881 return this.el.getXY();
23885 onInvalidDrop : function(target, e, id){
23886 this.beforeInvalidDrop(target, e, id);
23887 if(this.cachedTarget){
23888 if(this.cachedTarget.isNotifyTarget){
23889 this.cachedTarget.notifyOut(this, e, this.dragData);
23891 this.cacheTarget = null;
23893 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23895 if(this.afterInvalidDrop){
23897 * An empty function by default, but provided so that you can perform a custom action
23898 * after an invalid drop has occurred by providing an implementation.
23899 * @param {Event} e The event object
23900 * @param {String} id The id of the dropped element
23901 * @method afterInvalidDrop
23903 this.afterInvalidDrop(e, id);
23908 afterRepair : function(){
23910 this.el.highlight(this.hlColor || "c3daf9");
23912 this.dragging = false;
23916 * An empty function by default, but provided so that you can perform a custom action after an invalid
23917 * drop has occurred.
23918 * @param {Roo.dd.DragDrop} target The drop target
23919 * @param {Event} e The event object
23920 * @param {String} id The id of the dragged element
23921 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23923 beforeInvalidDrop : function(target, e, id){
23928 handleMouseDown : function(e){
23929 if(this.dragging) {
23932 var data = this.getDragData(e);
23933 if(data && this.onBeforeDrag(data, e) !== false){
23934 this.dragData = data;
23936 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23941 * An empty function by default, but provided so that you can perform a custom action before the initial
23942 * drag event begins and optionally cancel it.
23943 * @param {Object} data An object containing arbitrary data to be shared with drop targets
23944 * @param {Event} e The event object
23945 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23947 onBeforeDrag : function(data, e){
23952 * An empty function by default, but provided so that you can perform a custom action once the initial
23953 * drag event has begun. The drag cannot be canceled from this function.
23954 * @param {Number} x The x position of the click on the dragged object
23955 * @param {Number} y The y position of the click on the dragged object
23957 onStartDrag : Roo.emptyFn,
23959 // private - YUI override
23960 startDrag : function(x, y){
23961 this.proxy.reset();
23962 this.dragging = true;
23963 this.proxy.update("");
23964 this.onInitDrag(x, y);
23969 onInitDrag : function(x, y){
23970 var clone = this.el.dom.cloneNode(true);
23971 clone.id = Roo.id(); // prevent duplicate ids
23972 this.proxy.update(clone);
23973 this.onStartDrag(x, y);
23978 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23979 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23981 getProxy : function(){
23986 * Hides the drag source's {@link Roo.dd.StatusProxy}
23988 hideProxy : function(){
23990 this.proxy.reset(true);
23991 this.dragging = false;
23995 triggerCacheRefresh : function(){
23996 Roo.dd.DDM.refreshCache(this.groups);
23999 // private - override to prevent hiding
24000 b4EndDrag: function(e) {
24003 // private - override to prevent moving
24004 endDrag : function(e){
24005 this.onEndDrag(this.dragData, e);
24009 onEndDrag : function(data, e){
24012 // private - pin to cursor
24013 autoOffset : function(x, y) {
24014 this.setDelta(-12, -20);
24018 * Ext JS Library 1.1.1
24019 * Copyright(c) 2006-2007, Ext JS, LLC.
24021 * Originally Released Under LGPL - original licence link has changed is not relivant.
24024 * <script type="text/javascript">
24029 * @class Roo.dd.DropTarget
24030 * @extends Roo.dd.DDTarget
24031 * A simple class that provides the basic implementation needed to make any element a drop target that can have
24032 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
24034 * @param {String/HTMLElement/Element} el The container element
24035 * @param {Object} config
24037 Roo.dd.DropTarget = function(el, config){
24038 this.el = Roo.get(el);
24040 var listeners = false; ;
24041 if (config && config.listeners) {
24042 listeners= config.listeners;
24043 delete config.listeners;
24045 Roo.apply(this, config);
24047 if(this.containerScroll){
24048 Roo.dd.ScrollManager.register(this.el);
24052 * @scope Roo.dd.DropTarget
24057 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24058 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
24059 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
24061 * IMPORTANT : it should set this.valid to true|false
24063 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24064 * @param {Event} e The event
24065 * @param {Object} data An object containing arbitrary data supplied by the drag source
24071 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24072 * This method will be called on every mouse movement while the drag source is over the drop target.
24073 * This default implementation simply returns the dropAllowed config value.
24075 * IMPORTANT : it should set this.valid to true|false
24077 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24078 * @param {Event} e The event
24079 * @param {Object} data An object containing arbitrary data supplied by the drag source
24085 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24086 * out of the target without dropping. This default implementation simply removes the CSS class specified by
24087 * overClass (if any) from the drop element.
24090 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24091 * @param {Event} e The event
24092 * @param {Object} data An object containing arbitrary data supplied by the drag source
24098 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24099 * been dropped on it. This method has no default implementation and returns false, so you must provide an
24100 * implementation that does something to process the drop event and returns true so that the drag source's
24101 * repair action does not run.
24103 * IMPORTANT : it should set this.success
24105 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24106 * @param {Event} e The event
24107 * @param {Object} data An object containing arbitrary data supplied by the drag source
24113 Roo.dd.DropTarget.superclass.constructor.call( this,
24115 this.ddGroup || this.group,
24118 listeners : listeners || {}
24126 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24128 * @cfg {String} overClass
24129 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24132 * @cfg {String} ddGroup
24133 * The drag drop group to handle drop events for
24137 * @cfg {String} dropAllowed
24138 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24140 dropAllowed : "x-dd-drop-ok",
24142 * @cfg {String} dropNotAllowed
24143 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24145 dropNotAllowed : "x-dd-drop-nodrop",
24147 * @cfg {boolean} success
24148 * set this after drop listener..
24152 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24153 * if the drop point is valid for over/enter..
24160 isNotifyTarget : true,
24165 notifyEnter : function(dd, e, data)
24168 this.fireEvent('enter', dd, e, data);
24169 if(this.overClass){
24170 this.el.addClass(this.overClass);
24172 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24173 this.valid ? this.dropAllowed : this.dropNotAllowed
24180 notifyOver : function(dd, e, data)
24183 this.fireEvent('over', dd, e, data);
24184 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24185 this.valid ? this.dropAllowed : this.dropNotAllowed
24192 notifyOut : function(dd, e, data)
24194 this.fireEvent('out', dd, e, data);
24195 if(this.overClass){
24196 this.el.removeClass(this.overClass);
24203 notifyDrop : function(dd, e, data)
24205 this.success = false;
24206 this.fireEvent('drop', dd, e, data);
24207 return this.success;
24211 * Ext JS Library 1.1.1
24212 * Copyright(c) 2006-2007, Ext JS, LLC.
24214 * Originally Released Under LGPL - original licence link has changed is not relivant.
24217 * <script type="text/javascript">
24222 * @class Roo.dd.DragZone
24223 * @extends Roo.dd.DragSource
24224 * This class provides a container DD instance that proxies for multiple child node sources.<br />
24225 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24227 * @param {String/HTMLElement/Element} el The container element
24228 * @param {Object} config
24230 Roo.dd.DragZone = function(el, config){
24231 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24232 if(this.containerScroll){
24233 Roo.dd.ScrollManager.register(this.el);
24237 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24239 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24240 * for auto scrolling during drag operations.
24243 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24244 * method after a failed drop (defaults to "c3daf9" - light blue)
24248 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24249 * for a valid target to drag based on the mouse down. Override this method
24250 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24251 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24252 * @param {EventObject} e The mouse down event
24253 * @return {Object} The dragData
24255 getDragData : function(e){
24256 return Roo.dd.Registry.getHandleFromEvent(e);
24260 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24261 * this.dragData.ddel
24262 * @param {Number} x The x position of the click on the dragged object
24263 * @param {Number} y The y position of the click on the dragged object
24264 * @return {Boolean} true to continue the drag, false to cancel
24266 onInitDrag : function(x, y){
24267 this.proxy.update(this.dragData.ddel.cloneNode(true));
24268 this.onStartDrag(x, y);
24273 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
24275 afterRepair : function(){
24277 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24279 this.dragging = false;
24283 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24284 * the XY of this.dragData.ddel
24285 * @param {EventObject} e The mouse up event
24286 * @return {Array} The xy location (e.g. [100, 200])
24288 getRepairXY : function(e){
24289 return Roo.Element.fly(this.dragData.ddel).getXY();
24293 * Ext JS Library 1.1.1
24294 * Copyright(c) 2006-2007, Ext JS, LLC.
24296 * Originally Released Under LGPL - original licence link has changed is not relivant.
24299 * <script type="text/javascript">
24302 * @class Roo.dd.DropZone
24303 * @extends Roo.dd.DropTarget
24304 * This class provides a container DD instance that proxies for multiple child node targets.<br />
24305 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24307 * @param {String/HTMLElement/Element} el The container element
24308 * @param {Object} config
24310 Roo.dd.DropZone = function(el, config){
24311 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24314 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24316 * Returns a custom data object associated with the DOM node that is the target of the event. By default
24317 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24318 * provide your own custom lookup.
24319 * @param {Event} e The event
24320 * @return {Object} data The custom data
24322 getTargetFromEvent : function(e){
24323 return Roo.dd.Registry.getTargetFromEvent(e);
24327 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24328 * that it has registered. This method has no default implementation and should be overridden to provide
24329 * node-specific processing if necessary.
24330 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24331 * {@link #getTargetFromEvent} for this node)
24332 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24333 * @param {Event} e The event
24334 * @param {Object} data An object containing arbitrary data supplied by the drag source
24336 onNodeEnter : function(n, dd, e, data){
24341 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24342 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
24343 * overridden to provide the proper feedback.
24344 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24345 * {@link #getTargetFromEvent} for this node)
24346 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24347 * @param {Event} e The event
24348 * @param {Object} data An object containing arbitrary data supplied by the drag source
24349 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24350 * underlying {@link Roo.dd.StatusProxy} can be updated
24352 onNodeOver : function(n, dd, e, data){
24353 return this.dropAllowed;
24357 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24358 * the drop node without dropping. This method has no default implementation and should be overridden to provide
24359 * node-specific processing if necessary.
24360 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24361 * {@link #getTargetFromEvent} for this node)
24362 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24363 * @param {Event} e The event
24364 * @param {Object} data An object containing arbitrary data supplied by the drag source
24366 onNodeOut : function(n, dd, e, data){
24371 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24372 * the drop node. The default implementation returns false, so it should be overridden to provide the
24373 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24374 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24375 * {@link #getTargetFromEvent} for this node)
24376 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24377 * @param {Event} e The event
24378 * @param {Object} data An object containing arbitrary data supplied by the drag source
24379 * @return {Boolean} True if the drop was valid, else false
24381 onNodeDrop : function(n, dd, e, data){
24386 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24387 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
24388 * it should be overridden to provide the proper feedback if necessary.
24389 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24390 * @param {Event} e The event
24391 * @param {Object} data An object containing arbitrary data supplied by the drag source
24392 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24393 * underlying {@link Roo.dd.StatusProxy} can be updated
24395 onContainerOver : function(dd, e, data){
24396 return this.dropNotAllowed;
24400 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24401 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
24402 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24403 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
24404 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24405 * @param {Event} e The event
24406 * @param {Object} data An object containing arbitrary data supplied by the drag source
24407 * @return {Boolean} True if the drop was valid, else false
24409 onContainerDrop : function(dd, e, data){
24414 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24415 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
24416 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24417 * you should override this method and provide a custom implementation.
24418 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24419 * @param {Event} e The event
24420 * @param {Object} data An object containing arbitrary data supplied by the drag source
24421 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24422 * underlying {@link Roo.dd.StatusProxy} can be updated
24424 notifyEnter : function(dd, e, data){
24425 return this.dropNotAllowed;
24429 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24430 * This method will be called on every mouse movement while the drag source is over the drop zone.
24431 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24432 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24433 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24434 * registered node, it will call {@link #onContainerOver}.
24435 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24436 * @param {Event} e The event
24437 * @param {Object} data An object containing arbitrary data supplied by the drag source
24438 * @return {String} status The CSS class that communicates the drop status back to the source so that the
24439 * underlying {@link Roo.dd.StatusProxy} can be updated
24441 notifyOver : function(dd, e, data){
24442 var n = this.getTargetFromEvent(e);
24443 if(!n){ // not over valid drop target
24444 if(this.lastOverNode){
24445 this.onNodeOut(this.lastOverNode, dd, e, data);
24446 this.lastOverNode = null;
24448 return this.onContainerOver(dd, e, data);
24450 if(this.lastOverNode != n){
24451 if(this.lastOverNode){
24452 this.onNodeOut(this.lastOverNode, dd, e, data);
24454 this.onNodeEnter(n, dd, e, data);
24455 this.lastOverNode = n;
24457 return this.onNodeOver(n, dd, e, data);
24461 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24462 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
24463 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24464 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24465 * @param {Event} e The event
24466 * @param {Object} data An object containing arbitrary data supplied by the drag zone
24468 notifyOut : function(dd, e, data){
24469 if(this.lastOverNode){
24470 this.onNodeOut(this.lastOverNode, dd, e, data);
24471 this.lastOverNode = null;
24476 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24477 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
24478 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24479 * otherwise it will call {@link #onContainerDrop}.
24480 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24481 * @param {Event} e The event
24482 * @param {Object} data An object containing arbitrary data supplied by the drag source
24483 * @return {Boolean} True if the drop was valid, else false
24485 notifyDrop : function(dd, e, data){
24486 if(this.lastOverNode){
24487 this.onNodeOut(this.lastOverNode, dd, e, data);
24488 this.lastOverNode = null;
24490 var n = this.getTargetFromEvent(e);
24492 this.onNodeDrop(n, dd, e, data) :
24493 this.onContainerDrop(dd, e, data);
24497 triggerCacheRefresh : function(){
24498 Roo.dd.DDM.refreshCache(this.groups);
24502 * Ext JS Library 1.1.1
24503 * Copyright(c) 2006-2007, Ext JS, LLC.
24505 * Originally Released Under LGPL - original licence link has changed is not relivant.
24508 * <script type="text/javascript">
24513 * @class Roo.data.SortTypes
24515 * Defines the default sorting (casting?) comparison functions used when sorting data.
24517 Roo.data.SortTypes = {
24519 * Default sort that does nothing
24520 * @param {Mixed} s The value being converted
24521 * @return {Mixed} The comparison value
24523 none : function(s){
24528 * The regular expression used to strip tags
24532 stripTagsRE : /<\/?[^>]+>/gi,
24535 * Strips all HTML tags to sort on text only
24536 * @param {Mixed} s The value being converted
24537 * @return {String} The comparison value
24539 asText : function(s){
24540 return String(s).replace(this.stripTagsRE, "");
24544 * Strips all HTML tags to sort on text only - Case insensitive
24545 * @param {Mixed} s The value being converted
24546 * @return {String} The comparison value
24548 asUCText : function(s){
24549 return String(s).toUpperCase().replace(this.stripTagsRE, "");
24553 * Case insensitive string
24554 * @param {Mixed} s The value being converted
24555 * @return {String} The comparison value
24557 asUCString : function(s) {
24558 return String(s).toUpperCase();
24563 * @param {Mixed} s The value being converted
24564 * @return {Number} The comparison value
24566 asDate : function(s) {
24570 if(s instanceof Date){
24571 return s.getTime();
24573 return Date.parse(String(s));
24578 * @param {Mixed} s The value being converted
24579 * @return {Float} The comparison value
24581 asFloat : function(s) {
24582 var val = parseFloat(String(s).replace(/,/g, ""));
24591 * @param {Mixed} s The value being converted
24592 * @return {Number} The comparison value
24594 asInt : function(s) {
24595 var val = parseInt(String(s).replace(/,/g, ""));
24603 * Ext JS Library 1.1.1
24604 * Copyright(c) 2006-2007, Ext JS, LLC.
24606 * Originally Released Under LGPL - original licence link has changed is not relivant.
24609 * <script type="text/javascript">
24613 * @class Roo.data.Record
24614 * Instances of this class encapsulate both record <em>definition</em> information, and record
24615 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24616 * to access Records cached in an {@link Roo.data.Store} object.<br>
24618 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24619 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24622 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24624 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24625 * {@link #create}. The parameters are the same.
24626 * @param {Array} data An associative Array of data values keyed by the field name.
24627 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24628 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24629 * not specified an integer id is generated.
24631 Roo.data.Record = function(data, id){
24632 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24637 * Generate a constructor for a specific record layout.
24638 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24639 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24640 * Each field definition object may contain the following properties: <ul>
24641 * <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,
24642 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24643 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24644 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24645 * is being used, then this is a string containing the javascript expression to reference the data relative to
24646 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24647 * to the data item relative to the record element. If the mapping expression is the same as the field name,
24648 * this may be omitted.</p></li>
24649 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24650 * <ul><li>auto (Default, implies no conversion)</li>
24655 * <li>date</li></ul></p></li>
24656 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24657 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24658 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24659 * by the Reader into an object that will be stored in the Record. It is passed the
24660 * following parameters:<ul>
24661 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24663 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24665 * <br>usage:<br><pre><code>
24666 var TopicRecord = Roo.data.Record.create(
24667 {name: 'title', mapping: 'topic_title'},
24668 {name: 'author', mapping: 'username'},
24669 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24670 {name: 'lastPost', mapping: 'post_time', type: 'date'},
24671 {name: 'lastPoster', mapping: 'user2'},
24672 {name: 'excerpt', mapping: 'post_text'}
24675 var myNewRecord = new TopicRecord({
24676 title: 'Do my job please',
24679 lastPost: new Date(),
24680 lastPoster: 'Animal',
24681 excerpt: 'No way dude!'
24683 myStore.add(myNewRecord);
24688 Roo.data.Record.create = function(o){
24689 var f = function(){
24690 f.superclass.constructor.apply(this, arguments);
24692 Roo.extend(f, Roo.data.Record);
24693 var p = f.prototype;
24694 p.fields = new Roo.util.MixedCollection(false, function(field){
24697 for(var i = 0, len = o.length; i < len; i++){
24698 p.fields.add(new Roo.data.Field(o[i]));
24700 f.getField = function(name){
24701 return p.fields.get(name);
24706 Roo.data.Record.AUTO_ID = 1000;
24707 Roo.data.Record.EDIT = 'edit';
24708 Roo.data.Record.REJECT = 'reject';
24709 Roo.data.Record.COMMIT = 'commit';
24711 Roo.data.Record.prototype = {
24713 * Readonly flag - true if this record has been modified.
24722 join : function(store){
24723 this.store = store;
24727 * Set the named field to the specified value.
24728 * @param {String} name The name of the field to set.
24729 * @param {Object} value The value to set the field to.
24731 set : function(name, value){
24732 if(this.data[name] == value){
24736 if(!this.modified){
24737 this.modified = {};
24739 if(typeof this.modified[name] == 'undefined'){
24740 this.modified[name] = this.data[name];
24742 this.data[name] = value;
24743 if(!this.editing && this.store){
24744 this.store.afterEdit(this);
24749 * Get the value of the named field.
24750 * @param {String} name The name of the field to get the value of.
24751 * @return {Object} The value of the field.
24753 get : function(name){
24754 return this.data[name];
24758 beginEdit : function(){
24759 this.editing = true;
24760 this.modified = {};
24764 cancelEdit : function(){
24765 this.editing = false;
24766 delete this.modified;
24770 endEdit : function(){
24771 this.editing = false;
24772 if(this.dirty && this.store){
24773 this.store.afterEdit(this);
24778 * Usually called by the {@link Roo.data.Store} which owns the Record.
24779 * Rejects all changes made to the Record since either creation, or the last commit operation.
24780 * Modified fields are reverted to their original values.
24782 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24783 * of reject operations.
24785 reject : function(){
24786 var m = this.modified;
24788 if(typeof m[n] != "function"){
24789 this.data[n] = m[n];
24792 this.dirty = false;
24793 delete this.modified;
24794 this.editing = false;
24796 this.store.afterReject(this);
24801 * Usually called by the {@link Roo.data.Store} which owns the Record.
24802 * Commits all changes made to the Record since either creation, or the last commit operation.
24804 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24805 * of commit operations.
24807 commit : function(){
24808 this.dirty = false;
24809 delete this.modified;
24810 this.editing = false;
24812 this.store.afterCommit(this);
24817 hasError : function(){
24818 return this.error != null;
24822 clearError : function(){
24827 * Creates a copy of this record.
24828 * @param {String} id (optional) A new record id if you don't want to use this record's id
24831 copy : function(newId) {
24832 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24836 * Ext JS Library 1.1.1
24837 * Copyright(c) 2006-2007, Ext JS, LLC.
24839 * Originally Released Under LGPL - original licence link has changed is not relivant.
24842 * <script type="text/javascript">
24848 * @class Roo.data.Store
24849 * @extends Roo.util.Observable
24850 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24851 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24853 * 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
24854 * has no knowledge of the format of the data returned by the Proxy.<br>
24856 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24857 * instances from the data object. These records are cached and made available through accessor functions.
24859 * Creates a new Store.
24860 * @param {Object} config A config object containing the objects needed for the Store to access data,
24861 * and read the data into Records.
24863 Roo.data.Store = function(config){
24864 this.data = new Roo.util.MixedCollection(false);
24865 this.data.getKey = function(o){
24868 this.baseParams = {};
24870 this.paramNames = {
24875 "multisort" : "_multisort"
24878 if(config && config.data){
24879 this.inlineData = config.data;
24880 delete config.data;
24883 Roo.apply(this, config);
24885 if(this.reader){ // reader passed
24886 this.reader = Roo.factory(this.reader, Roo.data);
24887 this.reader.xmodule = this.xmodule || false;
24888 if(!this.recordType){
24889 this.recordType = this.reader.recordType;
24891 if(this.reader.onMetaChange){
24892 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24896 if(this.recordType){
24897 this.fields = this.recordType.prototype.fields;
24899 this.modified = [];
24903 * @event datachanged
24904 * Fires when the data cache has changed, and a widget which is using this Store
24905 * as a Record cache should refresh its view.
24906 * @param {Store} this
24908 datachanged : true,
24910 * @event metachange
24911 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24912 * @param {Store} this
24913 * @param {Object} meta The JSON metadata
24918 * Fires when Records have been added to the Store
24919 * @param {Store} this
24920 * @param {Roo.data.Record[]} records The array of Records added
24921 * @param {Number} index The index at which the record(s) were added
24926 * Fires when a Record has been removed from the Store
24927 * @param {Store} this
24928 * @param {Roo.data.Record} record The Record that was removed
24929 * @param {Number} index The index at which the record was removed
24934 * Fires when a Record has been updated
24935 * @param {Store} this
24936 * @param {Roo.data.Record} record The Record that was updated
24937 * @param {String} operation The update operation being performed. Value may be one of:
24939 Roo.data.Record.EDIT
24940 Roo.data.Record.REJECT
24941 Roo.data.Record.COMMIT
24947 * Fires when the data cache has been cleared.
24948 * @param {Store} this
24952 * @event beforeload
24953 * Fires before a request is made for a new data object. If the beforeload handler returns false
24954 * the load action will be canceled.
24955 * @param {Store} this
24956 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24960 * @event beforeloadadd
24961 * Fires after a new set of Records has been loaded.
24962 * @param {Store} this
24963 * @param {Roo.data.Record[]} records The Records that were loaded
24964 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24966 beforeloadadd : true,
24969 * Fires after a new set of Records has been loaded, before they are added to the store.
24970 * @param {Store} this
24971 * @param {Roo.data.Record[]} records The Records that were loaded
24972 * @param {Object} options The loading options that were specified (see {@link #load} for details)
24973 * @params {Object} return from reader
24977 * @event loadexception
24978 * Fires if an exception occurs in the Proxy during loading.
24979 * Called with the signature of the Proxy's "loadexception" event.
24980 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24983 * @param {Object} return from JsonData.reader() - success, totalRecords, records
24984 * @param {Object} load options
24985 * @param {Object} jsonData from your request (normally this contains the Exception)
24987 loadexception : true
24991 this.proxy = Roo.factory(this.proxy, Roo.data);
24992 this.proxy.xmodule = this.xmodule || false;
24993 this.relayEvents(this.proxy, ["loadexception"]);
24995 this.sortToggle = {};
24996 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24998 Roo.data.Store.superclass.constructor.call(this);
25000 if(this.inlineData){
25001 this.loadData(this.inlineData);
25002 delete this.inlineData;
25006 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25008 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
25009 * without a remote query - used by combo/forms at present.
25013 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25016 * @cfg {Array} data Inline data to be loaded when the store is initialized.
25019 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
25020 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25023 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25024 * on any HTTP request
25027 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25030 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25034 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25035 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25037 remoteSort : false,
25040 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25041 * loaded or when a record is removed. (defaults to false).
25043 pruneModifiedRecords : false,
25046 lastOptions : null,
25049 * Add Records to the Store and fires the add event.
25050 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25052 add : function(records){
25053 records = [].concat(records);
25054 for(var i = 0, len = records.length; i < len; i++){
25055 records[i].join(this);
25057 var index = this.data.length;
25058 this.data.addAll(records);
25059 this.fireEvent("add", this, records, index);
25063 * Remove a Record from the Store and fires the remove event.
25064 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25066 remove : function(record){
25067 var index = this.data.indexOf(record);
25068 this.data.removeAt(index);
25070 if(this.pruneModifiedRecords){
25071 this.modified.remove(record);
25073 this.fireEvent("remove", this, record, index);
25077 * Remove all Records from the Store and fires the clear event.
25079 removeAll : function(){
25081 if(this.pruneModifiedRecords){
25082 this.modified = [];
25084 this.fireEvent("clear", this);
25088 * Inserts Records to the Store at the given index and fires the add event.
25089 * @param {Number} index The start index at which to insert the passed Records.
25090 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25092 insert : function(index, records){
25093 records = [].concat(records);
25094 for(var i = 0, len = records.length; i < len; i++){
25095 this.data.insert(index, records[i]);
25096 records[i].join(this);
25098 this.fireEvent("add", this, records, index);
25102 * Get the index within the cache of the passed Record.
25103 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25104 * @return {Number} The index of the passed Record. Returns -1 if not found.
25106 indexOf : function(record){
25107 return this.data.indexOf(record);
25111 * Get the index within the cache of the Record with the passed id.
25112 * @param {String} id The id of the Record to find.
25113 * @return {Number} The index of the Record. Returns -1 if not found.
25115 indexOfId : function(id){
25116 return this.data.indexOfKey(id);
25120 * Get the Record with the specified id.
25121 * @param {String} id The id of the Record to find.
25122 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25124 getById : function(id){
25125 return this.data.key(id);
25129 * Get the Record at the specified index.
25130 * @param {Number} index The index of the Record to find.
25131 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25133 getAt : function(index){
25134 return this.data.itemAt(index);
25138 * Returns a range of Records between specified indices.
25139 * @param {Number} startIndex (optional) The starting index (defaults to 0)
25140 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25141 * @return {Roo.data.Record[]} An array of Records
25143 getRange : function(start, end){
25144 return this.data.getRange(start, end);
25148 storeOptions : function(o){
25149 o = Roo.apply({}, o);
25152 this.lastOptions = o;
25156 * Loads the Record cache from the configured Proxy using the configured Reader.
25158 * If using remote paging, then the first load call must specify the <em>start</em>
25159 * and <em>limit</em> properties in the options.params property to establish the initial
25160 * position within the dataset, and the number of Records to cache on each read from the Proxy.
25162 * <strong>It is important to note that for remote data sources, loading is asynchronous,
25163 * and this call will return before the new data has been loaded. Perform any post-processing
25164 * in a callback function, or in a "load" event handler.</strong>
25166 * @param {Object} options An object containing properties which control loading options:<ul>
25167 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25168 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25171 data : data, // array of key=>value data like JsonReader
25172 total : data.length,
25178 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25179 * passed the following arguments:<ul>
25180 * <li>r : Roo.data.Record[]</li>
25181 * <li>options: Options object from the load call</li>
25182 * <li>success: Boolean success indicator</li></ul></li>
25183 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25184 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25187 load : function(options){
25188 options = options || {};
25189 if(this.fireEvent("beforeload", this, options) !== false){
25190 this.storeOptions(options);
25191 var p = Roo.apply(options.params || {}, this.baseParams);
25192 // if meta was not loaded from remote source.. try requesting it.
25193 if (!this.reader.metaFromRemote) {
25194 p._requestMeta = 1;
25196 if(this.sortInfo && this.remoteSort){
25197 var pn = this.paramNames;
25198 p[pn["sort"]] = this.sortInfo.field;
25199 p[pn["dir"]] = this.sortInfo.direction;
25201 if (this.multiSort) {
25202 var pn = this.paramNames;
25203 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25206 this.proxy.load(p, this.reader, this.loadRecords, this, options);
25211 * Reloads the Record cache from the configured Proxy using the configured Reader and
25212 * the options from the last load operation performed.
25213 * @param {Object} options (optional) An object containing properties which may override the options
25214 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25215 * the most recently used options are reused).
25217 reload : function(options){
25218 this.load(Roo.applyIf(options||{}, this.lastOptions));
25222 // Called as a callback by the Reader during a load operation.
25223 loadRecords : function(o, options, success){
25226 if(success !== false){
25227 this.fireEvent("load", this, [], options, o);
25229 if(options.callback){
25230 options.callback.call(options.scope || this, [], options, false);
25234 // if data returned failure - throw an exception.
25235 if (o.success === false) {
25236 // show a message if no listener is registered.
25237 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25238 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25240 // loadmask wil be hooked into this..
25241 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25244 var r = o.records, t = o.totalRecords || r.length;
25246 this.fireEvent("beforeloadadd", this, r, options, o);
25248 if(!options || options.add !== true){
25249 if(this.pruneModifiedRecords){
25250 this.modified = [];
25252 for(var i = 0, len = r.length; i < len; i++){
25256 this.data = this.snapshot;
25257 delete this.snapshot;
25260 this.data.addAll(r);
25261 this.totalLength = t;
25263 this.fireEvent("datachanged", this);
25265 this.totalLength = Math.max(t, this.data.length+r.length);
25269 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25271 var e = new Roo.data.Record({});
25273 e.set(this.parent.displayField, this.parent.emptyTitle);
25274 e.set(this.parent.valueField, '');
25279 this.fireEvent("load", this, r, options, o);
25280 if(options.callback){
25281 options.callback.call(options.scope || this, r, options, true);
25287 * Loads data from a passed data block. A Reader which understands the format of the data
25288 * must have been configured in the constructor.
25289 * @param {Object} data The data block from which to read the Records. The format of the data expected
25290 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25291 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25293 loadData : function(o, append){
25294 var r = this.reader.readRecords(o);
25295 this.loadRecords(r, {add: append}, true);
25299 * using 'cn' the nested child reader read the child array into it's child stores.
25300 * @param {Object} rec The record with a 'children array
25302 loadDataFromChildren : function(rec)
25304 this.loadData(this.reader.toLoadData(rec));
25309 * Gets the number of cached records.
25311 * <em>If using paging, this may not be the total size of the dataset. If the data object
25312 * used by the Reader contains the dataset size, then the getTotalCount() function returns
25313 * the data set size</em>
25315 getCount : function(){
25316 return this.data.length || 0;
25320 * Gets the total number of records in the dataset as returned by the server.
25322 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25323 * the dataset size</em>
25325 getTotalCount : function(){
25326 return this.totalLength || 0;
25330 * Returns the sort state of the Store as an object with two properties:
25332 field {String} The name of the field by which the Records are sorted
25333 direction {String} The sort order, "ASC" or "DESC"
25336 getSortState : function(){
25337 return this.sortInfo;
25341 applySort : function(){
25342 if(this.sortInfo && !this.remoteSort){
25343 var s = this.sortInfo, f = s.field;
25344 var st = this.fields.get(f).sortType;
25345 var fn = function(r1, r2){
25346 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25347 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25349 this.data.sort(s.direction, fn);
25350 if(this.snapshot && this.snapshot != this.data){
25351 this.snapshot.sort(s.direction, fn);
25357 * Sets the default sort column and order to be used by the next load operation.
25358 * @param {String} fieldName The name of the field to sort by.
25359 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25361 setDefaultSort : function(field, dir){
25362 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25366 * Sort the Records.
25367 * If remote sorting is used, the sort is performed on the server, and the cache is
25368 * reloaded. If local sorting is used, the cache is sorted internally.
25369 * @param {String} fieldName The name of the field to sort by.
25370 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25372 sort : function(fieldName, dir){
25373 var f = this.fields.get(fieldName);
25375 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25377 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25378 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25383 this.sortToggle[f.name] = dir;
25384 this.sortInfo = {field: f.name, direction: dir};
25385 if(!this.remoteSort){
25387 this.fireEvent("datachanged", this);
25389 this.load(this.lastOptions);
25394 * Calls the specified function for each of the Records in the cache.
25395 * @param {Function} fn The function to call. The Record is passed as the first parameter.
25396 * Returning <em>false</em> aborts and exits the iteration.
25397 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25399 each : function(fn, scope){
25400 this.data.each(fn, scope);
25404 * Gets all records modified since the last commit. Modified records are persisted across load operations
25405 * (e.g., during paging).
25406 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25408 getModifiedRecords : function(){
25409 return this.modified;
25413 createFilterFn : function(property, value, anyMatch){
25414 if(!value.exec){ // not a regex
25415 value = String(value);
25416 if(value.length == 0){
25419 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25421 return function(r){
25422 return value.test(r.data[property]);
25427 * Sums the value of <i>property</i> for each record between start and end and returns the result.
25428 * @param {String} property A field on your records
25429 * @param {Number} start The record index to start at (defaults to 0)
25430 * @param {Number} end The last record index to include (defaults to length - 1)
25431 * @return {Number} The sum
25433 sum : function(property, start, end){
25434 var rs = this.data.items, v = 0;
25435 start = start || 0;
25436 end = (end || end === 0) ? end : rs.length-1;
25438 for(var i = start; i <= end; i++){
25439 v += (rs[i].data[property] || 0);
25445 * Filter the records by a specified property.
25446 * @param {String} field A field on your records
25447 * @param {String/RegExp} value Either a string that the field
25448 * should start with or a RegExp to test against the field
25449 * @param {Boolean} anyMatch True to match any part not just the beginning
25451 filter : function(property, value, anyMatch){
25452 var fn = this.createFilterFn(property, value, anyMatch);
25453 return fn ? this.filterBy(fn) : this.clearFilter();
25457 * Filter by a function. The specified function will be called with each
25458 * record in this data source. If the function returns true the record is included,
25459 * otherwise it is filtered.
25460 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25461 * @param {Object} scope (optional) The scope of the function (defaults to this)
25463 filterBy : function(fn, scope){
25464 this.snapshot = this.snapshot || this.data;
25465 this.data = this.queryBy(fn, scope||this);
25466 this.fireEvent("datachanged", this);
25470 * Query the records by a specified property.
25471 * @param {String} field A field on your records
25472 * @param {String/RegExp} value Either a string that the field
25473 * should start with or a RegExp to test against the field
25474 * @param {Boolean} anyMatch True to match any part not just the beginning
25475 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25477 query : function(property, value, anyMatch){
25478 var fn = this.createFilterFn(property, value, anyMatch);
25479 return fn ? this.queryBy(fn) : this.data.clone();
25483 * Query by a function. The specified function will be called with each
25484 * record in this data source. If the function returns true the record is included
25486 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25487 * @param {Object} scope (optional) The scope of the function (defaults to this)
25488 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25490 queryBy : function(fn, scope){
25491 var data = this.snapshot || this.data;
25492 return data.filterBy(fn, scope||this);
25496 * Collects unique values for a particular dataIndex from this store.
25497 * @param {String} dataIndex The property to collect
25498 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25499 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25500 * @return {Array} An array of the unique values
25502 collect : function(dataIndex, allowNull, bypassFilter){
25503 var d = (bypassFilter === true && this.snapshot) ?
25504 this.snapshot.items : this.data.items;
25505 var v, sv, r = [], l = {};
25506 for(var i = 0, len = d.length; i < len; i++){
25507 v = d[i].data[dataIndex];
25509 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25518 * Revert to a view of the Record cache with no filtering applied.
25519 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25521 clearFilter : function(suppressEvent){
25522 if(this.snapshot && this.snapshot != this.data){
25523 this.data = this.snapshot;
25524 delete this.snapshot;
25525 if(suppressEvent !== true){
25526 this.fireEvent("datachanged", this);
25532 afterEdit : function(record){
25533 if(this.modified.indexOf(record) == -1){
25534 this.modified.push(record);
25536 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25540 afterReject : function(record){
25541 this.modified.remove(record);
25542 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25546 afterCommit : function(record){
25547 this.modified.remove(record);
25548 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25552 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25553 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25555 commitChanges : function(){
25556 var m = this.modified.slice(0);
25557 this.modified = [];
25558 for(var i = 0, len = m.length; i < len; i++){
25564 * Cancel outstanding changes on all changed records.
25566 rejectChanges : function(){
25567 var m = this.modified.slice(0);
25568 this.modified = [];
25569 for(var i = 0, len = m.length; i < len; i++){
25574 onMetaChange : function(meta, rtype, o){
25575 this.recordType = rtype;
25576 this.fields = rtype.prototype.fields;
25577 delete this.snapshot;
25578 this.sortInfo = meta.sortInfo || this.sortInfo;
25579 this.modified = [];
25580 this.fireEvent('metachange', this, this.reader.meta);
25583 moveIndex : function(data, type)
25585 var index = this.indexOf(data);
25587 var newIndex = index + type;
25591 this.insert(newIndex, data);
25596 * Ext JS Library 1.1.1
25597 * Copyright(c) 2006-2007, Ext JS, LLC.
25599 * Originally Released Under LGPL - original licence link has changed is not relivant.
25602 * <script type="text/javascript">
25606 * @class Roo.data.SimpleStore
25607 * @extends Roo.data.Store
25608 * Small helper class to make creating Stores from Array data easier.
25609 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25610 * @cfg {Array} fields An array of field definition objects, or field name strings.
25611 * @cfg {Object} an existing reader (eg. copied from another store)
25612 * @cfg {Array} data The multi-dimensional array of data
25613 * @cfg {Roo.data.DataProxy} proxy [not-required]
25614 * @cfg {Roo.data.Reader} reader [not-required]
25616 * @param {Object} config
25618 Roo.data.SimpleStore = function(config)
25620 Roo.data.SimpleStore.superclass.constructor.call(this, {
25622 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25625 Roo.data.Record.create(config.fields)
25627 proxy : new Roo.data.MemoryProxy(config.data)
25631 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25633 * Ext JS Library 1.1.1
25634 * Copyright(c) 2006-2007, Ext JS, LLC.
25636 * Originally Released Under LGPL - original licence link has changed is not relivant.
25639 * <script type="text/javascript">
25644 * @extends Roo.data.Store
25645 * @class Roo.data.JsonStore
25646 * Small helper class to make creating Stores for JSON data easier. <br/>
25648 var store = new Roo.data.JsonStore({
25649 url: 'get-images.php',
25651 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25654 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25655 * JsonReader and HttpProxy (unless inline data is provided).</b>
25656 * @cfg {Array} fields An array of field definition objects, or field name strings.
25658 * @param {Object} config
25660 Roo.data.JsonStore = function(c){
25661 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25662 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25663 reader: new Roo.data.JsonReader(c, c.fields)
25666 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25668 * Ext JS Library 1.1.1
25669 * Copyright(c) 2006-2007, Ext JS, LLC.
25671 * Originally Released Under LGPL - original licence link has changed is not relivant.
25674 * <script type="text/javascript">
25678 Roo.data.Field = function(config){
25679 if(typeof config == "string"){
25680 config = {name: config};
25682 Roo.apply(this, config);
25685 this.type = "auto";
25688 var st = Roo.data.SortTypes;
25689 // named sortTypes are supported, here we look them up
25690 if(typeof this.sortType == "string"){
25691 this.sortType = st[this.sortType];
25694 // set default sortType for strings and dates
25695 if(!this.sortType){
25698 this.sortType = st.asUCString;
25701 this.sortType = st.asDate;
25704 this.sortType = st.none;
25709 var stripRe = /[\$,%]/g;
25711 // prebuilt conversion function for this field, instead of
25712 // switching every time we're reading a value
25714 var cv, dateFormat = this.dateFormat;
25719 cv = function(v){ return v; };
25722 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25726 return v !== undefined && v !== null && v !== '' ?
25727 parseInt(String(v).replace(stripRe, ""), 10) : '';
25732 return v !== undefined && v !== null && v !== '' ?
25733 parseFloat(String(v).replace(stripRe, ""), 10) : '';
25738 cv = function(v){ return v === true || v === "true" || v == 1; };
25745 if(v instanceof Date){
25749 if(dateFormat == "timestamp"){
25750 return new Date(v*1000);
25752 return Date.parseDate(v, dateFormat);
25754 var parsed = Date.parse(v);
25755 return parsed ? new Date(parsed) : null;
25764 Roo.data.Field.prototype = {
25772 * Ext JS Library 1.1.1
25773 * Copyright(c) 2006-2007, Ext JS, LLC.
25775 * Originally Released Under LGPL - original licence link has changed is not relivant.
25778 * <script type="text/javascript">
25781 // Base class for reading structured data from a data source. This class is intended to be
25782 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25785 * @class Roo.data.DataReader
25787 * Base class for reading structured data from a data source. This class is intended to be
25788 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25791 Roo.data.DataReader = function(meta, recordType){
25795 this.recordType = recordType instanceof Array ?
25796 Roo.data.Record.create(recordType) : recordType;
25799 Roo.data.DataReader.prototype = {
25802 readerType : 'Data',
25804 * Create an empty record
25805 * @param {Object} data (optional) - overlay some values
25806 * @return {Roo.data.Record} record created.
25808 newRow : function(d) {
25810 this.recordType.prototype.fields.each(function(c) {
25812 case 'int' : da[c.name] = 0; break;
25813 case 'date' : da[c.name] = new Date(); break;
25814 case 'float' : da[c.name] = 0.0; break;
25815 case 'boolean' : da[c.name] = false; break;
25816 default : da[c.name] = ""; break;
25820 return new this.recordType(Roo.apply(da, d));
25826 * Ext JS Library 1.1.1
25827 * Copyright(c) 2006-2007, Ext JS, LLC.
25829 * Originally Released Under LGPL - original licence link has changed is not relivant.
25832 * <script type="text/javascript">
25836 * @class Roo.data.DataProxy
25837 * @extends Roo.util.Observable
25839 * This class is an abstract base class for implementations which provide retrieval of
25840 * unformatted data objects.<br>
25842 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25843 * (of the appropriate type which knows how to parse the data object) to provide a block of
25844 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25846 * Custom implementations must implement the load method as described in
25847 * {@link Roo.data.HttpProxy#load}.
25849 Roo.data.DataProxy = function(){
25852 * @event beforeload
25853 * Fires before a network request is made to retrieve a data object.
25854 * @param {Object} This DataProxy object.
25855 * @param {Object} params The params parameter to the load function.
25860 * Fires before the load method's callback is called.
25861 * @param {Object} This DataProxy object.
25862 * @param {Object} o The data object.
25863 * @param {Object} arg The callback argument object passed to the load function.
25867 * @event loadexception
25868 * Fires if an Exception occurs during data retrieval.
25869 * @param {Object} This DataProxy object.
25870 * @param {Object} o The data object.
25871 * @param {Object} arg The callback argument object passed to the load function.
25872 * @param {Object} e The Exception.
25874 loadexception : true
25876 Roo.data.DataProxy.superclass.constructor.call(this);
25879 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25882 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25886 * Ext JS Library 1.1.1
25887 * Copyright(c) 2006-2007, Ext JS, LLC.
25889 * Originally Released Under LGPL - original licence link has changed is not relivant.
25892 * <script type="text/javascript">
25895 * @class Roo.data.MemoryProxy
25896 * @extends Roo.data.DataProxy
25897 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25898 * to the Reader when its load method is called.
25900 * @param {Object} config A config object containing the objects needed for the Store to access data,
25902 Roo.data.MemoryProxy = function(config){
25904 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25905 data = config.data;
25907 Roo.data.MemoryProxy.superclass.constructor.call(this);
25911 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25914 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25917 * Load data from the requested source (in this case an in-memory
25918 * data object passed to the constructor), read the data object into
25919 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25920 * process that block using the passed callback.
25921 * @param {Object} params This parameter is not used by the MemoryProxy class.
25922 * @param {Roo.data.DataReader} reader The Reader object which converts the data
25923 * object into a block of Roo.data.Records.
25924 * @param {Function} callback The function into which to pass the block of Roo.data.records.
25925 * The function must be passed <ul>
25926 * <li>The Record block object</li>
25927 * <li>The "arg" argument from the load function</li>
25928 * <li>A boolean success indicator</li>
25930 * @param {Object} scope The scope in which to call the callback
25931 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25933 load : function(params, reader, callback, scope, arg){
25934 params = params || {};
25937 result = reader.readRecords(params.data ? params.data :this.data);
25939 this.fireEvent("loadexception", this, arg, null, e);
25940 callback.call(scope, null, arg, false);
25943 callback.call(scope, result, arg, true);
25947 update : function(params, records){
25952 * Ext JS Library 1.1.1
25953 * Copyright(c) 2006-2007, Ext JS, LLC.
25955 * Originally Released Under LGPL - original licence link has changed is not relivant.
25958 * <script type="text/javascript">
25961 * @class Roo.data.HttpProxy
25962 * @extends Roo.data.DataProxy
25963 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25964 * configured to reference a certain URL.<br><br>
25966 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25967 * from which the running page was served.<br><br>
25969 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25971 * Be aware that to enable the browser to parse an XML document, the server must set
25972 * the Content-Type header in the HTTP response to "text/xml".
25974 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25975 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
25976 * will be used to make the request.
25978 Roo.data.HttpProxy = function(conn){
25979 Roo.data.HttpProxy.superclass.constructor.call(this);
25980 // is conn a conn config or a real conn?
25982 this.useAjax = !conn || !conn.events;
25986 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25987 // thse are take from connection...
25990 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25993 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25994 * extra parameters to each request made by this object. (defaults to undefined)
25997 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25998 * to each request made by this object. (defaults to undefined)
26001 * @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)
26004 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
26007 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
26013 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26017 * Return the {@link Roo.data.Connection} object being used by this Proxy.
26018 * @return {Connection} The Connection object. This object may be used to subscribe to events on
26019 * a finer-grained basis than the DataProxy events.
26021 getConnection : function(){
26022 return this.useAjax ? Roo.Ajax : this.conn;
26026 * Load data from the configured {@link Roo.data.Connection}, read the data object into
26027 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26028 * process that block using the passed callback.
26029 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26030 * for the request to the remote server.
26031 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26032 * object into a block of Roo.data.Records.
26033 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26034 * The function must be passed <ul>
26035 * <li>The Record block object</li>
26036 * <li>The "arg" argument from the load function</li>
26037 * <li>A boolean success indicator</li>
26039 * @param {Object} scope The scope in which to call the callback
26040 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26042 load : function(params, reader, callback, scope, arg){
26043 if(this.fireEvent("beforeload", this, params) !== false){
26045 params : params || {},
26047 callback : callback,
26052 callback : this.loadResponse,
26056 Roo.applyIf(o, this.conn);
26057 if(this.activeRequest){
26058 Roo.Ajax.abort(this.activeRequest);
26060 this.activeRequest = Roo.Ajax.request(o);
26062 this.conn.request(o);
26065 callback.call(scope||this, null, arg, false);
26070 loadResponse : function(o, success, response){
26071 delete this.activeRequest;
26073 this.fireEvent("loadexception", this, o, response);
26074 o.request.callback.call(o.request.scope, null, o.request.arg, false);
26079 result = o.reader.read(response);
26082 o.raw = { errorMsg : response.responseText };
26083 this.fireEvent("loadexception", this, o, response, e);
26084 o.request.callback.call(o.request.scope, o, o.request.arg, false);
26088 this.fireEvent("load", this, o, o.request.arg);
26089 o.request.callback.call(o.request.scope, result, o.request.arg, true);
26093 update : function(dataSet){
26098 updateResponse : function(dataSet){
26103 * Ext JS Library 1.1.1
26104 * Copyright(c) 2006-2007, Ext JS, LLC.
26106 * Originally Released Under LGPL - original licence link has changed is not relivant.
26109 * <script type="text/javascript">
26113 * @class Roo.data.ScriptTagProxy
26114 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26115 * other than the originating domain of the running page.<br><br>
26117 * <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
26118 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26120 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26121 * source code that is used as the source inside a <script> tag.<br><br>
26123 * In order for the browser to process the returned data, the server must wrap the data object
26124 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26125 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26126 * depending on whether the callback name was passed:
26129 boolean scriptTag = false;
26130 String cb = request.getParameter("callback");
26133 response.setContentType("text/javascript");
26135 response.setContentType("application/x-json");
26137 Writer out = response.getWriter();
26139 out.write(cb + "(");
26141 out.print(dataBlock.toJsonString());
26148 * @param {Object} config A configuration object.
26150 Roo.data.ScriptTagProxy = function(config){
26151 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26152 Roo.apply(this, config);
26153 this.head = document.getElementsByTagName("head")[0];
26156 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26158 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26160 * @cfg {String} url The URL from which to request the data object.
26163 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26167 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26168 * the server the name of the callback function set up by the load call to process the returned data object.
26169 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26170 * javascript output which calls this named function passing the data object as its only parameter.
26172 callbackParam : "callback",
26174 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26175 * name to the request.
26180 * Load data from the configured URL, read the data object into
26181 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26182 * process that block using the passed callback.
26183 * @param {Object} params An object containing properties which are to be used as HTTP parameters
26184 * for the request to the remote server.
26185 * @param {Roo.data.DataReader} reader The Reader object which converts the data
26186 * object into a block of Roo.data.Records.
26187 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26188 * The function must be passed <ul>
26189 * <li>The Record block object</li>
26190 * <li>The "arg" argument from the load function</li>
26191 * <li>A boolean success indicator</li>
26193 * @param {Object} scope The scope in which to call the callback
26194 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26196 load : function(params, reader, callback, scope, arg){
26197 if(this.fireEvent("beforeload", this, params) !== false){
26199 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26201 var url = this.url;
26202 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26204 url += "&_dc=" + (new Date().getTime());
26206 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26209 cb : "stcCallback"+transId,
26210 scriptId : "stcScript"+transId,
26214 callback : callback,
26220 window[trans.cb] = function(o){
26221 conn.handleResponse(o, trans);
26224 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26226 if(this.autoAbort !== false){
26230 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26232 var script = document.createElement("script");
26233 script.setAttribute("src", url);
26234 script.setAttribute("type", "text/javascript");
26235 script.setAttribute("id", trans.scriptId);
26236 this.head.appendChild(script);
26238 this.trans = trans;
26240 callback.call(scope||this, null, arg, false);
26245 isLoading : function(){
26246 return this.trans ? true : false;
26250 * Abort the current server request.
26252 abort : function(){
26253 if(this.isLoading()){
26254 this.destroyTrans(this.trans);
26259 destroyTrans : function(trans, isLoaded){
26260 this.head.removeChild(document.getElementById(trans.scriptId));
26261 clearTimeout(trans.timeoutId);
26263 window[trans.cb] = undefined;
26265 delete window[trans.cb];
26268 // if hasn't been loaded, wait for load to remove it to prevent script error
26269 window[trans.cb] = function(){
26270 window[trans.cb] = undefined;
26272 delete window[trans.cb];
26279 handleResponse : function(o, trans){
26280 this.trans = false;
26281 this.destroyTrans(trans, true);
26284 result = trans.reader.readRecords(o);
26286 this.fireEvent("loadexception", this, o, trans.arg, e);
26287 trans.callback.call(trans.scope||window, null, trans.arg, false);
26290 this.fireEvent("load", this, o, trans.arg);
26291 trans.callback.call(trans.scope||window, result, trans.arg, true);
26295 handleFailure : function(trans){
26296 this.trans = false;
26297 this.destroyTrans(trans, false);
26298 this.fireEvent("loadexception", this, null, trans.arg);
26299 trans.callback.call(trans.scope||window, null, trans.arg, false);
26303 * Ext JS Library 1.1.1
26304 * Copyright(c) 2006-2007, Ext JS, LLC.
26306 * Originally Released Under LGPL - original licence link has changed is not relivant.
26309 * <script type="text/javascript">
26313 * @class Roo.data.JsonReader
26314 * @extends Roo.data.DataReader
26315 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26316 * based on mappings in a provided Roo.data.Record constructor.
26318 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26319 * in the reply previously.
26324 var RecordDef = Roo.data.Record.create([
26325 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26326 {name: 'occupation'} // This field will use "occupation" as the mapping.
26328 var myReader = new Roo.data.JsonReader({
26329 totalProperty: "results", // The property which contains the total dataset size (optional)
26330 root: "rows", // The property which contains an Array of row objects
26331 id: "id" // The property within each row object that provides an ID for the record (optional)
26335 * This would consume a JSON file like this:
26337 { 'results': 2, 'rows': [
26338 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26339 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26342 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26343 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26344 * paged from the remote server.
26345 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26346 * @cfg {String} root name of the property which contains the Array of row objects.
26347 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26348 * @cfg {Array} fields Array of field definition objects
26350 * Create a new JsonReader
26351 * @param {Object} meta Metadata configuration options
26352 * @param {Object} recordType Either an Array of field definition objects,
26353 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26355 Roo.data.JsonReader = function(meta, recordType){
26358 // set some defaults:
26359 Roo.applyIf(meta, {
26360 totalProperty: 'total',
26361 successProperty : 'success',
26366 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26368 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26370 readerType : 'Json',
26373 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
26374 * Used by Store query builder to append _requestMeta to params.
26377 metaFromRemote : false,
26379 * This method is only used by a DataProxy which has retrieved data from a remote server.
26380 * @param {Object} response The XHR object which contains the JSON data in its responseText.
26381 * @return {Object} data A data block which is used by an Roo.data.Store object as
26382 * a cache of Roo.data.Records.
26384 read : function(response){
26385 var json = response.responseText;
26387 var o = /* eval:var:o */ eval("("+json+")");
26389 throw {message: "JsonReader.read: Json object not found"};
26395 this.metaFromRemote = true;
26396 this.meta = o.metaData;
26397 this.recordType = Roo.data.Record.create(o.metaData.fields);
26398 this.onMetaChange(this.meta, this.recordType, o);
26400 return this.readRecords(o);
26403 // private function a store will implement
26404 onMetaChange : function(meta, recordType, o){
26411 simpleAccess: function(obj, subsc) {
26418 getJsonAccessor: function(){
26420 return function(expr) {
26422 return(re.test(expr))
26423 ? new Function("obj", "return obj." + expr)
26428 return Roo.emptyFn;
26433 * Create a data block containing Roo.data.Records from an XML document.
26434 * @param {Object} o An object which contains an Array of row objects in the property specified
26435 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26436 * which contains the total size of the dataset.
26437 * @return {Object} data A data block which is used by an Roo.data.Store object as
26438 * a cache of Roo.data.Records.
26440 readRecords : function(o){
26442 * After any data loads, the raw JSON data is available for further custom processing.
26446 var s = this.meta, Record = this.recordType,
26447 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26449 // Generate extraction functions for the totalProperty, the root, the id, and for each field
26451 if(s.totalProperty) {
26452 this.getTotal = this.getJsonAccessor(s.totalProperty);
26454 if(s.successProperty) {
26455 this.getSuccess = this.getJsonAccessor(s.successProperty);
26457 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26459 var g = this.getJsonAccessor(s.id);
26460 this.getId = function(rec) {
26462 return (r === undefined || r === "") ? null : r;
26465 this.getId = function(){return null;};
26468 for(var jj = 0; jj < fl; jj++){
26470 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26471 this.ef[jj] = this.getJsonAccessor(map);
26475 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26476 if(s.totalProperty){
26477 var vt = parseInt(this.getTotal(o), 10);
26482 if(s.successProperty){
26483 var vs = this.getSuccess(o);
26484 if(vs === false || vs === 'false'){
26489 for(var i = 0; i < c; i++){
26492 var id = this.getId(n);
26493 for(var j = 0; j < fl; j++){
26495 var v = this.ef[j](n);
26497 Roo.log('missing convert for ' + f.name);
26501 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26505 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26511 var record = new Record(values, id);
26513 records[i] = record;
26519 totalRecords : totalRecords
26522 // used when loading children.. @see loadDataFromChildren
26523 toLoadData: function(rec)
26525 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26526 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26527 return { data : data, total : data.length };
26532 * Ext JS Library 1.1.1
26533 * Copyright(c) 2006-2007, Ext JS, LLC.
26535 * Originally Released Under LGPL - original licence link has changed is not relivant.
26538 * <script type="text/javascript">
26542 * @class Roo.data.XmlReader
26543 * @extends Roo.data.DataReader
26544 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26545 * based on mappings in a provided Roo.data.Record constructor.<br><br>
26547 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26548 * header in the HTTP response must be set to "text/xml".</em>
26552 var RecordDef = Roo.data.Record.create([
26553 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
26554 {name: 'occupation'} // This field will use "occupation" as the mapping.
26556 var myReader = new Roo.data.XmlReader({
26557 totalRecords: "results", // The element which contains the total dataset size (optional)
26558 record: "row", // The repeated element which contains row information
26559 id: "id" // The element within the row that provides an ID for the record (optional)
26563 * This would consume an XML file like this:
26567 <results>2</results>
26570 <name>Bill</name>
26571 <occupation>Gardener</occupation>
26575 <name>Ben</name>
26576 <occupation>Horticulturalist</occupation>
26580 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26581 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26582 * paged from the remote server.
26583 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26584 * @cfg {String} success The DomQuery path to the success attribute used by forms.
26585 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26586 * a record identifier value.
26588 * Create a new XmlReader
26589 * @param {Object} meta Metadata configuration options
26590 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
26591 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26592 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
26594 Roo.data.XmlReader = function(meta, recordType){
26596 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26598 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26600 readerType : 'Xml',
26603 * This method is only used by a DataProxy which has retrieved data from a remote server.
26604 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
26605 * to contain a method called 'responseXML' that returns an XML document object.
26606 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26607 * a cache of Roo.data.Records.
26609 read : function(response){
26610 var doc = response.responseXML;
26612 throw {message: "XmlReader.read: XML Document not available"};
26614 return this.readRecords(doc);
26618 * Create a data block containing Roo.data.Records from an XML document.
26619 * @param {Object} doc A parsed XML document.
26620 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26621 * a cache of Roo.data.Records.
26623 readRecords : function(doc){
26625 * After any data loads/reads, the raw XML Document is available for further custom processing.
26626 * @type XMLDocument
26628 this.xmlData = doc;
26629 var root = doc.documentElement || doc;
26630 var q = Roo.DomQuery;
26631 var recordType = this.recordType, fields = recordType.prototype.fields;
26632 var sid = this.meta.id;
26633 var totalRecords = 0, success = true;
26634 if(this.meta.totalRecords){
26635 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26638 if(this.meta.success){
26639 var sv = q.selectValue(this.meta.success, root, true);
26640 success = sv !== false && sv !== 'false';
26643 var ns = q.select(this.meta.record, root);
26644 for(var i = 0, len = ns.length; i < len; i++) {
26647 var id = sid ? q.selectValue(sid, n) : undefined;
26648 for(var j = 0, jlen = fields.length; j < jlen; j++){
26649 var f = fields.items[j];
26650 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26652 values[f.name] = v;
26654 var record = new recordType(values, id);
26656 records[records.length] = record;
26662 totalRecords : totalRecords || records.length
26667 * Ext JS Library 1.1.1
26668 * Copyright(c) 2006-2007, Ext JS, LLC.
26670 * Originally Released Under LGPL - original licence link has changed is not relivant.
26673 * <script type="text/javascript">
26677 * @class Roo.data.ArrayReader
26678 * @extends Roo.data.DataReader
26679 * Data reader class to create an Array of Roo.data.Record objects from an Array.
26680 * Each element of that Array represents a row of data fields. The
26681 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26682 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26686 var RecordDef = Roo.data.Record.create([
26687 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
26688 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
26690 var myReader = new Roo.data.ArrayReader({
26691 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
26695 * This would consume an Array like this:
26697 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26701 * Create a new JsonReader
26702 * @param {Object} meta Metadata configuration options.
26703 * @param {Object|Array} recordType Either an Array of field definition objects
26705 * @cfg {Array} fields Array of field definition objects
26706 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26707 * as specified to {@link Roo.data.Record#create},
26708 * or an {@link Roo.data.Record} object
26711 * created using {@link Roo.data.Record#create}.
26713 Roo.data.ArrayReader = function(meta, recordType)
26715 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26718 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26721 * Create a data block containing Roo.data.Records from an XML document.
26722 * @param {Object} o An Array of row objects which represents the dataset.
26723 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26724 * a cache of Roo.data.Records.
26726 readRecords : function(o)
26728 var sid = this.meta ? this.meta.id : null;
26729 var recordType = this.recordType, fields = recordType.prototype.fields;
26732 for(var i = 0; i < root.length; i++){
26735 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26736 for(var j = 0, jlen = fields.length; j < jlen; j++){
26737 var f = fields.items[j];
26738 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26739 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26741 values[f.name] = v;
26743 var record = new recordType(values, id);
26745 records[records.length] = record;
26749 totalRecords : records.length
26752 // used when loading children.. @see loadDataFromChildren
26753 toLoadData: function(rec)
26755 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26756 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26763 * Ext JS Library 1.1.1
26764 * Copyright(c) 2006-2007, Ext JS, LLC.
26766 * Originally Released Under LGPL - original licence link has changed is not relivant.
26769 * <script type="text/javascript">
26774 * @class Roo.data.Tree
26775 * @extends Roo.util.Observable
26776 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26777 * in the tree have most standard DOM functionality.
26779 * @param {Node} root (optional) The root node
26781 Roo.data.Tree = function(root){
26782 this.nodeHash = {};
26784 * The root node for this tree
26789 this.setRootNode(root);
26794 * Fires when a new child node is appended to a node in this tree.
26795 * @param {Tree} tree The owner tree
26796 * @param {Node} parent The parent node
26797 * @param {Node} node The newly appended node
26798 * @param {Number} index The index of the newly appended node
26803 * Fires when a child node is removed from a node in this tree.
26804 * @param {Tree} tree The owner tree
26805 * @param {Node} parent The parent node
26806 * @param {Node} node The child node removed
26811 * Fires when a node is moved to a new location in the tree
26812 * @param {Tree} tree The owner tree
26813 * @param {Node} node The node moved
26814 * @param {Node} oldParent The old parent of this node
26815 * @param {Node} newParent The new parent of this node
26816 * @param {Number} index The index it was moved to
26821 * Fires when a new child node is inserted in a node in this tree.
26822 * @param {Tree} tree The owner tree
26823 * @param {Node} parent The parent node
26824 * @param {Node} node The child node inserted
26825 * @param {Node} refNode The child node the node was inserted before
26829 * @event beforeappend
26830 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26831 * @param {Tree} tree The owner tree
26832 * @param {Node} parent The parent node
26833 * @param {Node} node The child node to be appended
26835 "beforeappend" : true,
26837 * @event beforeremove
26838 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26839 * @param {Tree} tree The owner tree
26840 * @param {Node} parent The parent node
26841 * @param {Node} node The child node to be removed
26843 "beforeremove" : true,
26845 * @event beforemove
26846 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26847 * @param {Tree} tree The owner tree
26848 * @param {Node} node The node being moved
26849 * @param {Node} oldParent The parent of the node
26850 * @param {Node} newParent The new parent the node is moving to
26851 * @param {Number} index The index it is being moved to
26853 "beforemove" : true,
26855 * @event beforeinsert
26856 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26857 * @param {Tree} tree The owner tree
26858 * @param {Node} parent The parent node
26859 * @param {Node} node The child node to be inserted
26860 * @param {Node} refNode The child node the node is being inserted before
26862 "beforeinsert" : true
26865 Roo.data.Tree.superclass.constructor.call(this);
26868 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26869 pathSeparator: "/",
26871 proxyNodeEvent : function(){
26872 return this.fireEvent.apply(this, arguments);
26876 * Returns the root node for this tree.
26879 getRootNode : function(){
26884 * Sets the root node for this tree.
26885 * @param {Node} node
26888 setRootNode : function(node){
26890 node.ownerTree = this;
26891 node.isRoot = true;
26892 this.registerNode(node);
26897 * Gets a node in this tree by its id.
26898 * @param {String} id
26901 getNodeById : function(id){
26902 return this.nodeHash[id];
26905 registerNode : function(node){
26906 this.nodeHash[node.id] = node;
26909 unregisterNode : function(node){
26910 delete this.nodeHash[node.id];
26913 toString : function(){
26914 return "[Tree"+(this.id?" "+this.id:"")+"]";
26919 * @class Roo.data.Node
26920 * @extends Roo.util.Observable
26921 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26922 * @cfg {String} id The id for this node. If one is not specified, one is generated.
26924 * @param {Object} attributes The attributes/config for the node
26926 Roo.data.Node = function(attributes){
26928 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26931 this.attributes = attributes || {};
26932 this.leaf = this.attributes.leaf;
26934 * The node id. @type String
26936 this.id = this.attributes.id;
26938 this.id = Roo.id(null, "ynode-");
26939 this.attributes.id = this.id;
26944 * All child nodes of this node. @type Array
26946 this.childNodes = [];
26947 if(!this.childNodes.indexOf){ // indexOf is a must
26948 this.childNodes.indexOf = function(o){
26949 for(var i = 0, len = this.length; i < len; i++){
26958 * The parent node for this node. @type Node
26960 this.parentNode = null;
26962 * The first direct child node of this node, or null if this node has no child nodes. @type Node
26964 this.firstChild = null;
26966 * The last direct child node of this node, or null if this node has no child nodes. @type Node
26968 this.lastChild = null;
26970 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26972 this.previousSibling = null;
26974 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26976 this.nextSibling = null;
26981 * Fires when a new child node is appended
26982 * @param {Tree} tree The owner tree
26983 * @param {Node} this This node
26984 * @param {Node} node The newly appended node
26985 * @param {Number} index The index of the newly appended node
26990 * Fires when a child node is removed
26991 * @param {Tree} tree The owner tree
26992 * @param {Node} this This node
26993 * @param {Node} node The removed node
26998 * Fires when this node is moved to a new location in the tree
26999 * @param {Tree} tree The owner tree
27000 * @param {Node} this This node
27001 * @param {Node} oldParent The old parent of this node
27002 * @param {Node} newParent The new parent of this node
27003 * @param {Number} index The index it was moved to
27008 * Fires when a new child node is inserted.
27009 * @param {Tree} tree The owner tree
27010 * @param {Node} this This node
27011 * @param {Node} node The child node inserted
27012 * @param {Node} refNode The child node the node was inserted before
27016 * @event beforeappend
27017 * Fires before a new child is appended, return false to cancel the append.
27018 * @param {Tree} tree The owner tree
27019 * @param {Node} this This node
27020 * @param {Node} node The child node to be appended
27022 "beforeappend" : true,
27024 * @event beforeremove
27025 * Fires before a child is removed, return false to cancel the remove.
27026 * @param {Tree} tree The owner tree
27027 * @param {Node} this This node
27028 * @param {Node} node The child node to be removed
27030 "beforeremove" : true,
27032 * @event beforemove
27033 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27034 * @param {Tree} tree The owner tree
27035 * @param {Node} this This node
27036 * @param {Node} oldParent The parent of this node
27037 * @param {Node} newParent The new parent this node is moving to
27038 * @param {Number} index The index it is being moved to
27040 "beforemove" : true,
27042 * @event beforeinsert
27043 * Fires before a new child is inserted, return false to cancel the insert.
27044 * @param {Tree} tree The owner tree
27045 * @param {Node} this This node
27046 * @param {Node} node The child node to be inserted
27047 * @param {Node} refNode The child node the node is being inserted before
27049 "beforeinsert" : true
27051 this.listeners = this.attributes.listeners;
27052 Roo.data.Node.superclass.constructor.call(this);
27055 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27056 fireEvent : function(evtName){
27057 // first do standard event for this node
27058 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27061 // then bubble it up to the tree if the event wasn't cancelled
27062 var ot = this.getOwnerTree();
27064 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27072 * Returns true if this node is a leaf
27073 * @return {Boolean}
27075 isLeaf : function(){
27076 return this.leaf === true;
27080 setFirstChild : function(node){
27081 this.firstChild = node;
27085 setLastChild : function(node){
27086 this.lastChild = node;
27091 * Returns true if this node is the last child of its parent
27092 * @return {Boolean}
27094 isLast : function(){
27095 return (!this.parentNode ? true : this.parentNode.lastChild == this);
27099 * Returns true if this node is the first child of its parent
27100 * @return {Boolean}
27102 isFirst : function(){
27103 return (!this.parentNode ? true : this.parentNode.firstChild == this);
27106 hasChildNodes : function(){
27107 return !this.isLeaf() && this.childNodes.length > 0;
27111 * Insert node(s) as the last child node of this node.
27112 * @param {Node/Array} node The node or Array of nodes to append
27113 * @return {Node} The appended node if single append, or null if an array was passed
27115 appendChild : function(node){
27117 if(node instanceof Array){
27119 }else if(arguments.length > 1){
27123 // if passed an array or multiple args do them one by one
27125 for(var i = 0, len = multi.length; i < len; i++) {
27126 this.appendChild(multi[i]);
27129 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27132 var index = this.childNodes.length;
27133 var oldParent = node.parentNode;
27134 // it's a move, make sure we move it cleanly
27136 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27139 oldParent.removeChild(node);
27142 index = this.childNodes.length;
27144 this.setFirstChild(node);
27146 this.childNodes.push(node);
27147 node.parentNode = this;
27148 var ps = this.childNodes[index-1];
27150 node.previousSibling = ps;
27151 ps.nextSibling = node;
27153 node.previousSibling = null;
27155 node.nextSibling = null;
27156 this.setLastChild(node);
27157 node.setOwnerTree(this.getOwnerTree());
27158 this.fireEvent("append", this.ownerTree, this, node, index);
27159 if(this.ownerTree) {
27160 this.ownerTree.fireEvent("appendnode", this, node, index);
27163 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27170 * Removes a child node from this node.
27171 * @param {Node} node The node to remove
27172 * @return {Node} The removed node
27174 removeChild : function(node){
27175 var index = this.childNodes.indexOf(node);
27179 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27183 // remove it from childNodes collection
27184 this.childNodes.splice(index, 1);
27187 if(node.previousSibling){
27188 node.previousSibling.nextSibling = node.nextSibling;
27190 if(node.nextSibling){
27191 node.nextSibling.previousSibling = node.previousSibling;
27194 // update child refs
27195 if(this.firstChild == node){
27196 this.setFirstChild(node.nextSibling);
27198 if(this.lastChild == node){
27199 this.setLastChild(node.previousSibling);
27202 node.setOwnerTree(null);
27203 // clear any references from the node
27204 node.parentNode = null;
27205 node.previousSibling = null;
27206 node.nextSibling = null;
27207 this.fireEvent("remove", this.ownerTree, this, node);
27212 * Inserts the first node before the second node in this nodes childNodes collection.
27213 * @param {Node} node The node to insert
27214 * @param {Node} refNode The node to insert before (if null the node is appended)
27215 * @return {Node} The inserted node
27217 insertBefore : function(node, refNode){
27218 if(!refNode){ // like standard Dom, refNode can be null for append
27219 return this.appendChild(node);
27222 if(node == refNode){
27226 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27229 var index = this.childNodes.indexOf(refNode);
27230 var oldParent = node.parentNode;
27231 var refIndex = index;
27233 // when moving internally, indexes will change after remove
27234 if(oldParent == this && this.childNodes.indexOf(node) < index){
27238 // it's a move, make sure we move it cleanly
27240 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27243 oldParent.removeChild(node);
27246 this.setFirstChild(node);
27248 this.childNodes.splice(refIndex, 0, node);
27249 node.parentNode = this;
27250 var ps = this.childNodes[refIndex-1];
27252 node.previousSibling = ps;
27253 ps.nextSibling = node;
27255 node.previousSibling = null;
27257 node.nextSibling = refNode;
27258 refNode.previousSibling = node;
27259 node.setOwnerTree(this.getOwnerTree());
27260 this.fireEvent("insert", this.ownerTree, this, node, refNode);
27262 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27268 * Returns the child node at the specified index.
27269 * @param {Number} index
27272 item : function(index){
27273 return this.childNodes[index];
27277 * Replaces one child node in this node with another.
27278 * @param {Node} newChild The replacement node
27279 * @param {Node} oldChild The node to replace
27280 * @return {Node} The replaced node
27282 replaceChild : function(newChild, oldChild){
27283 this.insertBefore(newChild, oldChild);
27284 this.removeChild(oldChild);
27289 * Returns the index of a child node
27290 * @param {Node} node
27291 * @return {Number} The index of the node or -1 if it was not found
27293 indexOf : function(child){
27294 return this.childNodes.indexOf(child);
27298 * Returns the tree this node is in.
27301 getOwnerTree : function(){
27302 // if it doesn't have one, look for one
27303 if(!this.ownerTree){
27307 this.ownerTree = p.ownerTree;
27313 return this.ownerTree;
27317 * Returns depth of this node (the root node has a depth of 0)
27320 getDepth : function(){
27323 while(p.parentNode){
27331 setOwnerTree : function(tree){
27332 // if it's move, we need to update everyone
27333 if(tree != this.ownerTree){
27334 if(this.ownerTree){
27335 this.ownerTree.unregisterNode(this);
27337 this.ownerTree = tree;
27338 var cs = this.childNodes;
27339 for(var i = 0, len = cs.length; i < len; i++) {
27340 cs[i].setOwnerTree(tree);
27343 tree.registerNode(this);
27349 * Returns the path for this node. The path can be used to expand or select this node programmatically.
27350 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27351 * @return {String} The path
27353 getPath : function(attr){
27354 attr = attr || "id";
27355 var p = this.parentNode;
27356 var b = [this.attributes[attr]];
27358 b.unshift(p.attributes[attr]);
27361 var sep = this.getOwnerTree().pathSeparator;
27362 return sep + b.join(sep);
27366 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27367 * function call will be the scope provided or the current node. The arguments to the function
27368 * will be the args provided or the current node. If the function returns false at any point,
27369 * the bubble is stopped.
27370 * @param {Function} fn The function to call
27371 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27372 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27374 bubble : function(fn, scope, args){
27377 if(fn.call(scope || p, args || p) === false){
27385 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27386 * function call will be the scope provided or the current node. The arguments to the function
27387 * will be the args provided or the current node. If the function returns false at any point,
27388 * the cascade is stopped on that branch.
27389 * @param {Function} fn The function to call
27390 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27391 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27393 cascade : function(fn, scope, args){
27394 if(fn.call(scope || this, args || this) !== false){
27395 var cs = this.childNodes;
27396 for(var i = 0, len = cs.length; i < len; i++) {
27397 cs[i].cascade(fn, scope, args);
27403 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27404 * function call will be the scope provided or the current node. The arguments to the function
27405 * will be the args provided or the current node. If the function returns false at any point,
27406 * the iteration stops.
27407 * @param {Function} fn The function to call
27408 * @param {Object} scope (optional) The scope of the function (defaults to current node)
27409 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27411 eachChild : function(fn, scope, args){
27412 var cs = this.childNodes;
27413 for(var i = 0, len = cs.length; i < len; i++) {
27414 if(fn.call(scope || this, args || cs[i]) === false){
27421 * Finds the first child that has the attribute with the specified value.
27422 * @param {String} attribute The attribute name
27423 * @param {Mixed} value The value to search for
27424 * @return {Node} The found child or null if none was found
27426 findChild : function(attribute, value){
27427 var cs = this.childNodes;
27428 for(var i = 0, len = cs.length; i < len; i++) {
27429 if(cs[i].attributes[attribute] == value){
27437 * Finds the first child by a custom function. The child matches if the function passed
27439 * @param {Function} fn
27440 * @param {Object} scope (optional)
27441 * @return {Node} The found child or null if none was found
27443 findChildBy : function(fn, scope){
27444 var cs = this.childNodes;
27445 for(var i = 0, len = cs.length; i < len; i++) {
27446 if(fn.call(scope||cs[i], cs[i]) === true){
27454 * Sorts this nodes children using the supplied sort function
27455 * @param {Function} fn
27456 * @param {Object} scope (optional)
27458 sort : function(fn, scope){
27459 var cs = this.childNodes;
27460 var len = cs.length;
27462 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27464 for(var i = 0; i < len; i++){
27466 n.previousSibling = cs[i-1];
27467 n.nextSibling = cs[i+1];
27469 this.setFirstChild(n);
27472 this.setLastChild(n);
27479 * Returns true if this node is an ancestor (at any point) of the passed node.
27480 * @param {Node} node
27481 * @return {Boolean}
27483 contains : function(node){
27484 return node.isAncestor(this);
27488 * Returns true if the passed node is an ancestor (at any point) of this node.
27489 * @param {Node} node
27490 * @return {Boolean}
27492 isAncestor : function(node){
27493 var p = this.parentNode;
27503 toString : function(){
27504 return "[Node"+(this.id?" "+this.id:"")+"]";
27508 * Ext JS Library 1.1.1
27509 * Copyright(c) 2006-2007, Ext JS, LLC.
27511 * Originally Released Under LGPL - original licence link has changed is not relivant.
27514 * <script type="text/javascript">
27519 * @class Roo.Shadow
27520 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
27521 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
27522 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27524 * Create a new Shadow
27525 * @param {Object} config The config object
27527 Roo.Shadow = function(config){
27528 Roo.apply(this, config);
27529 if(typeof this.mode != "string"){
27530 this.mode = this.defaultMode;
27532 var o = this.offset, a = {h: 0};
27533 var rad = Math.floor(this.offset/2);
27534 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27540 a.l -= this.offset + rad;
27541 a.t -= this.offset + rad;
27552 a.l -= (this.offset - rad);
27553 a.t -= this.offset + rad;
27555 a.w -= (this.offset - rad)*2;
27566 a.l -= (this.offset - rad);
27567 a.t -= (this.offset - rad);
27569 a.w -= (this.offset + rad + 1);
27570 a.h -= (this.offset + rad);
27579 Roo.Shadow.prototype = {
27581 * @cfg {String} mode
27582 * The shadow display mode. Supports the following options:<br />
27583 * sides: Shadow displays on both sides and bottom only<br />
27584 * frame: Shadow displays equally on all four sides<br />
27585 * drop: Traditional bottom-right drop shadow (default)
27589 * @cfg {String} offset
27590 * The number of pixels to offset the shadow from the element (defaults to 4)
27595 defaultMode: "drop",
27598 * Displays the shadow under the target element
27599 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27601 show : function(target){
27602 target = Roo.get(target);
27604 this.el = Roo.Shadow.Pool.pull();
27605 if(this.el.dom.nextSibling != target.dom){
27606 this.el.insertBefore(target);
27609 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27611 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27614 target.getLeft(true),
27615 target.getTop(true),
27619 this.el.dom.style.display = "block";
27623 * Returns true if the shadow is visible, else false
27625 isVisible : function(){
27626 return this.el ? true : false;
27630 * Direct alignment when values are already available. Show must be called at least once before
27631 * calling this method to ensure it is initialized.
27632 * @param {Number} left The target element left position
27633 * @param {Number} top The target element top position
27634 * @param {Number} width The target element width
27635 * @param {Number} height The target element height
27637 realign : function(l, t, w, h){
27641 var a = this.adjusts, d = this.el.dom, s = d.style;
27643 s.left = (l+a.l)+"px";
27644 s.top = (t+a.t)+"px";
27645 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27647 if(s.width != sws || s.height != shs){
27651 var cn = d.childNodes;
27652 var sww = Math.max(0, (sw-12))+"px";
27653 cn[0].childNodes[1].style.width = sww;
27654 cn[1].childNodes[1].style.width = sww;
27655 cn[2].childNodes[1].style.width = sww;
27656 cn[1].style.height = Math.max(0, (sh-12))+"px";
27662 * Hides this shadow
27666 this.el.dom.style.display = "none";
27667 Roo.Shadow.Pool.push(this.el);
27673 * Adjust the z-index of this shadow
27674 * @param {Number} zindex The new z-index
27676 setZIndex : function(z){
27679 this.el.setStyle("z-index", z);
27684 // Private utility class that manages the internal Shadow cache
27685 Roo.Shadow.Pool = function(){
27687 var markup = Roo.isIE ?
27688 '<div class="x-ie-shadow"></div>' :
27689 '<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>';
27692 var sh = p.shift();
27694 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27695 sh.autoBoxAdjust = false;
27700 push : function(sh){
27706 * Ext JS Library 1.1.1
27707 * Copyright(c) 2006-2007, Ext JS, LLC.
27709 * Originally Released Under LGPL - original licence link has changed is not relivant.
27712 * <script type="text/javascript">
27717 * @class Roo.SplitBar
27718 * @extends Roo.util.Observable
27719 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27723 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27724 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27725 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27726 split.minSize = 100;
27727 split.maxSize = 600;
27728 split.animate = true;
27729 split.on('moved', splitterMoved);
27732 * Create a new SplitBar
27733 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
27734 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
27735 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27736 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
27737 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27738 position of the SplitBar).
27740 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27743 this.el = Roo.get(dragElement, true);
27744 this.el.dom.unselectable = "on";
27746 this.resizingEl = Roo.get(resizingElement, true);
27750 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27751 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27754 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27757 * The minimum size of the resizing element. (Defaults to 0)
27763 * The maximum size of the resizing element. (Defaults to 2000)
27766 this.maxSize = 2000;
27769 * Whether to animate the transition to the new size
27772 this.animate = false;
27775 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27778 this.useShim = false;
27783 if(!existingProxy){
27785 this.proxy = Roo.SplitBar.createProxy(this.orientation);
27787 this.proxy = Roo.get(existingProxy).dom;
27790 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27793 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27796 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27799 this.dragSpecs = {};
27802 * @private The adapter to use to positon and resize elements
27804 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27805 this.adapter.init(this);
27807 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27809 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27810 this.el.addClass("x-splitbar-h");
27813 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27814 this.el.addClass("x-splitbar-v");
27820 * Fires when the splitter is moved (alias for {@link #event-moved})
27821 * @param {Roo.SplitBar} this
27822 * @param {Number} newSize the new width or height
27827 * Fires when the splitter is moved
27828 * @param {Roo.SplitBar} this
27829 * @param {Number} newSize the new width or height
27833 * @event beforeresize
27834 * Fires before the splitter is dragged
27835 * @param {Roo.SplitBar} this
27837 "beforeresize" : true,
27839 "beforeapply" : true
27842 Roo.util.Observable.call(this);
27845 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27846 onStartProxyDrag : function(x, y){
27847 this.fireEvent("beforeresize", this);
27849 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
27851 o.enableDisplayMode("block");
27852 // all splitbars share the same overlay
27853 Roo.SplitBar.prototype.overlay = o;
27855 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27856 this.overlay.show();
27857 Roo.get(this.proxy).setDisplayed("block");
27858 var size = this.adapter.getElementSize(this);
27859 this.activeMinSize = this.getMinimumSize();;
27860 this.activeMaxSize = this.getMaximumSize();;
27861 var c1 = size - this.activeMinSize;
27862 var c2 = Math.max(this.activeMaxSize - size, 0);
27863 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27864 this.dd.resetConstraints();
27865 this.dd.setXConstraint(
27866 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
27867 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27869 this.dd.setYConstraint(0, 0);
27871 this.dd.resetConstraints();
27872 this.dd.setXConstraint(0, 0);
27873 this.dd.setYConstraint(
27874 this.placement == Roo.SplitBar.TOP ? c1 : c2,
27875 this.placement == Roo.SplitBar.TOP ? c2 : c1
27878 this.dragSpecs.startSize = size;
27879 this.dragSpecs.startPoint = [x, y];
27880 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27884 * @private Called after the drag operation by the DDProxy
27886 onEndProxyDrag : function(e){
27887 Roo.get(this.proxy).setDisplayed(false);
27888 var endPoint = Roo.lib.Event.getXY(e);
27890 this.overlay.hide();
27893 if(this.orientation == Roo.SplitBar.HORIZONTAL){
27894 newSize = this.dragSpecs.startSize +
27895 (this.placement == Roo.SplitBar.LEFT ?
27896 endPoint[0] - this.dragSpecs.startPoint[0] :
27897 this.dragSpecs.startPoint[0] - endPoint[0]
27900 newSize = this.dragSpecs.startSize +
27901 (this.placement == Roo.SplitBar.TOP ?
27902 endPoint[1] - this.dragSpecs.startPoint[1] :
27903 this.dragSpecs.startPoint[1] - endPoint[1]
27906 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27907 if(newSize != this.dragSpecs.startSize){
27908 if(this.fireEvent('beforeapply', this, newSize) !== false){
27909 this.adapter.setElementSize(this, newSize);
27910 this.fireEvent("moved", this, newSize);
27911 this.fireEvent("resize", this, newSize);
27917 * Get the adapter this SplitBar uses
27918 * @return The adapter object
27920 getAdapter : function(){
27921 return this.adapter;
27925 * Set the adapter this SplitBar uses
27926 * @param {Object} adapter A SplitBar adapter object
27928 setAdapter : function(adapter){
27929 this.adapter = adapter;
27930 this.adapter.init(this);
27934 * Gets the minimum size for the resizing element
27935 * @return {Number} The minimum size
27937 getMinimumSize : function(){
27938 return this.minSize;
27942 * Sets the minimum size for the resizing element
27943 * @param {Number} minSize The minimum size
27945 setMinimumSize : function(minSize){
27946 this.minSize = minSize;
27950 * Gets the maximum size for the resizing element
27951 * @return {Number} The maximum size
27953 getMaximumSize : function(){
27954 return this.maxSize;
27958 * Sets the maximum size for the resizing element
27959 * @param {Number} maxSize The maximum size
27961 setMaximumSize : function(maxSize){
27962 this.maxSize = maxSize;
27966 * Sets the initialize size for the resizing element
27967 * @param {Number} size The initial size
27969 setCurrentSize : function(size){
27970 var oldAnimate = this.animate;
27971 this.animate = false;
27972 this.adapter.setElementSize(this, size);
27973 this.animate = oldAnimate;
27977 * Destroy this splitbar.
27978 * @param {Boolean} removeEl True to remove the element
27980 destroy : function(removeEl){
27982 this.shim.remove();
27985 this.proxy.parentNode.removeChild(this.proxy);
27993 * @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.
27995 Roo.SplitBar.createProxy = function(dir){
27996 var proxy = new Roo.Element(document.createElement("div"));
27997 proxy.unselectable();
27998 var cls = 'x-splitbar-proxy';
27999 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
28000 document.body.appendChild(proxy.dom);
28005 * @class Roo.SplitBar.BasicLayoutAdapter
28006 * Default Adapter. It assumes the splitter and resizing element are not positioned
28007 * elements and only gets/sets the width of the element. Generally used for table based layouts.
28009 Roo.SplitBar.BasicLayoutAdapter = function(){
28012 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28013 // do nothing for now
28014 init : function(s){
28018 * Called before drag operations to get the current size of the resizing element.
28019 * @param {Roo.SplitBar} s The SplitBar using this adapter
28021 getElementSize : function(s){
28022 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28023 return s.resizingEl.getWidth();
28025 return s.resizingEl.getHeight();
28030 * Called after drag operations to set the size of the resizing element.
28031 * @param {Roo.SplitBar} s The SplitBar using this adapter
28032 * @param {Number} newSize The new size to set
28033 * @param {Function} onComplete A function to be invoked when resizing is complete
28035 setElementSize : function(s, newSize, onComplete){
28036 if(s.orientation == Roo.SplitBar.HORIZONTAL){
28038 s.resizingEl.setWidth(newSize);
28040 onComplete(s, newSize);
28043 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28048 s.resizingEl.setHeight(newSize);
28050 onComplete(s, newSize);
28053 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28060 *@class Roo.SplitBar.AbsoluteLayoutAdapter
28061 * @extends Roo.SplitBar.BasicLayoutAdapter
28062 * Adapter that moves the splitter element to align with the resized sizing element.
28063 * Used with an absolute positioned SplitBar.
28064 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28065 * document.body, make sure you assign an id to the body element.
28067 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28068 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28069 this.container = Roo.get(container);
28072 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28073 init : function(s){
28074 this.basic.init(s);
28077 getElementSize : function(s){
28078 return this.basic.getElementSize(s);
28081 setElementSize : function(s, newSize, onComplete){
28082 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28085 moveSplitter : function(s){
28086 var yes = Roo.SplitBar;
28087 switch(s.placement){
28089 s.el.setX(s.resizingEl.getRight());
28092 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28095 s.el.setY(s.resizingEl.getBottom());
28098 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28105 * Orientation constant - Create a vertical SplitBar
28109 Roo.SplitBar.VERTICAL = 1;
28112 * Orientation constant - Create a horizontal SplitBar
28116 Roo.SplitBar.HORIZONTAL = 2;
28119 * Placement constant - The resizing element is to the left of the splitter element
28123 Roo.SplitBar.LEFT = 1;
28126 * Placement constant - The resizing element is to the right of the splitter element
28130 Roo.SplitBar.RIGHT = 2;
28133 * Placement constant - The resizing element is positioned above the splitter element
28137 Roo.SplitBar.TOP = 3;
28140 * Placement constant - The resizing element is positioned under splitter element
28144 Roo.SplitBar.BOTTOM = 4;
28147 * Ext JS Library 1.1.1
28148 * Copyright(c) 2006-2007, Ext JS, LLC.
28150 * Originally Released Under LGPL - original licence link has changed is not relivant.
28153 * <script type="text/javascript">
28158 * @extends Roo.util.Observable
28159 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
28160 * This class also supports single and multi selection modes. <br>
28161 * Create a data model bound view:
28163 var store = new Roo.data.Store(...);
28165 var view = new Roo.View({
28167 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
28169 singleSelect: true,
28170 selectedClass: "ydataview-selected",
28174 // listen for node click?
28175 view.on("click", function(vw, index, node, e){
28176 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28180 dataModel.load("foobar.xml");
28182 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28184 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28185 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28187 * Note: old style constructor is still suported (container, template, config)
28190 * Create a new View
28191 * @param {Object} config The config object
28194 Roo.View = function(config, depreciated_tpl, depreciated_config){
28196 this.parent = false;
28198 if (typeof(depreciated_tpl) == 'undefined') {
28199 // new way.. - universal constructor.
28200 Roo.apply(this, config);
28201 this.el = Roo.get(this.el);
28204 this.el = Roo.get(config);
28205 this.tpl = depreciated_tpl;
28206 Roo.apply(this, depreciated_config);
28208 this.wrapEl = this.el.wrap().wrap();
28209 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28212 if(typeof(this.tpl) == "string"){
28213 this.tpl = new Roo.Template(this.tpl);
28215 // support xtype ctors..
28216 this.tpl = new Roo.factory(this.tpl, Roo);
28220 this.tpl.compile();
28225 * @event beforeclick
28226 * Fires before a click is processed. Returns false to cancel the default action.
28227 * @param {Roo.View} this
28228 * @param {Number} index The index of the target node
28229 * @param {HTMLElement} node The target node
28230 * @param {Roo.EventObject} e The raw event object
28232 "beforeclick" : true,
28235 * Fires when a template node is clicked.
28236 * @param {Roo.View} this
28237 * @param {Number} index The index of the target node
28238 * @param {HTMLElement} node The target node
28239 * @param {Roo.EventObject} e The raw event object
28244 * Fires when a template node is double clicked.
28245 * @param {Roo.View} this
28246 * @param {Number} index The index of the target node
28247 * @param {HTMLElement} node The target node
28248 * @param {Roo.EventObject} e The raw event object
28252 * @event contextmenu
28253 * Fires when a template node is right clicked.
28254 * @param {Roo.View} this
28255 * @param {Number} index The index of the target node
28256 * @param {HTMLElement} node The target node
28257 * @param {Roo.EventObject} e The raw event object
28259 "contextmenu" : true,
28261 * @event selectionchange
28262 * Fires when the selected nodes change.
28263 * @param {Roo.View} this
28264 * @param {Array} selections Array of the selected nodes
28266 "selectionchange" : true,
28269 * @event beforeselect
28270 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28271 * @param {Roo.View} this
28272 * @param {HTMLElement} node The node to be selected
28273 * @param {Array} selections Array of currently selected nodes
28275 "beforeselect" : true,
28277 * @event preparedata
28278 * Fires on every row to render, to allow you to change the data.
28279 * @param {Roo.View} this
28280 * @param {Object} data to be rendered (change this)
28282 "preparedata" : true
28290 "click": this.onClick,
28291 "dblclick": this.onDblClick,
28292 "contextmenu": this.onContextMenu,
28296 this.selections = [];
28298 this.cmp = new Roo.CompositeElementLite([]);
28300 this.store = Roo.factory(this.store, Roo.data);
28301 this.setStore(this.store, true);
28304 if ( this.footer && this.footer.xtype) {
28306 var fctr = this.wrapEl.appendChild(document.createElement("div"));
28308 this.footer.dataSource = this.store;
28309 this.footer.container = fctr;
28310 this.footer = Roo.factory(this.footer, Roo);
28311 fctr.insertFirst(this.el);
28313 // this is a bit insane - as the paging toolbar seems to detach the el..
28314 // dom.parentNode.parentNode.parentNode
28315 // they get detached?
28319 Roo.View.superclass.constructor.call(this);
28324 Roo.extend(Roo.View, Roo.util.Observable, {
28327 * @cfg {Roo.data.Store} store Data store to load data from.
28332 * @cfg {String|Roo.Element} el The container element.
28337 * @cfg {String|Roo.Template} tpl The template used by this View
28341 * @cfg {String} dataName the named area of the template to use as the data area
28342 * Works with domtemplates roo-name="name"
28346 * @cfg {String} selectedClass The css class to add to selected nodes
28348 selectedClass : "x-view-selected",
28350 * @cfg {String} emptyText The empty text to show when nothing is loaded.
28355 * @cfg {String} text to display on mask (default Loading)
28359 * @cfg {Boolean} multiSelect Allow multiple selection
28361 multiSelect : false,
28363 * @cfg {Boolean} singleSelect Allow single selection
28365 singleSelect: false,
28368 * @cfg {Boolean} toggleSelect - selecting
28370 toggleSelect : false,
28373 * @cfg {Boolean} tickable - selecting
28378 * Returns the element this view is bound to.
28379 * @return {Roo.Element}
28381 getEl : function(){
28382 return this.wrapEl;
28388 * Refreshes the view. - called by datachanged on the store. - do not call directly.
28390 refresh : function(){
28391 //Roo.log('refresh');
28394 // if we are using something like 'domtemplate', then
28395 // the what gets used is:
28396 // t.applySubtemplate(NAME, data, wrapping data..)
28397 // the outer template then get' applied with
28398 // the store 'extra data'
28399 // and the body get's added to the
28400 // roo-name="data" node?
28401 // <span class='roo-tpl-{name}'></span> ?????
28405 this.clearSelections();
28406 this.el.update("");
28408 var records = this.store.getRange();
28409 if(records.length < 1) {
28411 // is this valid?? = should it render a template??
28413 this.el.update(this.emptyText);
28417 if (this.dataName) {
28418 this.el.update(t.apply(this.store.meta)); //????
28419 el = this.el.child('.roo-tpl-' + this.dataName);
28422 for(var i = 0, len = records.length; i < len; i++){
28423 var data = this.prepareData(records[i].data, i, records[i]);
28424 this.fireEvent("preparedata", this, data, i, records[i]);
28426 var d = Roo.apply({}, data);
28429 Roo.apply(d, {'roo-id' : Roo.id()});
28433 Roo.each(this.parent.item, function(item){
28434 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28437 Roo.apply(d, {'roo-data-checked' : 'checked'});
28441 html[html.length] = Roo.util.Format.trim(
28443 t.applySubtemplate(this.dataName, d, this.store.meta) :
28450 el.update(html.join(""));
28451 this.nodes = el.dom.childNodes;
28452 this.updateIndexes(0);
28457 * Function to override to reformat the data that is sent to
28458 * the template for each node.
28459 * DEPRICATED - use the preparedata event handler.
28460 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28461 * a JSON object for an UpdateManager bound view).
28463 prepareData : function(data, index, record)
28465 this.fireEvent("preparedata", this, data, index, record);
28469 onUpdate : function(ds, record){
28470 // Roo.log('on update');
28471 this.clearSelections();
28472 var index = this.store.indexOf(record);
28473 var n = this.nodes[index];
28474 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28475 n.parentNode.removeChild(n);
28476 this.updateIndexes(index, index);
28482 onAdd : function(ds, records, index)
28484 //Roo.log(['on Add', ds, records, index] );
28485 this.clearSelections();
28486 if(this.nodes.length == 0){
28490 var n = this.nodes[index];
28491 for(var i = 0, len = records.length; i < len; i++){
28492 var d = this.prepareData(records[i].data, i, records[i]);
28494 this.tpl.insertBefore(n, d);
28497 this.tpl.append(this.el, d);
28500 this.updateIndexes(index);
28503 onRemove : function(ds, record, index){
28504 // Roo.log('onRemove');
28505 this.clearSelections();
28506 var el = this.dataName ?
28507 this.el.child('.roo-tpl-' + this.dataName) :
28510 el.dom.removeChild(this.nodes[index]);
28511 this.updateIndexes(index);
28515 * Refresh an individual node.
28516 * @param {Number} index
28518 refreshNode : function(index){
28519 this.onUpdate(this.store, this.store.getAt(index));
28522 updateIndexes : function(startIndex, endIndex){
28523 var ns = this.nodes;
28524 startIndex = startIndex || 0;
28525 endIndex = endIndex || ns.length - 1;
28526 for(var i = startIndex; i <= endIndex; i++){
28527 ns[i].nodeIndex = i;
28532 * Changes the data store this view uses and refresh the view.
28533 * @param {Store} store
28535 setStore : function(store, initial){
28536 if(!initial && this.store){
28537 this.store.un("datachanged", this.refresh);
28538 this.store.un("add", this.onAdd);
28539 this.store.un("remove", this.onRemove);
28540 this.store.un("update", this.onUpdate);
28541 this.store.un("clear", this.refresh);
28542 this.store.un("beforeload", this.onBeforeLoad);
28543 this.store.un("load", this.onLoad);
28544 this.store.un("loadexception", this.onLoad);
28548 store.on("datachanged", this.refresh, this);
28549 store.on("add", this.onAdd, this);
28550 store.on("remove", this.onRemove, this);
28551 store.on("update", this.onUpdate, this);
28552 store.on("clear", this.refresh, this);
28553 store.on("beforeload", this.onBeforeLoad, this);
28554 store.on("load", this.onLoad, this);
28555 store.on("loadexception", this.onLoad, this);
28563 * onbeforeLoad - masks the loading area.
28566 onBeforeLoad : function(store,opts)
28568 //Roo.log('onBeforeLoad');
28570 this.el.update("");
28572 this.el.mask(this.mask ? this.mask : "Loading" );
28574 onLoad : function ()
28581 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28582 * @param {HTMLElement} node
28583 * @return {HTMLElement} The template node
28585 findItemFromChild : function(node){
28586 var el = this.dataName ?
28587 this.el.child('.roo-tpl-' + this.dataName,true) :
28590 if(!node || node.parentNode == el){
28593 var p = node.parentNode;
28594 while(p && p != el){
28595 if(p.parentNode == el){
28604 onClick : function(e){
28605 var item = this.findItemFromChild(e.getTarget());
28607 var index = this.indexOf(item);
28608 if(this.onItemClick(item, index, e) !== false){
28609 this.fireEvent("click", this, index, item, e);
28612 this.clearSelections();
28617 onContextMenu : function(e){
28618 var item = this.findItemFromChild(e.getTarget());
28620 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28625 onDblClick : function(e){
28626 var item = this.findItemFromChild(e.getTarget());
28628 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28632 onItemClick : function(item, index, e)
28634 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28637 if (this.toggleSelect) {
28638 var m = this.isSelected(item) ? 'unselect' : 'select';
28641 _t[m](item, true, false);
28644 if(this.multiSelect || this.singleSelect){
28645 if(this.multiSelect && e.shiftKey && this.lastSelection){
28646 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28648 this.select(item, this.multiSelect && e.ctrlKey);
28649 this.lastSelection = item;
28652 if(!this.tickable){
28653 e.preventDefault();
28661 * Get the number of selected nodes.
28664 getSelectionCount : function(){
28665 return this.selections.length;
28669 * Get the currently selected nodes.
28670 * @return {Array} An array of HTMLElements
28672 getSelectedNodes : function(){
28673 return this.selections;
28677 * Get the indexes of the selected nodes.
28680 getSelectedIndexes : function(){
28681 var indexes = [], s = this.selections;
28682 for(var i = 0, len = s.length; i < len; i++){
28683 indexes.push(s[i].nodeIndex);
28689 * Clear all selections
28690 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28692 clearSelections : function(suppressEvent){
28693 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28694 this.cmp.elements = this.selections;
28695 this.cmp.removeClass(this.selectedClass);
28696 this.selections = [];
28697 if(!suppressEvent){
28698 this.fireEvent("selectionchange", this, this.selections);
28704 * Returns true if the passed node is selected
28705 * @param {HTMLElement/Number} node The node or node index
28706 * @return {Boolean}
28708 isSelected : function(node){
28709 var s = this.selections;
28713 node = this.getNode(node);
28714 return s.indexOf(node) !== -1;
28719 * @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
28720 * @param {Boolean} keepExisting (optional) true to keep existing selections
28721 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28723 select : function(nodeInfo, keepExisting, suppressEvent){
28724 if(nodeInfo instanceof Array){
28726 this.clearSelections(true);
28728 for(var i = 0, len = nodeInfo.length; i < len; i++){
28729 this.select(nodeInfo[i], true, true);
28733 var node = this.getNode(nodeInfo);
28734 if(!node || this.isSelected(node)){
28735 return; // already selected.
28738 this.clearSelections(true);
28741 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28742 Roo.fly(node).addClass(this.selectedClass);
28743 this.selections.push(node);
28744 if(!suppressEvent){
28745 this.fireEvent("selectionchange", this, this.selections);
28753 * @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
28754 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28755 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28757 unselect : function(nodeInfo, keepExisting, suppressEvent)
28759 if(nodeInfo instanceof Array){
28760 Roo.each(this.selections, function(s) {
28761 this.unselect(s, nodeInfo);
28765 var node = this.getNode(nodeInfo);
28766 if(!node || !this.isSelected(node)){
28767 //Roo.log("not selected");
28768 return; // not selected.
28772 Roo.each(this.selections, function(s) {
28774 Roo.fly(node).removeClass(this.selectedClass);
28781 this.selections= ns;
28782 this.fireEvent("selectionchange", this, this.selections);
28786 * Gets a template node.
28787 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28788 * @return {HTMLElement} The node or null if it wasn't found
28790 getNode : function(nodeInfo){
28791 if(typeof nodeInfo == "string"){
28792 return document.getElementById(nodeInfo);
28793 }else if(typeof nodeInfo == "number"){
28794 return this.nodes[nodeInfo];
28800 * Gets a range template nodes.
28801 * @param {Number} startIndex
28802 * @param {Number} endIndex
28803 * @return {Array} An array of nodes
28805 getNodes : function(start, end){
28806 var ns = this.nodes;
28807 start = start || 0;
28808 end = typeof end == "undefined" ? ns.length - 1 : end;
28811 for(var i = start; i <= end; i++){
28815 for(var i = start; i >= end; i--){
28823 * Finds the index of the passed node
28824 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28825 * @return {Number} The index of the node or -1
28827 indexOf : function(node){
28828 node = this.getNode(node);
28829 if(typeof node.nodeIndex == "number"){
28830 return node.nodeIndex;
28832 var ns = this.nodes;
28833 for(var i = 0, len = ns.length; i < len; i++){
28843 * Ext JS Library 1.1.1
28844 * Copyright(c) 2006-2007, Ext JS, LLC.
28846 * Originally Released Under LGPL - original licence link has changed is not relivant.
28849 * <script type="text/javascript">
28853 * @class Roo.JsonView
28854 * @extends Roo.View
28855 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28857 var view = new Roo.JsonView({
28858 container: "my-element",
28859 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
28864 // listen for node click?
28865 view.on("click", function(vw, index, node, e){
28866 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28869 // direct load of JSON data
28870 view.load("foobar.php");
28872 // Example from my blog list
28873 var tpl = new Roo.Template(
28874 '<div class="entry">' +
28875 '<a class="entry-title" href="{link}">{title}</a>' +
28876 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
28877 "</div><hr />"
28880 var moreView = new Roo.JsonView({
28881 container : "entry-list",
28885 moreView.on("beforerender", this.sortEntries, this);
28887 url: "/blog/get-posts.php",
28888 params: "allposts=true",
28889 text: "Loading Blog Entries..."
28893 * Note: old code is supported with arguments : (container, template, config)
28897 * Create a new JsonView
28899 * @param {Object} config The config object
28902 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28905 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28907 var um = this.el.getUpdateManager();
28908 um.setRenderer(this);
28909 um.on("update", this.onLoad, this);
28910 um.on("failure", this.onLoadException, this);
28913 * @event beforerender
28914 * Fires before rendering of the downloaded JSON data.
28915 * @param {Roo.JsonView} this
28916 * @param {Object} data The JSON data loaded
28920 * Fires when data is loaded.
28921 * @param {Roo.JsonView} this
28922 * @param {Object} data The JSON data loaded
28923 * @param {Object} response The raw Connect response object
28926 * @event loadexception
28927 * Fires when loading fails.
28928 * @param {Roo.JsonView} this
28929 * @param {Object} response The raw Connect response object
28932 'beforerender' : true,
28934 'loadexception' : true
28937 Roo.extend(Roo.JsonView, Roo.View, {
28939 * @type {String} The root property in the loaded JSON object that contains the data
28944 * Refreshes the view.
28946 refresh : function(){
28947 this.clearSelections();
28948 this.el.update("");
28950 var o = this.jsonData;
28951 if(o && o.length > 0){
28952 for(var i = 0, len = o.length; i < len; i++){
28953 var data = this.prepareData(o[i], i, o);
28954 html[html.length] = this.tpl.apply(data);
28957 html.push(this.emptyText);
28959 this.el.update(html.join(""));
28960 this.nodes = this.el.dom.childNodes;
28961 this.updateIndexes(0);
28965 * 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.
28966 * @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:
28969 url: "your-url.php",
28970 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28971 callback: yourFunction,
28972 scope: yourObject, //(optional scope)
28975 text: "Loading...",
28980 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28981 * 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.
28982 * @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}
28983 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28984 * @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.
28987 var um = this.el.getUpdateManager();
28988 um.update.apply(um, arguments);
28991 // note - render is a standard framework call...
28992 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28993 render : function(el, response){
28995 this.clearSelections();
28996 this.el.update("");
28999 if (response != '') {
29000 o = Roo.util.JSON.decode(response.responseText);
29003 o = o[this.jsonRoot];
29009 * The current JSON data or null
29012 this.beforeRender();
29017 * Get the number of records in the current JSON dataset
29020 getCount : function(){
29021 return this.jsonData ? this.jsonData.length : 0;
29025 * Returns the JSON object for the specified node(s)
29026 * @param {HTMLElement/Array} node The node or an array of nodes
29027 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29028 * you get the JSON object for the node
29030 getNodeData : function(node){
29031 if(node instanceof Array){
29033 for(var i = 0, len = node.length; i < len; i++){
29034 data.push(this.getNodeData(node[i]));
29038 return this.jsonData[this.indexOf(node)] || null;
29041 beforeRender : function(){
29042 this.snapshot = this.jsonData;
29044 this.sort.apply(this, this.sortInfo);
29046 this.fireEvent("beforerender", this, this.jsonData);
29049 onLoad : function(el, o){
29050 this.fireEvent("load", this, this.jsonData, o);
29053 onLoadException : function(el, o){
29054 this.fireEvent("loadexception", this, o);
29058 * Filter the data by a specific property.
29059 * @param {String} property A property on your JSON objects
29060 * @param {String/RegExp} value Either string that the property values
29061 * should start with, or a RegExp to test against the property
29063 filter : function(property, value){
29066 var ss = this.snapshot;
29067 if(typeof value == "string"){
29068 var vlen = value.length;
29070 this.clearFilter();
29073 value = value.toLowerCase();
29074 for(var i = 0, len = ss.length; i < len; i++){
29076 if(o[property].substr(0, vlen).toLowerCase() == value){
29080 } else if(value.exec){ // regex?
29081 for(var i = 0, len = ss.length; i < len; i++){
29083 if(value.test(o[property])){
29090 this.jsonData = data;
29096 * Filter by a function. The passed function will be called with each
29097 * object in the current dataset. If the function returns true the value is kept,
29098 * otherwise it is filtered.
29099 * @param {Function} fn
29100 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29102 filterBy : function(fn, scope){
29105 var ss = this.snapshot;
29106 for(var i = 0, len = ss.length; i < len; i++){
29108 if(fn.call(scope || this, o)){
29112 this.jsonData = data;
29118 * Clears the current filter.
29120 clearFilter : function(){
29121 if(this.snapshot && this.jsonData != this.snapshot){
29122 this.jsonData = this.snapshot;
29129 * Sorts the data for this view and refreshes it.
29130 * @param {String} property A property on your JSON objects to sort on
29131 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29132 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29134 sort : function(property, dir, sortType){
29135 this.sortInfo = Array.prototype.slice.call(arguments, 0);
29138 var dsc = dir && dir.toLowerCase() == "desc";
29139 var f = function(o1, o2){
29140 var v1 = sortType ? sortType(o1[p]) : o1[p];
29141 var v2 = sortType ? sortType(o2[p]) : o2[p];
29144 return dsc ? +1 : -1;
29145 } else if(v1 > v2){
29146 return dsc ? -1 : +1;
29151 this.jsonData.sort(f);
29153 if(this.jsonData != this.snapshot){
29154 this.snapshot.sort(f);
29160 * Ext JS Library 1.1.1
29161 * Copyright(c) 2006-2007, Ext JS, LLC.
29163 * Originally Released Under LGPL - original licence link has changed is not relivant.
29166 * <script type="text/javascript">
29171 * @class Roo.ColorPalette
29172 * @extends Roo.Component
29173 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
29174 * Here's an example of typical usage:
29176 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
29177 cp.render('my-div');
29179 cp.on('select', function(palette, selColor){
29180 // do something with selColor
29184 * Create a new ColorPalette
29185 * @param {Object} config The config object
29187 Roo.ColorPalette = function(config){
29188 Roo.ColorPalette.superclass.constructor.call(this, config);
29192 * Fires when a color is selected
29193 * @param {ColorPalette} this
29194 * @param {String} color The 6-digit color hex code (without the # symbol)
29200 this.on("select", this.handler, this.scope, true);
29203 Roo.extend(Roo.ColorPalette, Roo.Component, {
29205 * @cfg {String} itemCls
29206 * The CSS class to apply to the containing element (defaults to "x-color-palette")
29208 itemCls : "x-color-palette",
29210 * @cfg {String} value
29211 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
29212 * the hex codes are case-sensitive.
29215 clickEvent:'click',
29217 ctype: "Roo.ColorPalette",
29220 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29222 allowReselect : false,
29225 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
29226 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
29227 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29228 * of colors with the width setting until the box is symmetrical.</p>
29229 * <p>You can override individual colors if needed:</p>
29231 var cp = new Roo.ColorPalette();
29232 cp.colors[0] = "FF0000"; // change the first box to red
29235 Or you can provide a custom array of your own for complete control:
29237 var cp = new Roo.ColorPalette();
29238 cp.colors = ["000000", "993300", "333300"];
29243 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29244 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29245 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29246 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29247 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29251 onRender : function(container, position){
29252 var t = new Roo.MasterTemplate(
29253 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
29255 var c = this.colors;
29256 for(var i = 0, len = c.length; i < len; i++){
29259 var el = document.createElement("div");
29260 el.className = this.itemCls;
29262 container.dom.insertBefore(el, position);
29263 this.el = Roo.get(el);
29264 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
29265 if(this.clickEvent != 'click'){
29266 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
29271 afterRender : function(){
29272 Roo.ColorPalette.superclass.afterRender.call(this);
29274 var s = this.value;
29281 handleClick : function(e, t){
29282 e.preventDefault();
29283 if(!this.disabled){
29284 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29285 this.select(c.toUpperCase());
29290 * Selects the specified color in the palette (fires the select event)
29291 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29293 select : function(color){
29294 color = color.replace("#", "");
29295 if(color != this.value || this.allowReselect){
29298 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29300 el.child("a.color-"+color).addClass("x-color-palette-sel");
29301 this.value = color;
29302 this.fireEvent("select", this, color);
29307 * Ext JS Library 1.1.1
29308 * Copyright(c) 2006-2007, Ext JS, LLC.
29310 * Originally Released Under LGPL - original licence link has changed is not relivant.
29313 * <script type="text/javascript">
29317 * @class Roo.DatePicker
29318 * @extends Roo.Component
29319 * Simple date picker class.
29321 * Create a new DatePicker
29322 * @param {Object} config The config object
29324 Roo.DatePicker = function(config){
29325 Roo.DatePicker.superclass.constructor.call(this, config);
29327 this.value = config && config.value ?
29328 config.value.clearTime() : new Date().clearTime();
29333 * Fires when a date is selected
29334 * @param {DatePicker} this
29335 * @param {Date} date The selected date
29339 * @event monthchange
29340 * Fires when the displayed month changes
29341 * @param {DatePicker} this
29342 * @param {Date} date The selected month
29344 'monthchange': true
29348 this.on("select", this.handler, this.scope || this);
29350 // build the disabledDatesRE
29351 if(!this.disabledDatesRE && this.disabledDates){
29352 var dd = this.disabledDates;
29354 for(var i = 0; i < dd.length; i++){
29356 if(i != dd.length-1) {
29360 this.disabledDatesRE = new RegExp(re + ")");
29364 Roo.extend(Roo.DatePicker, Roo.Component, {
29366 * @cfg {String} todayText
29367 * The text to display on the button that selects the current date (defaults to "Today")
29369 todayText : "Today",
29371 * @cfg {String} okText
29372 * The text to display on the ok button
29374 okText : " OK ", //   to give the user extra clicking room
29376 * @cfg {String} cancelText
29377 * The text to display on the cancel button
29379 cancelText : "Cancel",
29381 * @cfg {String} todayTip
29382 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29384 todayTip : "{0} (Spacebar)",
29386 * @cfg {Date} minDate
29387 * Minimum allowable date (JavaScript date object, defaults to null)
29391 * @cfg {Date} maxDate
29392 * Maximum allowable date (JavaScript date object, defaults to null)
29396 * @cfg {String} minText
29397 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29399 minText : "This date is before the minimum date",
29401 * @cfg {String} maxText
29402 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29404 maxText : "This date is after the maximum date",
29406 * @cfg {String} format
29407 * The default date format string which can be overriden for localization support. The format must be
29408 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29412 * @cfg {Array} disabledDays
29413 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29415 disabledDays : null,
29417 * @cfg {String} disabledDaysText
29418 * The tooltip to display when the date falls on a disabled day (defaults to "")
29420 disabledDaysText : "",
29422 * @cfg {RegExp} disabledDatesRE
29423 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29425 disabledDatesRE : null,
29427 * @cfg {String} disabledDatesText
29428 * The tooltip text to display when the date falls on a disabled date (defaults to "")
29430 disabledDatesText : "",
29432 * @cfg {Boolean} constrainToViewport
29433 * True to constrain the date picker to the viewport (defaults to true)
29435 constrainToViewport : true,
29437 * @cfg {Array} monthNames
29438 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29440 monthNames : Date.monthNames,
29442 * @cfg {Array} dayNames
29443 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29445 dayNames : Date.dayNames,
29447 * @cfg {String} nextText
29448 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29450 nextText: 'Next Month (Control+Right)',
29452 * @cfg {String} prevText
29453 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29455 prevText: 'Previous Month (Control+Left)',
29457 * @cfg {String} monthYearText
29458 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29460 monthYearText: 'Choose a month (Control+Up/Down to move years)',
29462 * @cfg {Number} startDay
29463 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29467 * @cfg {Bool} showClear
29468 * Show a clear button (usefull for date form elements that can be blank.)
29474 * Sets the value of the date field
29475 * @param {Date} value The date to set
29477 setValue : function(value){
29478 var old = this.value;
29480 if (typeof(value) == 'string') {
29482 value = Date.parseDate(value, this.format);
29485 value = new Date();
29488 this.value = value.clearTime(true);
29490 this.update(this.value);
29495 * Gets the current selected value of the date field
29496 * @return {Date} The selected date
29498 getValue : function(){
29503 focus : function(){
29505 this.update(this.activeDate);
29510 onRender : function(container, position){
29513 '<table cellspacing="0">',
29514 '<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>',
29515 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29516 var dn = this.dayNames;
29517 for(var i = 0; i < 7; i++){
29518 var d = this.startDay+i;
29522 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29524 m[m.length] = "</tr></thead><tbody><tr>";
29525 for(var i = 0; i < 42; i++) {
29526 if(i % 7 == 0 && i != 0){
29527 m[m.length] = "</tr><tr>";
29529 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29531 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29532 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29534 var el = document.createElement("div");
29535 el.className = "x-date-picker";
29536 el.innerHTML = m.join("");
29538 container.dom.insertBefore(el, position);
29540 this.el = Roo.get(el);
29541 this.eventEl = Roo.get(el.firstChild);
29543 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29544 handler: this.showPrevMonth,
29546 preventDefault:true,
29550 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29551 handler: this.showNextMonth,
29553 preventDefault:true,
29557 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
29559 this.monthPicker = this.el.down('div.x-date-mp');
29560 this.monthPicker.enableDisplayMode('block');
29562 var kn = new Roo.KeyNav(this.eventEl, {
29563 "left" : function(e){
29565 this.showPrevMonth() :
29566 this.update(this.activeDate.add("d", -1));
29569 "right" : function(e){
29571 this.showNextMonth() :
29572 this.update(this.activeDate.add("d", 1));
29575 "up" : function(e){
29577 this.showNextYear() :
29578 this.update(this.activeDate.add("d", -7));
29581 "down" : function(e){
29583 this.showPrevYear() :
29584 this.update(this.activeDate.add("d", 7));
29587 "pageUp" : function(e){
29588 this.showNextMonth();
29591 "pageDown" : function(e){
29592 this.showPrevMonth();
29595 "enter" : function(e){
29596 e.stopPropagation();
29603 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
29605 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
29607 this.el.unselectable();
29609 this.cells = this.el.select("table.x-date-inner tbody td");
29610 this.textNodes = this.el.query("table.x-date-inner tbody span");
29612 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29614 tooltip: this.monthYearText
29617 this.mbtn.on('click', this.showMonthPicker, this);
29618 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29621 var today = (new Date()).dateFormat(this.format);
29623 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29624 if (this.showClear) {
29625 baseTb.add( new Roo.Toolbar.Fill());
29628 text: String.format(this.todayText, today),
29629 tooltip: String.format(this.todayTip, today),
29630 handler: this.selectToday,
29634 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29637 if (this.showClear) {
29639 baseTb.add( new Roo.Toolbar.Fill());
29642 cls: 'x-btn-icon x-btn-clear',
29643 handler: function() {
29645 this.fireEvent("select", this, '');
29655 this.update(this.value);
29658 createMonthPicker : function(){
29659 if(!this.monthPicker.dom.firstChild){
29660 var buf = ['<table border="0" cellspacing="0">'];
29661 for(var i = 0; i < 6; i++){
29663 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29664 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29666 '<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>' :
29667 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29671 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29673 '</button><button type="button" class="x-date-mp-cancel">',
29675 '</button></td></tr>',
29678 this.monthPicker.update(buf.join(''));
29679 this.monthPicker.on('click', this.onMonthClick, this);
29680 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29682 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29683 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29685 this.mpMonths.each(function(m, a, i){
29688 m.dom.xmonth = 5 + Math.round(i * .5);
29690 m.dom.xmonth = Math.round((i-1) * .5);
29696 showMonthPicker : function(){
29697 this.createMonthPicker();
29698 var size = this.el.getSize();
29699 this.monthPicker.setSize(size);
29700 this.monthPicker.child('table').setSize(size);
29702 this.mpSelMonth = (this.activeDate || this.value).getMonth();
29703 this.updateMPMonth(this.mpSelMonth);
29704 this.mpSelYear = (this.activeDate || this.value).getFullYear();
29705 this.updateMPYear(this.mpSelYear);
29707 this.monthPicker.slideIn('t', {duration:.2});
29710 updateMPYear : function(y){
29712 var ys = this.mpYears.elements;
29713 for(var i = 1; i <= 10; i++){
29714 var td = ys[i-1], y2;
29716 y2 = y + Math.round(i * .5);
29717 td.firstChild.innerHTML = y2;
29720 y2 = y - (5-Math.round(i * .5));
29721 td.firstChild.innerHTML = y2;
29724 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29728 updateMPMonth : function(sm){
29729 this.mpMonths.each(function(m, a, i){
29730 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29734 selectMPMonth: function(m){
29738 onMonthClick : function(e, t){
29740 var el = new Roo.Element(t), pn;
29741 if(el.is('button.x-date-mp-cancel')){
29742 this.hideMonthPicker();
29744 else if(el.is('button.x-date-mp-ok')){
29745 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29746 this.hideMonthPicker();
29748 else if(pn = el.up('td.x-date-mp-month', 2)){
29749 this.mpMonths.removeClass('x-date-mp-sel');
29750 pn.addClass('x-date-mp-sel');
29751 this.mpSelMonth = pn.dom.xmonth;
29753 else if(pn = el.up('td.x-date-mp-year', 2)){
29754 this.mpYears.removeClass('x-date-mp-sel');
29755 pn.addClass('x-date-mp-sel');
29756 this.mpSelYear = pn.dom.xyear;
29758 else if(el.is('a.x-date-mp-prev')){
29759 this.updateMPYear(this.mpyear-10);
29761 else if(el.is('a.x-date-mp-next')){
29762 this.updateMPYear(this.mpyear+10);
29766 onMonthDblClick : function(e, t){
29768 var el = new Roo.Element(t), pn;
29769 if(pn = el.up('td.x-date-mp-month', 2)){
29770 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29771 this.hideMonthPicker();
29773 else if(pn = el.up('td.x-date-mp-year', 2)){
29774 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29775 this.hideMonthPicker();
29779 hideMonthPicker : function(disableAnim){
29780 if(this.monthPicker){
29781 if(disableAnim === true){
29782 this.monthPicker.hide();
29784 this.monthPicker.slideOut('t', {duration:.2});
29790 showPrevMonth : function(e){
29791 this.update(this.activeDate.add("mo", -1));
29795 showNextMonth : function(e){
29796 this.update(this.activeDate.add("mo", 1));
29800 showPrevYear : function(){
29801 this.update(this.activeDate.add("y", -1));
29805 showNextYear : function(){
29806 this.update(this.activeDate.add("y", 1));
29810 handleMouseWheel : function(e){
29811 var delta = e.getWheelDelta();
29813 this.showPrevMonth();
29815 } else if(delta < 0){
29816 this.showNextMonth();
29822 handleDateClick : function(e, t){
29824 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29825 this.setValue(new Date(t.dateValue));
29826 this.fireEvent("select", this, this.value);
29831 selectToday : function(){
29832 this.setValue(new Date().clearTime());
29833 this.fireEvent("select", this, this.value);
29837 update : function(date)
29839 var vd = this.activeDate;
29840 this.activeDate = date;
29842 var t = date.getTime();
29843 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29844 this.cells.removeClass("x-date-selected");
29845 this.cells.each(function(c){
29846 if(c.dom.firstChild.dateValue == t){
29847 c.addClass("x-date-selected");
29848 setTimeout(function(){
29849 try{c.dom.firstChild.focus();}catch(e){}
29858 var days = date.getDaysInMonth();
29859 var firstOfMonth = date.getFirstDateOfMonth();
29860 var startingPos = firstOfMonth.getDay()-this.startDay;
29862 if(startingPos <= this.startDay){
29866 var pm = date.add("mo", -1);
29867 var prevStart = pm.getDaysInMonth()-startingPos;
29869 var cells = this.cells.elements;
29870 var textEls = this.textNodes;
29871 days += startingPos;
29873 // convert everything to numbers so it's fast
29874 var day = 86400000;
29875 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29876 var today = new Date().clearTime().getTime();
29877 var sel = date.clearTime().getTime();
29878 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29879 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29880 var ddMatch = this.disabledDatesRE;
29881 var ddText = this.disabledDatesText;
29882 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29883 var ddaysText = this.disabledDaysText;
29884 var format = this.format;
29886 var setCellClass = function(cal, cell){
29888 var t = d.getTime();
29889 cell.firstChild.dateValue = t;
29891 cell.className += " x-date-today";
29892 cell.title = cal.todayText;
29895 cell.className += " x-date-selected";
29896 setTimeout(function(){
29897 try{cell.firstChild.focus();}catch(e){}
29902 cell.className = " x-date-disabled";
29903 cell.title = cal.minText;
29907 cell.className = " x-date-disabled";
29908 cell.title = cal.maxText;
29912 if(ddays.indexOf(d.getDay()) != -1){
29913 cell.title = ddaysText;
29914 cell.className = " x-date-disabled";
29917 if(ddMatch && format){
29918 var fvalue = d.dateFormat(format);
29919 if(ddMatch.test(fvalue)){
29920 cell.title = ddText.replace("%0", fvalue);
29921 cell.className = " x-date-disabled";
29927 for(; i < startingPos; i++) {
29928 textEls[i].innerHTML = (++prevStart);
29929 d.setDate(d.getDate()+1);
29930 cells[i].className = "x-date-prevday";
29931 setCellClass(this, cells[i]);
29933 for(; i < days; i++){
29934 intDay = i - startingPos + 1;
29935 textEls[i].innerHTML = (intDay);
29936 d.setDate(d.getDate()+1);
29937 cells[i].className = "x-date-active";
29938 setCellClass(this, cells[i]);
29941 for(; i < 42; i++) {
29942 textEls[i].innerHTML = (++extraDays);
29943 d.setDate(d.getDate()+1);
29944 cells[i].className = "x-date-nextday";
29945 setCellClass(this, cells[i]);
29948 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29949 this.fireEvent('monthchange', this, date);
29951 if(!this.internalRender){
29952 var main = this.el.dom.firstChild;
29953 var w = main.offsetWidth;
29954 this.el.setWidth(w + this.el.getBorderWidth("lr"));
29955 Roo.fly(main).setWidth(w);
29956 this.internalRender = true;
29957 // opera does not respect the auto grow header center column
29958 // then, after it gets a width opera refuses to recalculate
29959 // without a second pass
29960 if(Roo.isOpera && !this.secondPass){
29961 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29962 this.secondPass = true;
29963 this.update.defer(10, this, [date]);
29971 * Ext JS Library 1.1.1
29972 * Copyright(c) 2006-2007, Ext JS, LLC.
29974 * Originally Released Under LGPL - original licence link has changed is not relivant.
29977 * <script type="text/javascript">
29980 * @class Roo.TabPanel
29981 * @extends Roo.util.Observable
29982 * A lightweight tab container.
29986 // basic tabs 1, built from existing content
29987 var tabs = new Roo.TabPanel("tabs1");
29988 tabs.addTab("script", "View Script");
29989 tabs.addTab("markup", "View Markup");
29990 tabs.activate("script");
29992 // more advanced tabs, built from javascript
29993 var jtabs = new Roo.TabPanel("jtabs");
29994 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29996 // set up the UpdateManager
29997 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29998 var updater = tab2.getUpdateManager();
29999 updater.setDefaultUrl("ajax1.htm");
30000 tab2.on('activate', updater.refresh, updater, true);
30002 // Use setUrl for Ajax loading
30003 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
30004 tab3.setUrl("ajax2.htm", null, true);
30007 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
30010 jtabs.activate("jtabs-1");
30013 * Create a new TabPanel.
30014 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30015 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30017 Roo.TabPanel = function(container, config){
30019 * The container element for this TabPanel.
30020 * @type Roo.Element
30022 this.el = Roo.get(container, true);
30024 if(typeof config == "boolean"){
30025 this.tabPosition = config ? "bottom" : "top";
30027 Roo.apply(this, config);
30030 if(this.tabPosition == "bottom"){
30031 this.bodyEl = Roo.get(this.createBody(this.el.dom));
30032 this.el.addClass("x-tabs-bottom");
30034 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30035 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30036 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30038 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30040 if(this.tabPosition != "bottom"){
30041 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30042 * @type Roo.Element
30044 this.bodyEl = Roo.get(this.createBody(this.el.dom));
30045 this.el.addClass("x-tabs-top");
30049 this.bodyEl.setStyle("position", "relative");
30051 this.active = null;
30052 this.activateDelegate = this.activate.createDelegate(this);
30057 * Fires when the active tab changes
30058 * @param {Roo.TabPanel} this
30059 * @param {Roo.TabPanelItem} activePanel The new active tab
30063 * @event beforetabchange
30064 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30065 * @param {Roo.TabPanel} this
30066 * @param {Object} e Set cancel to true on this object to cancel the tab change
30067 * @param {Roo.TabPanelItem} tab The tab being changed to
30069 "beforetabchange" : true
30072 Roo.EventManager.onWindowResize(this.onResize, this);
30073 this.cpad = this.el.getPadding("lr");
30074 this.hiddenCount = 0;
30077 // toolbar on the tabbar support...
30078 if (this.toolbar) {
30079 var tcfg = this.toolbar;
30080 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
30081 this.toolbar = new Roo.Toolbar(tcfg);
30082 if (Roo.isSafari) {
30083 var tbl = tcfg.container.child('table', true);
30084 tbl.setAttribute('width', '100%');
30091 Roo.TabPanel.superclass.constructor.call(this);
30094 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30096 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30098 tabPosition : "top",
30100 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30102 currentTabWidth : 0,
30104 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30108 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30112 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30114 preferredTabWidth : 175,
30116 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30118 resizeTabs : false,
30120 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30122 monitorResize : true,
30124 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
30129 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30130 * @param {String} id The id of the div to use <b>or create</b>
30131 * @param {String} text The text for the tab
30132 * @param {String} content (optional) Content to put in the TabPanelItem body
30133 * @param {Boolean} closable (optional) True to create a close icon on the tab
30134 * @return {Roo.TabPanelItem} The created TabPanelItem
30136 addTab : function(id, text, content, closable){
30137 var item = new Roo.TabPanelItem(this, id, text, closable);
30138 this.addTabItem(item);
30140 item.setContent(content);
30146 * Returns the {@link Roo.TabPanelItem} with the specified id/index
30147 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30148 * @return {Roo.TabPanelItem}
30150 getTab : function(id){
30151 return this.items[id];
30155 * Hides the {@link Roo.TabPanelItem} with the specified id/index
30156 * @param {String/Number} id The id or index of the TabPanelItem to hide.
30158 hideTab : function(id){
30159 var t = this.items[id];
30162 this.hiddenCount++;
30163 this.autoSizeTabs();
30168 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30169 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30171 unhideTab : function(id){
30172 var t = this.items[id];
30174 t.setHidden(false);
30175 this.hiddenCount--;
30176 this.autoSizeTabs();
30181 * Adds an existing {@link Roo.TabPanelItem}.
30182 * @param {Roo.TabPanelItem} item The TabPanelItem to add
30184 addTabItem : function(item){
30185 this.items[item.id] = item;
30186 this.items.push(item);
30187 if(this.resizeTabs){
30188 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30189 this.autoSizeTabs();
30196 * Removes a {@link Roo.TabPanelItem}.
30197 * @param {String/Number} id The id or index of the TabPanelItem to remove.
30199 removeTab : function(id){
30200 var items = this.items;
30201 var tab = items[id];
30202 if(!tab) { return; }
30203 var index = items.indexOf(tab);
30204 if(this.active == tab && items.length > 1){
30205 var newTab = this.getNextAvailable(index);
30210 this.stripEl.dom.removeChild(tab.pnode.dom);
30211 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30212 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30214 items.splice(index, 1);
30215 delete this.items[tab.id];
30216 tab.fireEvent("close", tab);
30217 tab.purgeListeners();
30218 this.autoSizeTabs();
30221 getNextAvailable : function(start){
30222 var items = this.items;
30224 // look for a next tab that will slide over to
30225 // replace the one being removed
30226 while(index < items.length){
30227 var item = items[++index];
30228 if(item && !item.isHidden()){
30232 // if one isn't found select the previous tab (on the left)
30235 var item = items[--index];
30236 if(item && !item.isHidden()){
30244 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30245 * @param {String/Number} id The id or index of the TabPanelItem to disable.
30247 disableTab : function(id){
30248 var tab = this.items[id];
30249 if(tab && this.active != tab){
30255 * Enables a {@link Roo.TabPanelItem} that is disabled.
30256 * @param {String/Number} id The id or index of the TabPanelItem to enable.
30258 enableTab : function(id){
30259 var tab = this.items[id];
30264 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30265 * @param {String/Number} id The id or index of the TabPanelItem to activate.
30266 * @return {Roo.TabPanelItem} The TabPanelItem.
30268 activate : function(id){
30269 var tab = this.items[id];
30273 if(tab == this.active || tab.disabled){
30277 this.fireEvent("beforetabchange", this, e, tab);
30278 if(e.cancel !== true && !tab.disabled){
30280 this.active.hide();
30282 this.active = this.items[id];
30283 this.active.show();
30284 this.fireEvent("tabchange", this, this.active);
30290 * Gets the active {@link Roo.TabPanelItem}.
30291 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30293 getActiveTab : function(){
30294 return this.active;
30298 * Updates the tab body element to fit the height of the container element
30299 * for overflow scrolling
30300 * @param {Number} targetHeight (optional) Override the starting height from the elements height
30302 syncHeight : function(targetHeight){
30303 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30304 var bm = this.bodyEl.getMargins();
30305 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30306 this.bodyEl.setHeight(newHeight);
30310 onResize : function(){
30311 if(this.monitorResize){
30312 this.autoSizeTabs();
30317 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30319 beginUpdate : function(){
30320 this.updating = true;
30324 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30326 endUpdate : function(){
30327 this.updating = false;
30328 this.autoSizeTabs();
30332 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30334 autoSizeTabs : function(){
30335 var count = this.items.length;
30336 var vcount = count - this.hiddenCount;
30337 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30340 var w = Math.max(this.el.getWidth() - this.cpad, 10);
30341 var availWidth = Math.floor(w / vcount);
30342 var b = this.stripBody;
30343 if(b.getWidth() > w){
30344 var tabs = this.items;
30345 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30346 if(availWidth < this.minTabWidth){
30347 /*if(!this.sleft){ // incomplete scrolling code
30348 this.createScrollButtons();
30351 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30354 if(this.currentTabWidth < this.preferredTabWidth){
30355 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30361 * Returns the number of tabs in this TabPanel.
30364 getCount : function(){
30365 return this.items.length;
30369 * Resizes all the tabs to the passed width
30370 * @param {Number} The new width
30372 setTabWidth : function(width){
30373 this.currentTabWidth = width;
30374 for(var i = 0, len = this.items.length; i < len; i++) {
30375 if(!this.items[i].isHidden()) {
30376 this.items[i].setWidth(width);
30382 * Destroys this TabPanel
30383 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30385 destroy : function(removeEl){
30386 Roo.EventManager.removeResizeListener(this.onResize, this);
30387 for(var i = 0, len = this.items.length; i < len; i++){
30388 this.items[i].purgeListeners();
30390 if(removeEl === true){
30391 this.el.update("");
30398 * @class Roo.TabPanelItem
30399 * @extends Roo.util.Observable
30400 * Represents an individual item (tab plus body) in a TabPanel.
30401 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30402 * @param {String} id The id of this TabPanelItem
30403 * @param {String} text The text for the tab of this TabPanelItem
30404 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30406 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30408 * The {@link Roo.TabPanel} this TabPanelItem belongs to
30409 * @type Roo.TabPanel
30411 this.tabPanel = tabPanel;
30413 * The id for this TabPanelItem
30418 this.disabled = false;
30422 this.loaded = false;
30423 this.closable = closable;
30426 * The body element for this TabPanelItem.
30427 * @type Roo.Element
30429 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30430 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30431 this.bodyEl.setStyle("display", "block");
30432 this.bodyEl.setStyle("zoom", "1");
30435 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30437 this.el = Roo.get(els.el, true);
30438 this.inner = Roo.get(els.inner, true);
30439 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30440 this.pnode = Roo.get(els.el.parentNode, true);
30441 this.el.on("mousedown", this.onTabMouseDown, this);
30442 this.el.on("click", this.onTabClick, this);
30445 var c = Roo.get(els.close, true);
30446 c.dom.title = this.closeText;
30447 c.addClassOnOver("close-over");
30448 c.on("click", this.closeClick, this);
30454 * Fires when this tab becomes the active tab.
30455 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30456 * @param {Roo.TabPanelItem} this
30460 * @event beforeclose
30461 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30462 * @param {Roo.TabPanelItem} this
30463 * @param {Object} e Set cancel to true on this object to cancel the close.
30465 "beforeclose": true,
30468 * Fires when this tab is closed.
30469 * @param {Roo.TabPanelItem} this
30473 * @event deactivate
30474 * Fires when this tab is no longer the active tab.
30475 * @param {Roo.TabPanel} tabPanel The parent TabPanel
30476 * @param {Roo.TabPanelItem} this
30478 "deactivate" : true
30480 this.hidden = false;
30482 Roo.TabPanelItem.superclass.constructor.call(this);
30485 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30486 purgeListeners : function(){
30487 Roo.util.Observable.prototype.purgeListeners.call(this);
30488 this.el.removeAllListeners();
30491 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30494 this.pnode.addClass("on");
30497 this.tabPanel.stripWrap.repaint();
30499 this.fireEvent("activate", this.tabPanel, this);
30503 * Returns true if this tab is the active tab.
30504 * @return {Boolean}
30506 isActive : function(){
30507 return this.tabPanel.getActiveTab() == this;
30511 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30514 this.pnode.removeClass("on");
30516 this.fireEvent("deactivate", this.tabPanel, this);
30519 hideAction : function(){
30520 this.bodyEl.hide();
30521 this.bodyEl.setStyle("position", "absolute");
30522 this.bodyEl.setLeft("-20000px");
30523 this.bodyEl.setTop("-20000px");
30526 showAction : function(){
30527 this.bodyEl.setStyle("position", "relative");
30528 this.bodyEl.setTop("");
30529 this.bodyEl.setLeft("");
30530 this.bodyEl.show();
30534 * Set the tooltip for the tab.
30535 * @param {String} tooltip The tab's tooltip
30537 setTooltip : function(text){
30538 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30539 this.textEl.dom.qtip = text;
30540 this.textEl.dom.removeAttribute('title');
30542 this.textEl.dom.title = text;
30546 onTabClick : function(e){
30547 e.preventDefault();
30548 this.tabPanel.activate(this.id);
30551 onTabMouseDown : function(e){
30552 e.preventDefault();
30553 this.tabPanel.activate(this.id);
30556 getWidth : function(){
30557 return this.inner.getWidth();
30560 setWidth : function(width){
30561 var iwidth = width - this.pnode.getPadding("lr");
30562 this.inner.setWidth(iwidth);
30563 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30564 this.pnode.setWidth(width);
30568 * Show or hide the tab
30569 * @param {Boolean} hidden True to hide or false to show.
30571 setHidden : function(hidden){
30572 this.hidden = hidden;
30573 this.pnode.setStyle("display", hidden ? "none" : "");
30577 * Returns true if this tab is "hidden"
30578 * @return {Boolean}
30580 isHidden : function(){
30581 return this.hidden;
30585 * Returns the text for this tab
30588 getText : function(){
30592 autoSize : function(){
30593 //this.el.beginMeasure();
30594 this.textEl.setWidth(1);
30596 * #2804 [new] Tabs in Roojs
30597 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30599 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30600 //this.el.endMeasure();
30604 * Sets the text for the tab (Note: this also sets the tooltip text)
30605 * @param {String} text The tab's text and tooltip
30607 setText : function(text){
30609 this.textEl.update(text);
30610 this.setTooltip(text);
30611 if(!this.tabPanel.resizeTabs){
30616 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30618 activate : function(){
30619 this.tabPanel.activate(this.id);
30623 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30625 disable : function(){
30626 if(this.tabPanel.active != this){
30627 this.disabled = true;
30628 this.pnode.addClass("disabled");
30633 * Enables this TabPanelItem if it was previously disabled.
30635 enable : function(){
30636 this.disabled = false;
30637 this.pnode.removeClass("disabled");
30641 * Sets the content for this TabPanelItem.
30642 * @param {String} content The content
30643 * @param {Boolean} loadScripts true to look for and load scripts
30645 setContent : function(content, loadScripts){
30646 this.bodyEl.update(content, loadScripts);
30650 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30651 * @return {Roo.UpdateManager} The UpdateManager
30653 getUpdateManager : function(){
30654 return this.bodyEl.getUpdateManager();
30658 * Set a URL to be used to load the content for this TabPanelItem.
30659 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30660 * @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)
30661 * @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)
30662 * @return {Roo.UpdateManager} The UpdateManager
30664 setUrl : function(url, params, loadOnce){
30665 if(this.refreshDelegate){
30666 this.un('activate', this.refreshDelegate);
30668 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30669 this.on("activate", this.refreshDelegate);
30670 return this.bodyEl.getUpdateManager();
30674 _handleRefresh : function(url, params, loadOnce){
30675 if(!loadOnce || !this.loaded){
30676 var updater = this.bodyEl.getUpdateManager();
30677 updater.update(url, params, this._setLoaded.createDelegate(this));
30682 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
30683 * Will fail silently if the setUrl method has not been called.
30684 * This does not activate the panel, just updates its content.
30686 refresh : function(){
30687 if(this.refreshDelegate){
30688 this.loaded = false;
30689 this.refreshDelegate();
30694 _setLoaded : function(){
30695 this.loaded = true;
30699 closeClick : function(e){
30702 this.fireEvent("beforeclose", this, o);
30703 if(o.cancel !== true){
30704 this.tabPanel.removeTab(this.id);
30708 * The text displayed in the tooltip for the close icon.
30711 closeText : "Close this tab"
30715 Roo.TabPanel.prototype.createStrip = function(container){
30716 var strip = document.createElement("div");
30717 strip.className = "x-tabs-wrap";
30718 container.appendChild(strip);
30722 Roo.TabPanel.prototype.createStripList = function(strip){
30723 // div wrapper for retard IE
30724 // returns the "tr" element.
30725 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30726 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30727 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30728 return strip.firstChild.firstChild.firstChild.firstChild;
30731 Roo.TabPanel.prototype.createBody = function(container){
30732 var body = document.createElement("div");
30733 Roo.id(body, "tab-body");
30734 Roo.fly(body).addClass("x-tabs-body");
30735 container.appendChild(body);
30739 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30740 var body = Roo.getDom(id);
30742 body = document.createElement("div");
30745 Roo.fly(body).addClass("x-tabs-item-body");
30746 bodyEl.insertBefore(body, bodyEl.firstChild);
30750 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30751 var td = document.createElement("td");
30752 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30753 //stripEl.appendChild(td);
30755 td.className = "x-tabs-closable";
30756 if(!this.closeTpl){
30757 this.closeTpl = new Roo.Template(
30758 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30759 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30760 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
30763 var el = this.closeTpl.overwrite(td, {"text": text});
30764 var close = el.getElementsByTagName("div")[0];
30765 var inner = el.getElementsByTagName("em")[0];
30766 return {"el": el, "close": close, "inner": inner};
30769 this.tabTpl = new Roo.Template(
30770 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30771 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30774 var el = this.tabTpl.overwrite(td, {"text": text});
30775 var inner = el.getElementsByTagName("em")[0];
30776 return {"el": el, "inner": inner};
30780 * Ext JS Library 1.1.1
30781 * Copyright(c) 2006-2007, Ext JS, LLC.
30783 * Originally Released Under LGPL - original licence link has changed is not relivant.
30786 * <script type="text/javascript">
30790 * @class Roo.Button
30791 * @extends Roo.util.Observable
30792 * Simple Button class
30793 * @cfg {String} text The button text
30794 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30795 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30796 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30797 * @cfg {Object} scope The scope of the handler
30798 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30799 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30800 * @cfg {Boolean} hidden True to start hidden (defaults to false)
30801 * @cfg {Boolean} disabled True to start disabled (defaults to false)
30802 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30803 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30804 applies if enableToggle = true)
30805 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30806 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30807 an {@link Roo.util.ClickRepeater} config object (defaults to false).
30809 * Create a new button
30810 * @param {Object} config The config object
30812 Roo.Button = function(renderTo, config)
30816 renderTo = config.renderTo || false;
30819 Roo.apply(this, config);
30823 * Fires when this button is clicked
30824 * @param {Button} this
30825 * @param {EventObject} e The click event
30830 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30831 * @param {Button} this
30832 * @param {Boolean} pressed
30837 * Fires when the mouse hovers over the button
30838 * @param {Button} this
30839 * @param {Event} e The event object
30841 'mouseover' : true,
30844 * Fires when the mouse exits the button
30845 * @param {Button} this
30846 * @param {Event} e The event object
30851 * Fires when the button is rendered
30852 * @param {Button} this
30857 this.menu = Roo.menu.MenuMgr.get(this.menu);
30859 // register listeners first!! - so render can be captured..
30860 Roo.util.Observable.call(this);
30862 this.render(renderTo);
30868 Roo.extend(Roo.Button, Roo.util.Observable, {
30874 * Read-only. True if this button is hidden
30879 * Read-only. True if this button is disabled
30884 * Read-only. True if this button is pressed (only if enableToggle = true)
30890 * @cfg {Number} tabIndex
30891 * The DOM tabIndex for this button (defaults to undefined)
30893 tabIndex : undefined,
30896 * @cfg {Boolean} enableToggle
30897 * True to enable pressed/not pressed toggling (defaults to false)
30899 enableToggle: false,
30901 * @cfg {Roo.menu.Menu} menu
30902 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30906 * @cfg {String} menuAlign
30907 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30909 menuAlign : "tl-bl?",
30912 * @cfg {String} iconCls
30913 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30915 iconCls : undefined,
30917 * @cfg {String} type
30918 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
30923 menuClassTarget: 'tr',
30926 * @cfg {String} clickEvent
30927 * The type of event to map to the button's event handler (defaults to 'click')
30929 clickEvent : 'click',
30932 * @cfg {Boolean} handleMouseEvents
30933 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30935 handleMouseEvents : true,
30938 * @cfg {String} tooltipType
30939 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30941 tooltipType : 'qtip',
30944 * @cfg {String} cls
30945 * A CSS class to apply to the button's main element.
30949 * @cfg {Roo.Template} template (Optional)
30950 * An {@link Roo.Template} with which to create the Button's main element. This Template must
30951 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30952 * require code modifications if required elements (e.g. a button) aren't present.
30956 render : function(renderTo){
30958 if(this.hideParent){
30959 this.parentEl = Roo.get(renderTo);
30961 if(!this.dhconfig){
30962 if(!this.template){
30963 if(!Roo.Button.buttonTemplate){
30964 // hideous table template
30965 Roo.Button.buttonTemplate = new Roo.Template(
30966 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30967 '<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>',
30968 "</tr></tbody></table>");
30970 this.template = Roo.Button.buttonTemplate;
30972 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
30973 var btnEl = btn.child("button:first");
30974 btnEl.on('focus', this.onFocus, this);
30975 btnEl.on('blur', this.onBlur, this);
30977 btn.addClass(this.cls);
30980 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30983 btnEl.addClass(this.iconCls);
30985 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30988 if(this.tabIndex !== undefined){
30989 btnEl.dom.tabIndex = this.tabIndex;
30992 if(typeof this.tooltip == 'object'){
30993 Roo.QuickTips.tips(Roo.apply({
30997 btnEl.dom[this.tooltipType] = this.tooltip;
31001 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
31005 this.el.dom.id = this.el.id = this.id;
31008 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
31009 this.menu.on("show", this.onMenuShow, this);
31010 this.menu.on("hide", this.onMenuHide, this);
31012 btn.addClass("x-btn");
31013 if(Roo.isIE && !Roo.isIE7){
31014 this.autoWidth.defer(1, this);
31018 if(this.handleMouseEvents){
31019 btn.on("mouseover", this.onMouseOver, this);
31020 btn.on("mouseout", this.onMouseOut, this);
31021 btn.on("mousedown", this.onMouseDown, this);
31023 btn.on(this.clickEvent, this.onClick, this);
31024 //btn.on("mouseup", this.onMouseUp, this);
31031 Roo.ButtonToggleMgr.register(this);
31033 this.el.addClass("x-btn-pressed");
31036 var repeater = new Roo.util.ClickRepeater(btn,
31037 typeof this.repeat == "object" ? this.repeat : {}
31039 repeater.on("click", this.onClick, this);
31042 this.fireEvent('render', this);
31046 * Returns the button's underlying element
31047 * @return {Roo.Element} The element
31049 getEl : function(){
31054 * Destroys this Button and removes any listeners.
31056 destroy : function(){
31057 Roo.ButtonToggleMgr.unregister(this);
31058 this.el.removeAllListeners();
31059 this.purgeListeners();
31064 autoWidth : function(){
31066 this.el.setWidth("auto");
31067 if(Roo.isIE7 && Roo.isStrict){
31068 var ib = this.el.child('button');
31069 if(ib && ib.getWidth() > 20){
31071 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31076 this.el.beginMeasure();
31078 if(this.el.getWidth() < this.minWidth){
31079 this.el.setWidth(this.minWidth);
31082 this.el.endMeasure();
31089 * Assigns this button's click handler
31090 * @param {Function} handler The function to call when the button is clicked
31091 * @param {Object} scope (optional) Scope for the function passed in
31093 setHandler : function(handler, scope){
31094 this.handler = handler;
31095 this.scope = scope;
31099 * Sets this button's text
31100 * @param {String} text The button text
31102 setText : function(text){
31105 this.el.child("td.x-btn-center button.x-btn-text").update(text);
31111 * Gets the text for this button
31112 * @return {String} The button text
31114 getText : function(){
31122 this.hidden = false;
31124 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31132 this.hidden = true;
31134 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31139 * Convenience function for boolean show/hide
31140 * @param {Boolean} visible True to show, false to hide
31142 setVisible: function(visible){
31150 * Similar to toggle, but does not trigger event.
31151 * @param {Boolean} state [required] Force a particular state
31153 setPressed : function(state)
31155 if(state != this.pressed){
31157 this.el.addClass("x-btn-pressed");
31158 this.pressed = true;
31160 this.el.removeClass("x-btn-pressed");
31161 this.pressed = false;
31167 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31168 * @param {Boolean} state (optional) Force a particular state
31170 toggle : function(state){
31171 state = state === undefined ? !this.pressed : state;
31172 if(state != this.pressed){
31174 this.el.addClass("x-btn-pressed");
31175 this.pressed = true;
31176 this.fireEvent("toggle", this, true);
31178 this.el.removeClass("x-btn-pressed");
31179 this.pressed = false;
31180 this.fireEvent("toggle", this, false);
31182 if(this.toggleHandler){
31183 this.toggleHandler.call(this.scope || this, this, state);
31193 focus : function(){
31194 this.el.child('button:first').focus();
31198 * Disable this button
31200 disable : function(){
31202 this.el.addClass("x-btn-disabled");
31204 this.disabled = true;
31208 * Enable this button
31210 enable : function(){
31212 this.el.removeClass("x-btn-disabled");
31214 this.disabled = false;
31218 * Convenience function for boolean enable/disable
31219 * @param {Boolean} enabled True to enable, false to disable
31221 setDisabled : function(v){
31222 this[v !== true ? "enable" : "disable"]();
31226 onClick : function(e)
31229 e.preventDefault();
31234 if(!this.disabled){
31235 if(this.enableToggle){
31238 if(this.menu && !this.menu.isVisible()){
31239 this.menu.show(this.el, this.menuAlign);
31241 this.fireEvent("click", this, e);
31243 this.el.removeClass("x-btn-over");
31244 this.handler.call(this.scope || this, this, e);
31249 onMouseOver : function(e){
31250 if(!this.disabled){
31251 this.el.addClass("x-btn-over");
31252 this.fireEvent('mouseover', this, e);
31256 onMouseOut : function(e){
31257 if(!e.within(this.el, true)){
31258 this.el.removeClass("x-btn-over");
31259 this.fireEvent('mouseout', this, e);
31263 onFocus : function(e){
31264 if(!this.disabled){
31265 this.el.addClass("x-btn-focus");
31269 onBlur : function(e){
31270 this.el.removeClass("x-btn-focus");
31273 onMouseDown : function(e){
31274 if(!this.disabled && e.button == 0){
31275 this.el.addClass("x-btn-click");
31276 Roo.get(document).on('mouseup', this.onMouseUp, this);
31280 onMouseUp : function(e){
31282 this.el.removeClass("x-btn-click");
31283 Roo.get(document).un('mouseup', this.onMouseUp, this);
31287 onMenuShow : function(e){
31288 this.el.addClass("x-btn-menu-active");
31291 onMenuHide : function(e){
31292 this.el.removeClass("x-btn-menu-active");
31296 // Private utility class used by Button
31297 Roo.ButtonToggleMgr = function(){
31300 function toggleGroup(btn, state){
31302 var g = groups[btn.toggleGroup];
31303 for(var i = 0, l = g.length; i < l; i++){
31305 g[i].toggle(false);
31312 register : function(btn){
31313 if(!btn.toggleGroup){
31316 var g = groups[btn.toggleGroup];
31318 g = groups[btn.toggleGroup] = [];
31321 btn.on("toggle", toggleGroup);
31324 unregister : function(btn){
31325 if(!btn.toggleGroup){
31328 var g = groups[btn.toggleGroup];
31331 btn.un("toggle", toggleGroup);
31337 * Ext JS Library 1.1.1
31338 * Copyright(c) 2006-2007, Ext JS, LLC.
31340 * Originally Released Under LGPL - original licence link has changed is not relivant.
31343 * <script type="text/javascript">
31347 * @class Roo.SplitButton
31348 * @extends Roo.Button
31349 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31350 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
31351 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31352 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31353 * @cfg {String} arrowTooltip The title attribute of the arrow
31355 * Create a new menu button
31356 * @param {String/HTMLElement/Element} renderTo The element to append the button to
31357 * @param {Object} config The config object
31359 Roo.SplitButton = function(renderTo, config){
31360 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31362 * @event arrowclick
31363 * Fires when this button's arrow is clicked
31364 * @param {SplitButton} this
31365 * @param {EventObject} e The click event
31367 this.addEvents({"arrowclick":true});
31370 Roo.extend(Roo.SplitButton, Roo.Button, {
31371 render : function(renderTo){
31372 // this is one sweet looking template!
31373 var tpl = new Roo.Template(
31374 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31375 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31376 '<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>',
31377 "</tbody></table></td><td>",
31378 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31379 '<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>',
31380 "</tbody></table></td></tr></table>"
31382 var btn = tpl.append(renderTo, [this.text, this.type], true);
31383 var btnEl = btn.child("button");
31385 btn.addClass(this.cls);
31388 btnEl.setStyle('background-image', 'url(' +this.icon +')');
31391 btnEl.addClass(this.iconCls);
31393 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31397 if(this.handleMouseEvents){
31398 btn.on("mouseover", this.onMouseOver, this);
31399 btn.on("mouseout", this.onMouseOut, this);
31400 btn.on("mousedown", this.onMouseDown, this);
31401 btn.on("mouseup", this.onMouseUp, this);
31403 btn.on(this.clickEvent, this.onClick, this);
31405 if(typeof this.tooltip == 'object'){
31406 Roo.QuickTips.tips(Roo.apply({
31410 btnEl.dom[this.tooltipType] = this.tooltip;
31413 if(this.arrowTooltip){
31414 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31423 this.el.addClass("x-btn-pressed");
31425 if(Roo.isIE && !Roo.isIE7){
31426 this.autoWidth.defer(1, this);
31431 this.menu.on("show", this.onMenuShow, this);
31432 this.menu.on("hide", this.onMenuHide, this);
31434 this.fireEvent('render', this);
31438 autoWidth : function(){
31440 var tbl = this.el.child("table:first");
31441 var tbl2 = this.el.child("table:last");
31442 this.el.setWidth("auto");
31443 tbl.setWidth("auto");
31444 if(Roo.isIE7 && Roo.isStrict){
31445 var ib = this.el.child('button:first');
31446 if(ib && ib.getWidth() > 20){
31448 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31453 this.el.beginMeasure();
31455 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31456 tbl.setWidth(this.minWidth-tbl2.getWidth());
31459 this.el.endMeasure();
31462 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31466 * Sets this button's click handler
31467 * @param {Function} handler The function to call when the button is clicked
31468 * @param {Object} scope (optional) Scope for the function passed above
31470 setHandler : function(handler, scope){
31471 this.handler = handler;
31472 this.scope = scope;
31476 * Sets this button's arrow click handler
31477 * @param {Function} handler The function to call when the arrow is clicked
31478 * @param {Object} scope (optional) Scope for the function passed above
31480 setArrowHandler : function(handler, scope){
31481 this.arrowHandler = handler;
31482 this.scope = scope;
31488 focus : function(){
31490 this.el.child("button:first").focus();
31495 onClick : function(e){
31496 e.preventDefault();
31497 if(!this.disabled){
31498 if(e.getTarget(".x-btn-menu-arrow-wrap")){
31499 if(this.menu && !this.menu.isVisible()){
31500 this.menu.show(this.el, this.menuAlign);
31502 this.fireEvent("arrowclick", this, e);
31503 if(this.arrowHandler){
31504 this.arrowHandler.call(this.scope || this, this, e);
31507 this.fireEvent("click", this, e);
31509 this.handler.call(this.scope || this, this, e);
31515 onMouseDown : function(e){
31516 if(!this.disabled){
31517 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31521 onMouseUp : function(e){
31522 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31527 // backwards compat
31528 Roo.MenuButton = Roo.SplitButton;/*
31530 * Ext JS Library 1.1.1
31531 * Copyright(c) 2006-2007, Ext JS, LLC.
31533 * Originally Released Under LGPL - original licence link has changed is not relivant.
31536 * <script type="text/javascript">
31540 * @class Roo.Toolbar
31541 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31542 * Basic Toolbar class.
31544 * Creates a new Toolbar
31545 * @param {Object} container The config object
31547 Roo.Toolbar = function(container, buttons, config)
31549 /// old consturctor format still supported..
31550 if(container instanceof Array){ // omit the container for later rendering
31551 buttons = container;
31555 if (typeof(container) == 'object' && container.xtype) {
31556 config = container;
31557 container = config.container;
31558 buttons = config.buttons || []; // not really - use items!!
31561 if (config && config.items) {
31562 xitems = config.items;
31563 delete config.items;
31565 Roo.apply(this, config);
31566 this.buttons = buttons;
31569 this.render(container);
31571 this.xitems = xitems;
31572 Roo.each(xitems, function(b) {
31578 Roo.Toolbar.prototype = {
31580 * @cfg {Array} items
31581 * array of button configs or elements to add (will be converted to a MixedCollection)
31585 * @cfg {String/HTMLElement/Element} container
31586 * The id or element that will contain the toolbar
31589 render : function(ct){
31590 this.el = Roo.get(ct);
31592 this.el.addClass(this.cls);
31594 // using a table allows for vertical alignment
31595 // 100% width is needed by Safari...
31596 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31597 this.tr = this.el.child("tr", true);
31599 this.items = new Roo.util.MixedCollection(false, function(o){
31600 return o.id || ("item" + (++autoId));
31603 this.add.apply(this, this.buttons);
31604 delete this.buttons;
31609 * Adds element(s) to the toolbar -- this function takes a variable number of
31610 * arguments of mixed type and adds them to the toolbar.
31611 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31613 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31614 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31615 * <li>Field: Any form field (equivalent to {@link #addField})</li>
31616 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31617 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31618 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31619 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31620 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31621 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31623 * @param {Mixed} arg2
31624 * @param {Mixed} etc.
31627 var a = arguments, l = a.length;
31628 for(var i = 0; i < l; i++){
31633 _add : function(el) {
31636 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31639 if (el.applyTo){ // some kind of form field
31640 return this.addField(el);
31642 if (el.render){ // some kind of Toolbar.Item
31643 return this.addItem(el);
31645 if (typeof el == "string"){ // string
31646 if(el == "separator" || el == "-"){
31647 return this.addSeparator();
31650 return this.addSpacer();
31653 return this.addFill();
31655 return this.addText(el);
31658 if(el.tagName){ // element
31659 return this.addElement(el);
31661 if(typeof el == "object"){ // must be button config?
31662 return this.addButton(el);
31664 // and now what?!?!
31670 * Add an Xtype element
31671 * @param {Object} xtype Xtype Object
31672 * @return {Object} created Object
31674 addxtype : function(e){
31675 return this.add(e);
31679 * Returns the Element for this toolbar.
31680 * @return {Roo.Element}
31682 getEl : function(){
31688 * @return {Roo.Toolbar.Item} The separator item
31690 addSeparator : function(){
31691 return this.addItem(new Roo.Toolbar.Separator());
31695 * Adds a spacer element
31696 * @return {Roo.Toolbar.Spacer} The spacer item
31698 addSpacer : function(){
31699 return this.addItem(new Roo.Toolbar.Spacer());
31703 * Adds a fill element that forces subsequent additions to the right side of the toolbar
31704 * @return {Roo.Toolbar.Fill} The fill item
31706 addFill : function(){
31707 return this.addItem(new Roo.Toolbar.Fill());
31711 * Adds any standard HTML element to the toolbar
31712 * @param {String/HTMLElement/Element} el The element or id of the element to add
31713 * @return {Roo.Toolbar.Item} The element's item
31715 addElement : function(el){
31716 return this.addItem(new Roo.Toolbar.Item(el));
31719 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31720 * @type Roo.util.MixedCollection
31725 * Adds any Toolbar.Item or subclass
31726 * @param {Roo.Toolbar.Item} item
31727 * @return {Roo.Toolbar.Item} The item
31729 addItem : function(item){
31730 var td = this.nextBlock();
31732 this.items.add(item);
31737 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31738 * @param {Object/Array} config A button config or array of configs
31739 * @return {Roo.Toolbar.Button/Array}
31741 addButton : function(config){
31742 if(config instanceof Array){
31744 for(var i = 0, len = config.length; i < len; i++) {
31745 buttons.push(this.addButton(config[i]));
31750 if(!(config instanceof Roo.Toolbar.Button)){
31752 new Roo.Toolbar.SplitButton(config) :
31753 new Roo.Toolbar.Button(config);
31755 var td = this.nextBlock();
31762 * Adds text to the toolbar
31763 * @param {String} text The text to add
31764 * @return {Roo.Toolbar.Item} The element's item
31766 addText : function(text){
31767 return this.addItem(new Roo.Toolbar.TextItem(text));
31771 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31772 * @param {Number} index The index where the item is to be inserted
31773 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31774 * @return {Roo.Toolbar.Button/Item}
31776 insertButton : function(index, item){
31777 if(item instanceof Array){
31779 for(var i = 0, len = item.length; i < len; i++) {
31780 buttons.push(this.insertButton(index + i, item[i]));
31784 if (!(item instanceof Roo.Toolbar.Button)){
31785 item = new Roo.Toolbar.Button(item);
31787 var td = document.createElement("td");
31788 this.tr.insertBefore(td, this.tr.childNodes[index]);
31790 this.items.insert(index, item);
31795 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31796 * @param {Object} config
31797 * @return {Roo.Toolbar.Item} The element's item
31799 addDom : function(config, returnEl){
31800 var td = this.nextBlock();
31801 Roo.DomHelper.overwrite(td, config);
31802 var ti = new Roo.Toolbar.Item(td.firstChild);
31804 this.items.add(ti);
31809 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31810 * @type Roo.util.MixedCollection
31815 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31816 * Note: the field should not have been rendered yet. For a field that has already been
31817 * rendered, use {@link #addElement}.
31818 * @param {Roo.form.Field} field
31819 * @return {Roo.ToolbarItem}
31823 addField : function(field) {
31824 if (!this.fields) {
31826 this.fields = new Roo.util.MixedCollection(false, function(o){
31827 return o.id || ("item" + (++autoId));
31832 var td = this.nextBlock();
31834 var ti = new Roo.Toolbar.Item(td.firstChild);
31836 this.items.add(ti);
31837 this.fields.add(field);
31848 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31849 this.el.child('div').hide();
31857 this.el.child('div').show();
31861 nextBlock : function(){
31862 var td = document.createElement("td");
31863 this.tr.appendChild(td);
31868 destroy : function(){
31869 if(this.items){ // rendered?
31870 Roo.destroy.apply(Roo, this.items.items);
31872 if(this.fields){ // rendered?
31873 Roo.destroy.apply(Roo, this.fields.items);
31875 Roo.Element.uncache(this.el, this.tr);
31880 * @class Roo.Toolbar.Item
31881 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31883 * Creates a new Item
31884 * @param {HTMLElement} el
31886 Roo.Toolbar.Item = function(el){
31888 if (typeof (el.xtype) != 'undefined') {
31893 this.el = Roo.getDom(el);
31894 this.id = Roo.id(this.el);
31895 this.hidden = false;
31900 * Fires when the button is rendered
31901 * @param {Button} this
31905 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31907 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31908 //Roo.Toolbar.Item.prototype = {
31911 * Get this item's HTML Element
31912 * @return {HTMLElement}
31914 getEl : function(){
31919 render : function(td){
31922 td.appendChild(this.el);
31924 this.fireEvent('render', this);
31928 * Removes and destroys this item.
31930 destroy : function(){
31931 this.td.parentNode.removeChild(this.td);
31938 this.hidden = false;
31939 this.td.style.display = "";
31946 this.hidden = true;
31947 this.td.style.display = "none";
31951 * Convenience function for boolean show/hide.
31952 * @param {Boolean} visible true to show/false to hide
31954 setVisible: function(visible){
31963 * Try to focus this item.
31965 focus : function(){
31966 Roo.fly(this.el).focus();
31970 * Disables this item.
31972 disable : function(){
31973 Roo.fly(this.td).addClass("x-item-disabled");
31974 this.disabled = true;
31975 this.el.disabled = true;
31979 * Enables this item.
31981 enable : function(){
31982 Roo.fly(this.td).removeClass("x-item-disabled");
31983 this.disabled = false;
31984 this.el.disabled = false;
31990 * @class Roo.Toolbar.Separator
31991 * @extends Roo.Toolbar.Item
31992 * A simple toolbar separator class
31994 * Creates a new Separator
31996 Roo.Toolbar.Separator = function(cfg){
31998 var s = document.createElement("span");
31999 s.className = "ytb-sep";
32004 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
32006 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
32007 enable:Roo.emptyFn,
32008 disable:Roo.emptyFn,
32013 * @class Roo.Toolbar.Spacer
32014 * @extends Roo.Toolbar.Item
32015 * A simple element that adds extra horizontal space to a toolbar.
32017 * Creates a new Spacer
32019 Roo.Toolbar.Spacer = function(cfg){
32020 var s = document.createElement("div");
32021 s.className = "ytb-spacer";
32025 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
32027 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
32028 enable:Roo.emptyFn,
32029 disable:Roo.emptyFn,
32034 * @class Roo.Toolbar.Fill
32035 * @extends Roo.Toolbar.Spacer
32036 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32038 * Creates a new Spacer
32040 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32042 render : function(td){
32043 td.style.width = '100%';
32044 Roo.Toolbar.Fill.superclass.render.call(this, td);
32049 * @class Roo.Toolbar.TextItem
32050 * @extends Roo.Toolbar.Item
32051 * A simple class that renders text directly into a toolbar.
32053 * Creates a new TextItem
32054 * @cfg {string} text
32056 Roo.Toolbar.TextItem = function(cfg){
32057 var text = cfg || "";
32058 if (typeof(cfg) == 'object') {
32059 text = cfg.text || "";
32063 var s = document.createElement("span");
32064 s.className = "ytb-text";
32065 s.innerHTML = text;
32070 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
32072 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32075 enable:Roo.emptyFn,
32076 disable:Roo.emptyFn,
32079 * Shows this button
32082 this.hidden = false;
32083 this.el.style.display = "";
32087 * Hides this button
32090 this.hidden = true;
32091 this.el.style.display = "none";
32097 * @class Roo.Toolbar.Button
32098 * @extends Roo.Button
32099 * A button that renders into a toolbar.
32101 * Creates a new Button
32102 * @param {Object} config A standard {@link Roo.Button} config object
32104 Roo.Toolbar.Button = function(config){
32105 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32107 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32111 render : function(td){
32113 Roo.Toolbar.Button.superclass.render.call(this, td);
32117 * Removes and destroys this button
32119 destroy : function(){
32120 Roo.Toolbar.Button.superclass.destroy.call(this);
32121 this.td.parentNode.removeChild(this.td);
32125 * Shows this button
32128 this.hidden = false;
32129 this.td.style.display = "";
32133 * Hides this button
32136 this.hidden = true;
32137 this.td.style.display = "none";
32141 * Disables this item
32143 disable : function(){
32144 Roo.fly(this.td).addClass("x-item-disabled");
32145 this.disabled = true;
32149 * Enables this item
32151 enable : function(){
32152 Roo.fly(this.td).removeClass("x-item-disabled");
32153 this.disabled = false;
32156 // backwards compat
32157 Roo.ToolbarButton = Roo.Toolbar.Button;
32160 * @class Roo.Toolbar.SplitButton
32161 * @extends Roo.SplitButton
32162 * A menu button that renders into a toolbar.
32164 * Creates a new SplitButton
32165 * @param {Object} config A standard {@link Roo.SplitButton} config object
32167 Roo.Toolbar.SplitButton = function(config){
32168 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32170 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32171 render : function(td){
32173 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32177 * Removes and destroys this button
32179 destroy : function(){
32180 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32181 this.td.parentNode.removeChild(this.td);
32185 * Shows this button
32188 this.hidden = false;
32189 this.td.style.display = "";
32193 * Hides this button
32196 this.hidden = true;
32197 this.td.style.display = "none";
32201 // backwards compat
32202 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32204 * Ext JS Library 1.1.1
32205 * Copyright(c) 2006-2007, Ext JS, LLC.
32207 * Originally Released Under LGPL - original licence link has changed is not relivant.
32210 * <script type="text/javascript">
32214 * @class Roo.PagingToolbar
32215 * @extends Roo.Toolbar
32216 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32217 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32219 * Create a new PagingToolbar
32220 * @param {Object} config The config object
32222 Roo.PagingToolbar = function(el, ds, config)
32224 // old args format still supported... - xtype is prefered..
32225 if (typeof(el) == 'object' && el.xtype) {
32226 // created from xtype...
32228 ds = el.dataSource;
32229 el = config.container;
32232 if (config.items) {
32233 items = config.items;
32237 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32240 this.renderButtons(this.el);
32243 // supprot items array.
32245 Roo.each(items, function(e) {
32246 this.add(Roo.factory(e));
32251 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32254 * @cfg {String/HTMLElement/Element} container
32255 * container The id or element that will contain the toolbar
32258 * @cfg {Boolean} displayInfo
32259 * True to display the displayMsg (defaults to false)
32264 * @cfg {Number} pageSize
32265 * The number of records to display per page (defaults to 20)
32269 * @cfg {String} displayMsg
32270 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32272 displayMsg : 'Displaying {0} - {1} of {2}',
32274 * @cfg {String} emptyMsg
32275 * The message to display when no records are found (defaults to "No data to display")
32277 emptyMsg : 'No data to display',
32279 * Customizable piece of the default paging text (defaults to "Page")
32282 beforePageText : "Page",
32284 * Customizable piece of the default paging text (defaults to "of %0")
32287 afterPageText : "of {0}",
32289 * Customizable piece of the default paging text (defaults to "First Page")
32292 firstText : "First Page",
32294 * Customizable piece of the default paging text (defaults to "Previous Page")
32297 prevText : "Previous Page",
32299 * Customizable piece of the default paging text (defaults to "Next Page")
32302 nextText : "Next Page",
32304 * Customizable piece of the default paging text (defaults to "Last Page")
32307 lastText : "Last Page",
32309 * Customizable piece of the default paging text (defaults to "Refresh")
32312 refreshText : "Refresh",
32315 renderButtons : function(el){
32316 Roo.PagingToolbar.superclass.render.call(this, el);
32317 this.first = this.addButton({
32318 tooltip: this.firstText,
32319 cls: "x-btn-icon x-grid-page-first",
32321 handler: this.onClick.createDelegate(this, ["first"])
32323 this.prev = this.addButton({
32324 tooltip: this.prevText,
32325 cls: "x-btn-icon x-grid-page-prev",
32327 handler: this.onClick.createDelegate(this, ["prev"])
32329 //this.addSeparator();
32330 this.add(this.beforePageText);
32331 this.field = Roo.get(this.addDom({
32336 cls: "x-grid-page-number"
32338 this.field.on("keydown", this.onPagingKeydown, this);
32339 this.field.on("focus", function(){this.dom.select();});
32340 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32341 this.field.setHeight(18);
32342 //this.addSeparator();
32343 this.next = this.addButton({
32344 tooltip: this.nextText,
32345 cls: "x-btn-icon x-grid-page-next",
32347 handler: this.onClick.createDelegate(this, ["next"])
32349 this.last = this.addButton({
32350 tooltip: this.lastText,
32351 cls: "x-btn-icon x-grid-page-last",
32353 handler: this.onClick.createDelegate(this, ["last"])
32355 //this.addSeparator();
32356 this.loading = this.addButton({
32357 tooltip: this.refreshText,
32358 cls: "x-btn-icon x-grid-loading",
32359 handler: this.onClick.createDelegate(this, ["refresh"])
32362 if(this.displayInfo){
32363 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32368 updateInfo : function(){
32369 if(this.displayEl){
32370 var count = this.ds.getCount();
32371 var msg = count == 0 ?
32375 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32377 this.displayEl.update(msg);
32382 onLoad : function(ds, r, o){
32383 this.cursor = o.params ? o.params.start : 0;
32384 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32386 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32387 this.field.dom.value = ap;
32388 this.first.setDisabled(ap == 1);
32389 this.prev.setDisabled(ap == 1);
32390 this.next.setDisabled(ap == ps);
32391 this.last.setDisabled(ap == ps);
32392 this.loading.enable();
32397 getPageData : function(){
32398 var total = this.ds.getTotalCount();
32401 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32402 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32407 onLoadError : function(){
32408 this.loading.enable();
32412 onPagingKeydown : function(e){
32413 var k = e.getKey();
32414 var d = this.getPageData();
32416 var v = this.field.dom.value, pageNum;
32417 if(!v || isNaN(pageNum = parseInt(v, 10))){
32418 this.field.dom.value = d.activePage;
32421 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32422 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32425 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))
32427 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32428 this.field.dom.value = pageNum;
32429 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32432 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32434 var v = this.field.dom.value, pageNum;
32435 var increment = (e.shiftKey) ? 10 : 1;
32436 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32439 if(!v || isNaN(pageNum = parseInt(v, 10))) {
32440 this.field.dom.value = d.activePage;
32443 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32445 this.field.dom.value = parseInt(v, 10) + increment;
32446 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32447 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32454 beforeLoad : function(){
32456 this.loading.disable();
32460 * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32461 * @param {String} which (first|prev|next|last|refresh) which button to press.
32465 onClick : function(which){
32469 ds.load({params:{start: 0, limit: this.pageSize}});
32472 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32475 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32478 var total = ds.getTotalCount();
32479 var extra = total % this.pageSize;
32480 var lastStart = extra ? (total - extra) : total-this.pageSize;
32481 ds.load({params:{start: lastStart, limit: this.pageSize}});
32484 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32490 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32491 * @param {Roo.data.Store} store The data store to unbind
32493 unbind : function(ds){
32494 ds.un("beforeload", this.beforeLoad, this);
32495 ds.un("load", this.onLoad, this);
32496 ds.un("loadexception", this.onLoadError, this);
32497 ds.un("remove", this.updateInfo, this);
32498 ds.un("add", this.updateInfo, this);
32499 this.ds = undefined;
32503 * Binds the paging toolbar to the specified {@link Roo.data.Store}
32504 * @param {Roo.data.Store} store The data store to bind
32506 bind : function(ds){
32507 ds.on("beforeload", this.beforeLoad, this);
32508 ds.on("load", this.onLoad, this);
32509 ds.on("loadexception", this.onLoadError, this);
32510 ds.on("remove", this.updateInfo, this);
32511 ds.on("add", this.updateInfo, this);
32516 * Ext JS Library 1.1.1
32517 * Copyright(c) 2006-2007, Ext JS, LLC.
32519 * Originally Released Under LGPL - original licence link has changed is not relivant.
32522 * <script type="text/javascript">
32526 * @class Roo.Resizable
32527 * @extends Roo.util.Observable
32528 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32529 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32530 * 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
32531 * the element will be wrapped for you automatically.</p>
32532 * <p>Here is the list of valid resize handles:</p>
32535 ------ -------------------
32544 'hd' horizontal drag
32547 * <p>Here's an example showing the creation of a typical Resizable:</p>
32549 var resizer = new Roo.Resizable("element-id", {
32557 resizer.on("resize", myHandler);
32559 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32560 * resizer.east.setDisplayed(false);</p>
32561 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32562 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32563 * resize operation's new size (defaults to [0, 0])
32564 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32565 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32566 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32567 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32568 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32569 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32570 * @cfg {Number} width The width of the element in pixels (defaults to null)
32571 * @cfg {Number} height The height of the element in pixels (defaults to null)
32572 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32573 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32574 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32575 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32576 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
32577 * in favor of the handles config option (defaults to false)
32578 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32579 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32580 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32581 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32582 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32583 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32584 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32585 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32586 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32587 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32588 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32590 * Create a new resizable component
32591 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32592 * @param {Object} config configuration options
32594 Roo.Resizable = function(el, config)
32596 this.el = Roo.get(el);
32598 if(config && config.wrap){
32599 config.resizeChild = this.el;
32600 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32601 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32602 this.el.setStyle("overflow", "hidden");
32603 this.el.setPositioning(config.resizeChild.getPositioning());
32604 config.resizeChild.clearPositioning();
32605 if(!config.width || !config.height){
32606 var csize = config.resizeChild.getSize();
32607 this.el.setSize(csize.width, csize.height);
32609 if(config.pinned && !config.adjustments){
32610 config.adjustments = "auto";
32614 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32615 this.proxy.unselectable();
32616 this.proxy.enableDisplayMode('block');
32618 Roo.apply(this, config);
32621 this.disableTrackOver = true;
32622 this.el.addClass("x-resizable-pinned");
32624 // if the element isn't positioned, make it relative
32625 var position = this.el.getStyle("position");
32626 if(position != "absolute" && position != "fixed"){
32627 this.el.setStyle("position", "relative");
32629 if(!this.handles){ // no handles passed, must be legacy style
32630 this.handles = 's,e,se';
32631 if(this.multiDirectional){
32632 this.handles += ',n,w';
32635 if(this.handles == "all"){
32636 this.handles = "n s e w ne nw se sw";
32638 var hs = this.handles.split(/\s*?[,;]\s*?| /);
32639 var ps = Roo.Resizable.positions;
32640 for(var i = 0, len = hs.length; i < len; i++){
32641 if(hs[i] && ps[hs[i]]){
32642 var pos = ps[hs[i]];
32643 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32647 this.corner = this.southeast;
32649 // updateBox = the box can move..
32650 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32651 this.updateBox = true;
32654 this.activeHandle = null;
32656 if(this.resizeChild){
32657 if(typeof this.resizeChild == "boolean"){
32658 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32660 this.resizeChild = Roo.get(this.resizeChild, true);
32664 if(this.adjustments == "auto"){
32665 var rc = this.resizeChild;
32666 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32667 if(rc && (hw || hn)){
32668 rc.position("relative");
32669 rc.setLeft(hw ? hw.el.getWidth() : 0);
32670 rc.setTop(hn ? hn.el.getHeight() : 0);
32672 this.adjustments = [
32673 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32674 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32678 if(this.draggable){
32679 this.dd = this.dynamic ?
32680 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32681 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32687 * @event beforeresize
32688 * Fired before resize is allowed. Set enabled to false to cancel resize.
32689 * @param {Roo.Resizable} this
32690 * @param {Roo.EventObject} e The mousedown event
32692 "beforeresize" : true,
32695 * Fired a resizing.
32696 * @param {Roo.Resizable} this
32697 * @param {Number} x The new x position
32698 * @param {Number} y The new y position
32699 * @param {Number} w The new w width
32700 * @param {Number} h The new h hight
32701 * @param {Roo.EventObject} e The mouseup event
32706 * Fired after a resize.
32707 * @param {Roo.Resizable} this
32708 * @param {Number} width The new width
32709 * @param {Number} height The new height
32710 * @param {Roo.EventObject} e The mouseup event
32715 if(this.width !== null && this.height !== null){
32716 this.resizeTo(this.width, this.height);
32718 this.updateChildSize();
32721 this.el.dom.style.zoom = 1;
32723 Roo.Resizable.superclass.constructor.call(this);
32726 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32727 resizeChild : false,
32728 adjustments : [0, 0],
32738 multiDirectional : false,
32739 disableTrackOver : false,
32740 easing : 'easeOutStrong',
32741 widthIncrement : 0,
32742 heightIncrement : 0,
32746 preserveRatio : false,
32747 transparent: false,
32753 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32755 constrainTo: undefined,
32757 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32759 resizeRegion: undefined,
32763 * Perform a manual resize
32764 * @param {Number} width
32765 * @param {Number} height
32767 resizeTo : function(width, height){
32768 this.el.setSize(width, height);
32769 this.updateChildSize();
32770 this.fireEvent("resize", this, width, height, null);
32774 startSizing : function(e, handle){
32775 this.fireEvent("beforeresize", this, e);
32776 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32779 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
32780 this.overlay.unselectable();
32781 this.overlay.enableDisplayMode("block");
32782 this.overlay.on("mousemove", this.onMouseMove, this);
32783 this.overlay.on("mouseup", this.onMouseUp, this);
32785 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32787 this.resizing = true;
32788 this.startBox = this.el.getBox();
32789 this.startPoint = e.getXY();
32790 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32791 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32793 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32794 this.overlay.show();
32796 if(this.constrainTo) {
32797 var ct = Roo.get(this.constrainTo);
32798 this.resizeRegion = ct.getRegion().adjust(
32799 ct.getFrameWidth('t'),
32800 ct.getFrameWidth('l'),
32801 -ct.getFrameWidth('b'),
32802 -ct.getFrameWidth('r')
32806 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32808 this.proxy.setBox(this.startBox);
32810 this.proxy.setStyle('visibility', 'visible');
32816 onMouseDown : function(handle, e){
32819 this.activeHandle = handle;
32820 this.startSizing(e, handle);
32825 onMouseUp : function(e){
32826 var size = this.resizeElement();
32827 this.resizing = false;
32829 this.overlay.hide();
32831 this.fireEvent("resize", this, size.width, size.height, e);
32835 updateChildSize : function(){
32837 if(this.resizeChild){
32839 var child = this.resizeChild;
32840 var adj = this.adjustments;
32841 if(el.dom.offsetWidth){
32842 var b = el.getSize(true);
32843 child.setSize(b.width+adj[0], b.height+adj[1]);
32845 // Second call here for IE
32846 // The first call enables instant resizing and
32847 // the second call corrects scroll bars if they
32850 setTimeout(function(){
32851 if(el.dom.offsetWidth){
32852 var b = el.getSize(true);
32853 child.setSize(b.width+adj[0], b.height+adj[1]);
32861 snap : function(value, inc, min){
32862 if(!inc || !value) {
32865 var newValue = value;
32866 var m = value % inc;
32869 newValue = value + (inc-m);
32871 newValue = value - m;
32874 return Math.max(min, newValue);
32878 resizeElement : function(){
32879 var box = this.proxy.getBox();
32880 if(this.updateBox){
32881 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32883 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32885 this.updateChildSize();
32893 constrain : function(v, diff, m, mx){
32896 }else if(v - diff > mx){
32903 onMouseMove : function(e){
32906 try{// try catch so if something goes wrong the user doesn't get hung
32908 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32912 //var curXY = this.startPoint;
32913 var curSize = this.curSize || this.startBox;
32914 var x = this.startBox.x, y = this.startBox.y;
32915 var ox = x, oy = y;
32916 var w = curSize.width, h = curSize.height;
32917 var ow = w, oh = h;
32918 var mw = this.minWidth, mh = this.minHeight;
32919 var mxw = this.maxWidth, mxh = this.maxHeight;
32920 var wi = this.widthIncrement;
32921 var hi = this.heightIncrement;
32923 var eventXY = e.getXY();
32924 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32925 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32927 var pos = this.activeHandle.position;
32932 w = Math.min(Math.max(mw, w), mxw);
32937 h = Math.min(Math.max(mh, h), mxh);
32942 w = Math.min(Math.max(mw, w), mxw);
32943 h = Math.min(Math.max(mh, h), mxh);
32946 diffY = this.constrain(h, diffY, mh, mxh);
32953 var adiffX = Math.abs(diffX);
32954 var sub = (adiffX % wi); // how much
32955 if (sub > (wi/2)) { // far enough to snap
32956 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32958 // remove difference..
32959 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32963 x = Math.max(this.minX, x);
32966 diffX = this.constrain(w, diffX, mw, mxw);
32972 w = Math.min(Math.max(mw, w), mxw);
32973 diffY = this.constrain(h, diffY, mh, mxh);
32978 diffX = this.constrain(w, diffX, mw, mxw);
32979 diffY = this.constrain(h, diffY, mh, mxh);
32986 diffX = this.constrain(w, diffX, mw, mxw);
32988 h = Math.min(Math.max(mh, h), mxh);
32994 var sw = this.snap(w, wi, mw);
32995 var sh = this.snap(h, hi, mh);
32996 if(sw != w || sh != h){
33019 if(this.preserveRatio){
33024 h = Math.min(Math.max(mh, h), mxh);
33029 w = Math.min(Math.max(mw, w), mxw);
33034 w = Math.min(Math.max(mw, w), mxw);
33040 w = Math.min(Math.max(mw, w), mxw);
33046 h = Math.min(Math.max(mh, h), mxh);
33054 h = Math.min(Math.max(mh, h), mxh);
33064 h = Math.min(Math.max(mh, h), mxh);
33072 if (pos == 'hdrag') {
33075 this.proxy.setBounds(x, y, w, h);
33077 this.resizeElement();
33081 this.fireEvent("resizing", this, x, y, w, h, e);
33085 handleOver : function(){
33087 this.el.addClass("x-resizable-over");
33092 handleOut : function(){
33093 if(!this.resizing){
33094 this.el.removeClass("x-resizable-over");
33099 * Returns the element this component is bound to.
33100 * @return {Roo.Element}
33102 getEl : function(){
33107 * Returns the resizeChild element (or null).
33108 * @return {Roo.Element}
33110 getResizeChild : function(){
33111 return this.resizeChild;
33113 groupHandler : function()
33118 * Destroys this resizable. If the element was wrapped and
33119 * removeEl is not true then the element remains.
33120 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33122 destroy : function(removeEl){
33123 this.proxy.remove();
33125 this.overlay.removeAllListeners();
33126 this.overlay.remove();
33128 var ps = Roo.Resizable.positions;
33130 if(typeof ps[k] != "function" && this[ps[k]]){
33131 var h = this[ps[k]];
33132 h.el.removeAllListeners();
33137 this.el.update("");
33144 // hash to map config positions to true positions
33145 Roo.Resizable.positions = {
33146 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
33151 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33153 // only initialize the template if resizable is used
33154 var tpl = Roo.DomHelper.createTemplate(
33155 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33158 Roo.Resizable.Handle.prototype.tpl = tpl;
33160 this.position = pos;
33162 // show north drag fro topdra
33163 var handlepos = pos == 'hdrag' ? 'north' : pos;
33165 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33166 if (pos == 'hdrag') {
33167 this.el.setStyle('cursor', 'pointer');
33169 this.el.unselectable();
33171 this.el.setOpacity(0);
33173 this.el.on("mousedown", this.onMouseDown, this);
33174 if(!disableTrackOver){
33175 this.el.on("mouseover", this.onMouseOver, this);
33176 this.el.on("mouseout", this.onMouseOut, this);
33181 Roo.Resizable.Handle.prototype = {
33182 afterResize : function(rz){
33187 onMouseDown : function(e){
33188 this.rz.onMouseDown(this, e);
33191 onMouseOver : function(e){
33192 this.rz.handleOver(this, e);
33195 onMouseOut : function(e){
33196 this.rz.handleOut(this, e);
33200 * Ext JS Library 1.1.1
33201 * Copyright(c) 2006-2007, Ext JS, LLC.
33203 * Originally Released Under LGPL - original licence link has changed is not relivant.
33206 * <script type="text/javascript">
33210 * @class Roo.Editor
33211 * @extends Roo.Component
33212 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33214 * Create a new Editor
33215 * @param {Roo.form.Field} field The Field object (or descendant)
33216 * @param {Object} config The config object
33218 Roo.Editor = function(field, config){
33219 Roo.Editor.superclass.constructor.call(this, config);
33220 this.field = field;
33223 * @event beforestartedit
33224 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
33225 * false from the handler of this event.
33226 * @param {Editor} this
33227 * @param {Roo.Element} boundEl The underlying element bound to this editor
33228 * @param {Mixed} value The field value being set
33230 "beforestartedit" : true,
33233 * Fires when this editor is displayed
33234 * @param {Roo.Element} boundEl The underlying element bound to this editor
33235 * @param {Mixed} value The starting field value
33237 "startedit" : true,
33239 * @event beforecomplete
33240 * Fires after a change has been made to the field, but before the change is reflected in the underlying
33241 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
33242 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33243 * event will not fire since no edit actually occurred.
33244 * @param {Editor} this
33245 * @param {Mixed} value The current field value
33246 * @param {Mixed} startValue The original field value
33248 "beforecomplete" : true,
33251 * Fires after editing is complete and any changed value has been written to the underlying field.
33252 * @param {Editor} this
33253 * @param {Mixed} value The current field value
33254 * @param {Mixed} startValue The original field value
33258 * @event specialkey
33259 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
33260 * {@link Roo.EventObject#getKey} to determine which key was pressed.
33261 * @param {Roo.form.Field} this
33262 * @param {Roo.EventObject} e The event object
33264 "specialkey" : true
33268 Roo.extend(Roo.Editor, Roo.Component, {
33270 * @cfg {Boolean/String} autosize
33271 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33272 * or "height" to adopt the height only (defaults to false)
33275 * @cfg {Boolean} revertInvalid
33276 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33277 * validation fails (defaults to true)
33280 * @cfg {Boolean} ignoreNoChange
33281 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33282 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
33283 * will never be ignored.
33286 * @cfg {Boolean} hideEl
33287 * False to keep the bound element visible while the editor is displayed (defaults to true)
33290 * @cfg {Mixed} value
33291 * The data value of the underlying field (defaults to "")
33295 * @cfg {String} alignment
33296 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33300 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33301 * for bottom-right shadow (defaults to "frame")
33305 * @cfg {Boolean} constrain True to constrain the editor to the viewport
33309 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33311 completeOnEnter : false,
33313 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33315 cancelOnEsc : false,
33317 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33322 onRender : function(ct, position){
33323 this.el = new Roo.Layer({
33324 shadow: this.shadow,
33330 constrain: this.constrain
33332 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33333 if(this.field.msgTarget != 'title'){
33334 this.field.msgTarget = 'qtip';
33336 this.field.render(this.el);
33338 this.field.el.dom.setAttribute('autocomplete', 'off');
33340 this.field.on("specialkey", this.onSpecialKey, this);
33341 if(this.swallowKeys){
33342 this.field.el.swallowEvent(['keydown','keypress']);
33345 this.field.on("blur", this.onBlur, this);
33346 if(this.field.grow){
33347 this.field.on("autosize", this.el.sync, this.el, {delay:1});
33351 onSpecialKey : function(field, e)
33353 //Roo.log('editor onSpecialKey');
33354 if(this.completeOnEnter && e.getKey() == e.ENTER){
33356 this.completeEdit();
33359 // do not fire special key otherwise it might hide close the editor...
33360 if(e.getKey() == e.ENTER){
33363 if(this.cancelOnEsc && e.getKey() == e.ESC){
33367 this.fireEvent('specialkey', field, e);
33372 * Starts the editing process and shows the editor.
33373 * @param {String/HTMLElement/Element} el The element to edit
33374 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33375 * to the innerHTML of el.
33377 startEdit : function(el, value){
33379 this.completeEdit();
33381 this.boundEl = Roo.get(el);
33382 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33383 if(!this.rendered){
33384 this.render(this.parentEl || document.body);
33386 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33389 this.startValue = v;
33390 this.field.setValue(v);
33392 var sz = this.boundEl.getSize();
33393 switch(this.autoSize){
33395 this.setSize(sz.width, "");
33398 this.setSize("", sz.height);
33401 this.setSize(sz.width, sz.height);
33404 this.el.alignTo(this.boundEl, this.alignment);
33405 this.editing = true;
33407 Roo.QuickTips.disable();
33413 * Sets the height and width of this editor.
33414 * @param {Number} width The new width
33415 * @param {Number} height The new height
33417 setSize : function(w, h){
33418 this.field.setSize(w, h);
33425 * Realigns the editor to the bound field based on the current alignment config value.
33427 realign : function(){
33428 this.el.alignTo(this.boundEl, this.alignment);
33432 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33433 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33435 completeEdit : function(remainVisible){
33439 var v = this.getValue();
33440 if(this.revertInvalid !== false && !this.field.isValid()){
33441 v = this.startValue;
33442 this.cancelEdit(true);
33444 if(String(v) === String(this.startValue) && this.ignoreNoChange){
33445 this.editing = false;
33449 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33450 this.editing = false;
33451 if(this.updateEl && this.boundEl){
33452 this.boundEl.update(v);
33454 if(remainVisible !== true){
33457 this.fireEvent("complete", this, v, this.startValue);
33462 onShow : function(){
33464 if(this.hideEl !== false){
33465 this.boundEl.hide();
33468 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33469 this.fixIEFocus = true;
33470 this.deferredFocus.defer(50, this);
33472 this.field.focus();
33474 this.fireEvent("startedit", this.boundEl, this.startValue);
33477 deferredFocus : function(){
33479 this.field.focus();
33484 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
33485 * reverted to the original starting value.
33486 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33487 * cancel (defaults to false)
33489 cancelEdit : function(remainVisible){
33491 this.setValue(this.startValue);
33492 if(remainVisible !== true){
33499 onBlur : function(){
33500 if(this.allowBlur !== true && this.editing){
33501 this.completeEdit();
33506 onHide : function(){
33508 this.completeEdit();
33512 if(this.field.collapse){
33513 this.field.collapse();
33516 if(this.hideEl !== false){
33517 this.boundEl.show();
33520 Roo.QuickTips.enable();
33525 * Sets the data value of the editor
33526 * @param {Mixed} value Any valid value supported by the underlying field
33528 setValue : function(v){
33529 this.field.setValue(v);
33533 * Gets the data value of the editor
33534 * @return {Mixed} The data value
33536 getValue : function(){
33537 return this.field.getValue();
33541 * Ext JS Library 1.1.1
33542 * Copyright(c) 2006-2007, Ext JS, LLC.
33544 * Originally Released Under LGPL - original licence link has changed is not relivant.
33547 * <script type="text/javascript">
33551 * @class Roo.BasicDialog
33552 * @extends Roo.util.Observable
33553 * @parent none builder
33554 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
33556 var dlg = new Roo.BasicDialog("my-dlg", {
33565 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33566 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
33567 dlg.addButton('Cancel', dlg.hide, dlg);
33570 <b>A Dialog should always be a direct child of the body element.</b>
33571 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33572 * @cfg {String} title Default text to display in the title bar (defaults to null)
33573 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33574 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
33575 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33576 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33577 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33578 * (defaults to null with no animation)
33579 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33580 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33581 * property for valid values (defaults to 'all')
33582 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33583 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33584 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33585 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33586 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33587 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33588 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33589 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33590 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33591 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33592 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33593 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33594 * draggable = true (defaults to false)
33595 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33596 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33597 * shadow (defaults to false)
33598 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33599 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33600 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33601 * @cfg {Array} buttons Array of buttons
33602 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33604 * Create a new BasicDialog.
33605 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33606 * @param {Object} config Configuration options
33608 Roo.BasicDialog = function(el, config){
33609 this.el = Roo.get(el);
33610 var dh = Roo.DomHelper;
33611 if(!this.el && config && config.autoCreate){
33612 if(typeof config.autoCreate == "object"){
33613 if(!config.autoCreate.id){
33614 config.autoCreate.id = el;
33616 this.el = dh.append(document.body,
33617 config.autoCreate, true);
33619 this.el = dh.append(document.body,
33620 {tag: "div", id: el, style:'visibility:hidden;'}, true);
33624 el.setDisplayed(true);
33625 el.hide = this.hideAction;
33627 el.addClass("x-dlg");
33629 Roo.apply(this, config);
33631 this.proxy = el.createProxy("x-dlg-proxy");
33632 this.proxy.hide = this.hideAction;
33633 this.proxy.setOpacity(.5);
33637 el.setWidth(config.width);
33640 el.setHeight(config.height);
33642 this.size = el.getSize();
33643 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33644 this.xy = [config.x,config.y];
33646 this.xy = el.getCenterXY(true);
33648 /** The header element @type Roo.Element */
33649 this.header = el.child("> .x-dlg-hd");
33650 /** The body element @type Roo.Element */
33651 this.body = el.child("> .x-dlg-bd");
33652 /** The footer element @type Roo.Element */
33653 this.footer = el.child("> .x-dlg-ft");
33656 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
33659 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33662 this.header.unselectable();
33664 this.header.update(this.title);
33666 // this element allows the dialog to be focused for keyboard event
33667 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33668 this.focusEl.swallowEvent("click", true);
33670 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33672 // wrap the body and footer for special rendering
33673 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33675 this.bwrap.dom.appendChild(this.footer.dom);
33678 this.bg = this.el.createChild({
33679 tag: "div", cls:"x-dlg-bg",
33680 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
33682 this.centerBg = this.bg.child("div.x-dlg-bg-center");
33685 if(this.autoScroll !== false && !this.autoTabs){
33686 this.body.setStyle("overflow", "auto");
33689 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33691 if(this.closable !== false){
33692 this.el.addClass("x-dlg-closable");
33693 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33694 this.close.on("click", this.closeClick, this);
33695 this.close.addClassOnOver("x-dlg-close-over");
33697 if(this.collapsible !== false){
33698 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33699 this.collapseBtn.on("click", this.collapseClick, this);
33700 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33701 this.header.on("dblclick", this.collapseClick, this);
33703 if(this.resizable !== false){
33704 this.el.addClass("x-dlg-resizable");
33705 this.resizer = new Roo.Resizable(el, {
33706 minWidth: this.minWidth || 80,
33707 minHeight:this.minHeight || 80,
33708 handles: this.resizeHandles || "all",
33711 this.resizer.on("beforeresize", this.beforeResize, this);
33712 this.resizer.on("resize", this.onResize, this);
33714 if(this.draggable !== false){
33715 el.addClass("x-dlg-draggable");
33716 if (!this.proxyDrag) {
33717 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33720 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33722 dd.setHandleElId(this.header.id);
33723 dd.endDrag = this.endMove.createDelegate(this);
33724 dd.startDrag = this.startMove.createDelegate(this);
33725 dd.onDrag = this.onDrag.createDelegate(this);
33730 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33731 this.mask.enableDisplayMode("block");
33733 this.el.addClass("x-dlg-modal");
33736 this.shadow = new Roo.Shadow({
33737 mode : typeof this.shadow == "string" ? this.shadow : "sides",
33738 offset : this.shadowOffset
33741 this.shadowOffset = 0;
33743 if(Roo.useShims && this.shim !== false){
33744 this.shim = this.el.createShim();
33745 this.shim.hide = this.hideAction;
33753 if (this.buttons) {
33754 var bts= this.buttons;
33756 Roo.each(bts, function(b) {
33765 * Fires when a key is pressed
33766 * @param {Roo.BasicDialog} this
33767 * @param {Roo.EventObject} e
33772 * Fires when this dialog is moved by the user.
33773 * @param {Roo.BasicDialog} this
33774 * @param {Number} x The new page X
33775 * @param {Number} y The new page Y
33780 * Fires when this dialog is resized by the user.
33781 * @param {Roo.BasicDialog} this
33782 * @param {Number} width The new width
33783 * @param {Number} height The new height
33787 * @event beforehide
33788 * Fires before this dialog is hidden.
33789 * @param {Roo.BasicDialog} this
33791 "beforehide" : true,
33794 * Fires when this dialog is hidden.
33795 * @param {Roo.BasicDialog} this
33799 * @event beforeshow
33800 * Fires before this dialog is shown.
33801 * @param {Roo.BasicDialog} this
33803 "beforeshow" : true,
33806 * Fires when this dialog is shown.
33807 * @param {Roo.BasicDialog} this
33811 el.on("keydown", this.onKeyDown, this);
33812 el.on("mousedown", this.toFront, this);
33813 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33815 Roo.DialogManager.register(this);
33816 Roo.BasicDialog.superclass.constructor.call(this);
33819 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33820 shadowOffset: Roo.isIE ? 6 : 5,
33823 minButtonWidth: 75,
33824 defaultButton: null,
33825 buttonAlign: "right",
33830 * Sets the dialog title text
33831 * @param {String} text The title text to display
33832 * @return {Roo.BasicDialog} this
33834 setTitle : function(text){
33835 this.header.update(text);
33840 closeClick : function(){
33845 collapseClick : function(){
33846 this[this.collapsed ? "expand" : "collapse"]();
33850 * Collapses the dialog to its minimized state (only the title bar is visible).
33851 * Equivalent to the user clicking the collapse dialog button.
33853 collapse : function(){
33854 if(!this.collapsed){
33855 this.collapsed = true;
33856 this.el.addClass("x-dlg-collapsed");
33857 this.restoreHeight = this.el.getHeight();
33858 this.resizeTo(this.el.getWidth(), this.header.getHeight());
33863 * Expands a collapsed dialog back to its normal state. Equivalent to the user
33864 * clicking the expand dialog button.
33866 expand : function(){
33867 if(this.collapsed){
33868 this.collapsed = false;
33869 this.el.removeClass("x-dlg-collapsed");
33870 this.resizeTo(this.el.getWidth(), this.restoreHeight);
33875 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33876 * @return {Roo.TabPanel} The tabs component
33878 initTabs : function(){
33879 var tabs = this.getTabs();
33880 while(tabs.getTab(0)){
33883 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33885 tabs.addTab(Roo.id(dom), dom.title);
33893 beforeResize : function(){
33894 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33898 onResize : function(){
33899 this.refreshSize();
33900 this.syncBodyHeight();
33901 this.adjustAssets();
33903 this.fireEvent("resize", this, this.size.width, this.size.height);
33907 onKeyDown : function(e){
33908 if(this.isVisible()){
33909 this.fireEvent("keydown", this, e);
33914 * Resizes the dialog.
33915 * @param {Number} width
33916 * @param {Number} height
33917 * @return {Roo.BasicDialog} this
33919 resizeTo : function(width, height){
33920 this.el.setSize(width, height);
33921 this.size = {width: width, height: height};
33922 this.syncBodyHeight();
33923 if(this.fixedcenter){
33926 if(this.isVisible()){
33927 this.constrainXY();
33928 this.adjustAssets();
33930 this.fireEvent("resize", this, width, height);
33936 * Resizes the dialog to fit the specified content size.
33937 * @param {Number} width
33938 * @param {Number} height
33939 * @return {Roo.BasicDialog} this
33941 setContentSize : function(w, h){
33942 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33943 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33944 //if(!this.el.isBorderBox()){
33945 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33946 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33949 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33950 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33952 this.resizeTo(w, h);
33957 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
33958 * executed in response to a particular key being pressed while the dialog is active.
33959 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33960 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33961 * @param {Function} fn The function to call
33962 * @param {Object} scope (optional) The scope of the function
33963 * @return {Roo.BasicDialog} this
33965 addKeyListener : function(key, fn, scope){
33966 var keyCode, shift, ctrl, alt;
33967 if(typeof key == "object" && !(key instanceof Array)){
33968 keyCode = key["key"];
33969 shift = key["shift"];
33970 ctrl = key["ctrl"];
33975 var handler = function(dlg, e){
33976 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
33977 var k = e.getKey();
33978 if(keyCode instanceof Array){
33979 for(var i = 0, len = keyCode.length; i < len; i++){
33980 if(keyCode[i] == k){
33981 fn.call(scope || window, dlg, k, e);
33987 fn.call(scope || window, dlg, k, e);
33992 this.on("keydown", handler);
33997 * Returns the TabPanel component (creates it if it doesn't exist).
33998 * Note: If you wish to simply check for the existence of tabs without creating them,
33999 * check for a null 'tabs' property.
34000 * @return {Roo.TabPanel} The tabs component
34002 getTabs : function(){
34004 this.el.addClass("x-dlg-auto-tabs");
34005 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
34006 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
34012 * Adds a button to the footer section of the dialog.
34013 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
34014 * object or a valid Roo.DomHelper element config
34015 * @param {Function} handler The function called when the button is clicked
34016 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
34017 * @return {Roo.Button} The new button
34019 addButton : function(config, handler, scope){
34020 var dh = Roo.DomHelper;
34022 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
34024 if(!this.btnContainer){
34025 var tb = this.footer.createChild({
34027 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
34028 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34030 this.btnContainer = tb.firstChild.firstChild.firstChild;
34035 minWidth: this.minButtonWidth,
34038 if(typeof config == "string"){
34039 bconfig.text = config;
34042 bconfig.dhconfig = config;
34044 Roo.apply(bconfig, config);
34048 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34049 bconfig.position = Math.max(0, bconfig.position);
34050 fc = this.btnContainer.childNodes[bconfig.position];
34053 var btn = new Roo.Button(
34055 this.btnContainer.insertBefore(document.createElement("td"),fc)
34056 : this.btnContainer.appendChild(document.createElement("td")),
34057 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
34060 this.syncBodyHeight();
34063 * Array of all the buttons that have been added to this dialog via addButton
34068 this.buttons.push(btn);
34073 * Sets the default button to be focused when the dialog is displayed.
34074 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34075 * @return {Roo.BasicDialog} this
34077 setDefaultButton : function(btn){
34078 this.defaultButton = btn;
34083 getHeaderFooterHeight : function(safe){
34086 height += this.header.getHeight();
34089 var fm = this.footer.getMargins();
34090 height += (this.footer.getHeight()+fm.top+fm.bottom);
34092 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34093 height += this.centerBg.getPadding("tb");
34098 syncBodyHeight : function()
34100 var bd = this.body, // the text
34101 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34103 var height = this.size.height - this.getHeaderFooterHeight(false);
34104 bd.setHeight(height-bd.getMargins("tb"));
34105 var hh = this.header.getHeight();
34106 var h = this.size.height-hh;
34109 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34110 bw.setHeight(h-cb.getPadding("tb"));
34112 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34113 bd.setWidth(bw.getWidth(true));
34115 this.tabs.syncHeight();
34117 this.tabs.el.repaint();
34123 * Restores the previous state of the dialog if Roo.state is configured.
34124 * @return {Roo.BasicDialog} this
34126 restoreState : function(){
34127 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34128 if(box && box.width){
34129 this.xy = [box.x, box.y];
34130 this.resizeTo(box.width, box.height);
34136 beforeShow : function(){
34138 if(this.fixedcenter){
34139 this.xy = this.el.getCenterXY(true);
34142 Roo.get(document.body).addClass("x-body-masked");
34143 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34146 this.constrainXY();
34150 animShow : function(){
34151 var b = Roo.get(this.animateTarget).getBox();
34152 this.proxy.setSize(b.width, b.height);
34153 this.proxy.setLocation(b.x, b.y);
34155 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34156 true, .35, this.showEl.createDelegate(this));
34160 * Shows the dialog.
34161 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34162 * @return {Roo.BasicDialog} this
34164 show : function(animateTarget){
34165 if (this.fireEvent("beforeshow", this) === false){
34168 if(this.syncHeightBeforeShow){
34169 this.syncBodyHeight();
34170 }else if(this.firstShow){
34171 this.firstShow = false;
34172 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34174 this.animateTarget = animateTarget || this.animateTarget;
34175 if(!this.el.isVisible()){
34177 if(this.animateTarget && Roo.get(this.animateTarget)){
34187 showEl : function(){
34189 this.el.setXY(this.xy);
34191 this.adjustAssets(true);
34194 // IE peekaboo bug - fix found by Dave Fenwick
34198 this.fireEvent("show", this);
34202 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
34203 * dialog itself will receive focus.
34205 focus : function(){
34206 if(this.defaultButton){
34207 this.defaultButton.focus();
34209 this.focusEl.focus();
34214 constrainXY : function(){
34215 if(this.constraintoviewport !== false){
34216 if(!this.viewSize){
34217 if(this.container){
34218 var s = this.container.getSize();
34219 this.viewSize = [s.width, s.height];
34221 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34224 var s = Roo.get(this.container||document).getScroll();
34226 var x = this.xy[0], y = this.xy[1];
34227 var w = this.size.width, h = this.size.height;
34228 var vw = this.viewSize[0], vh = this.viewSize[1];
34229 // only move it if it needs it
34231 // first validate right/bottom
34232 if(x + w > vw+s.left){
34236 if(y + h > vh+s.top){
34240 // then make sure top/left isn't negative
34252 if(this.isVisible()){
34253 this.el.setLocation(x, y);
34254 this.adjustAssets();
34261 onDrag : function(){
34262 if(!this.proxyDrag){
34263 this.xy = this.el.getXY();
34264 this.adjustAssets();
34269 adjustAssets : function(doShow){
34270 var x = this.xy[0], y = this.xy[1];
34271 var w = this.size.width, h = this.size.height;
34272 if(doShow === true){
34274 this.shadow.show(this.el);
34280 if(this.shadow && this.shadow.isVisible()){
34281 this.shadow.show(this.el);
34283 if(this.shim && this.shim.isVisible()){
34284 this.shim.setBounds(x, y, w, h);
34289 adjustViewport : function(w, h){
34291 w = Roo.lib.Dom.getViewWidth();
34292 h = Roo.lib.Dom.getViewHeight();
34295 this.viewSize = [w, h];
34296 if(this.modal && this.mask.isVisible()){
34297 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34298 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34300 if(this.isVisible()){
34301 this.constrainXY();
34306 * Destroys this dialog and all its supporting elements (including any tabs, shim,
34307 * shadow, proxy, mask, etc.) Also removes all event listeners.
34308 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34310 destroy : function(removeEl){
34311 if(this.isVisible()){
34312 this.animateTarget = null;
34315 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34317 this.tabs.destroy(removeEl);
34330 for(var i = 0, len = this.buttons.length; i < len; i++){
34331 this.buttons[i].destroy();
34334 this.el.removeAllListeners();
34335 if(removeEl === true){
34336 this.el.update("");
34339 Roo.DialogManager.unregister(this);
34343 startMove : function(){
34344 if(this.proxyDrag){
34347 if(this.constraintoviewport !== false){
34348 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34353 endMove : function(){
34354 if(!this.proxyDrag){
34355 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34357 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34360 this.refreshSize();
34361 this.adjustAssets();
34363 this.fireEvent("move", this, this.xy[0], this.xy[1]);
34367 * Brings this dialog to the front of any other visible dialogs
34368 * @return {Roo.BasicDialog} this
34370 toFront : function(){
34371 Roo.DialogManager.bringToFront(this);
34376 * Sends this dialog to the back (under) of any other visible dialogs
34377 * @return {Roo.BasicDialog} this
34379 toBack : function(){
34380 Roo.DialogManager.sendToBack(this);
34385 * Centers this dialog in the viewport
34386 * @return {Roo.BasicDialog} this
34388 center : function(){
34389 var xy = this.el.getCenterXY(true);
34390 this.moveTo(xy[0], xy[1]);
34395 * Moves the dialog's top-left corner to the specified point
34396 * @param {Number} x
34397 * @param {Number} y
34398 * @return {Roo.BasicDialog} this
34400 moveTo : function(x, y){
34402 if(this.isVisible()){
34403 this.el.setXY(this.xy);
34404 this.adjustAssets();
34410 * Aligns the dialog to the specified element
34411 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34412 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34413 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34414 * @return {Roo.BasicDialog} this
34416 alignTo : function(element, position, offsets){
34417 this.xy = this.el.getAlignToXY(element, position, offsets);
34418 if(this.isVisible()){
34419 this.el.setXY(this.xy);
34420 this.adjustAssets();
34426 * Anchors an element to another element and realigns it when the window is resized.
34427 * @param {String/HTMLElement/Roo.Element} element The element to align to.
34428 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34429 * @param {Array} offsets (optional) Offset the positioning by [x, y]
34430 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34431 * is a number, it is used as the buffer delay (defaults to 50ms).
34432 * @return {Roo.BasicDialog} this
34434 anchorTo : function(el, alignment, offsets, monitorScroll){
34435 var action = function(){
34436 this.alignTo(el, alignment, offsets);
34438 Roo.EventManager.onWindowResize(action, this);
34439 var tm = typeof monitorScroll;
34440 if(tm != 'undefined'){
34441 Roo.EventManager.on(window, 'scroll', action, this,
34442 {buffer: tm == 'number' ? monitorScroll : 50});
34449 * Returns true if the dialog is visible
34450 * @return {Boolean}
34452 isVisible : function(){
34453 return this.el.isVisible();
34457 animHide : function(callback){
34458 var b = Roo.get(this.animateTarget).getBox();
34460 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34462 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34463 this.hideEl.createDelegate(this, [callback]));
34467 * Hides the dialog.
34468 * @param {Function} callback (optional) Function to call when the dialog is hidden
34469 * @return {Roo.BasicDialog} this
34471 hide : function(callback){
34472 if (this.fireEvent("beforehide", this) === false){
34476 this.shadow.hide();
34481 // sometimes animateTarget seems to get set.. causing problems...
34482 // this just double checks..
34483 if(this.animateTarget && Roo.get(this.animateTarget)) {
34484 this.animHide(callback);
34487 this.hideEl(callback);
34493 hideEl : function(callback){
34497 Roo.get(document.body).removeClass("x-body-masked");
34499 this.fireEvent("hide", this);
34500 if(typeof callback == "function"){
34506 hideAction : function(){
34507 this.setLeft("-10000px");
34508 this.setTop("-10000px");
34509 this.setStyle("visibility", "hidden");
34513 refreshSize : function(){
34514 this.size = this.el.getSize();
34515 this.xy = this.el.getXY();
34516 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34520 // z-index is managed by the DialogManager and may be overwritten at any time
34521 setZIndex : function(index){
34523 this.mask.setStyle("z-index", index);
34526 this.shim.setStyle("z-index", ++index);
34529 this.shadow.setZIndex(++index);
34531 this.el.setStyle("z-index", ++index);
34533 this.proxy.setStyle("z-index", ++index);
34536 this.resizer.proxy.setStyle("z-index", ++index);
34539 this.lastZIndex = index;
34543 * Returns the element for this dialog
34544 * @return {Roo.Element} The underlying dialog Element
34546 getEl : function(){
34552 * @class Roo.DialogManager
34553 * Provides global access to BasicDialogs that have been created and
34554 * support for z-indexing (layering) multiple open dialogs.
34556 Roo.DialogManager = function(){
34558 var accessList = [];
34562 var sortDialogs = function(d1, d2){
34563 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34567 var orderDialogs = function(){
34568 accessList.sort(sortDialogs);
34569 var seed = Roo.DialogManager.zseed;
34570 for(var i = 0, len = accessList.length; i < len; i++){
34571 var dlg = accessList[i];
34573 dlg.setZIndex(seed + (i*10));
34580 * The starting z-index for BasicDialogs (defaults to 9000)
34581 * @type Number The z-index value
34586 register : function(dlg){
34587 list[dlg.id] = dlg;
34588 accessList.push(dlg);
34592 unregister : function(dlg){
34593 delete list[dlg.id];
34596 if(!accessList.indexOf){
34597 for( i = 0, len = accessList.length; i < len; i++){
34598 if(accessList[i] == dlg){
34599 accessList.splice(i, 1);
34604 i = accessList.indexOf(dlg);
34606 accessList.splice(i, 1);
34612 * Gets a registered dialog by id
34613 * @param {String/Object} id The id of the dialog or a dialog
34614 * @return {Roo.BasicDialog} this
34616 get : function(id){
34617 return typeof id == "object" ? id : list[id];
34621 * Brings the specified dialog to the front
34622 * @param {String/Object} dlg The id of the dialog or a dialog
34623 * @return {Roo.BasicDialog} this
34625 bringToFront : function(dlg){
34626 dlg = this.get(dlg);
34629 dlg._lastAccess = new Date().getTime();
34636 * Sends the specified dialog to the back
34637 * @param {String/Object} dlg The id of the dialog or a dialog
34638 * @return {Roo.BasicDialog} this
34640 sendToBack : function(dlg){
34641 dlg = this.get(dlg);
34642 dlg._lastAccess = -(new Date().getTime());
34648 * Hides all dialogs
34650 hideAll : function(){
34651 for(var id in list){
34652 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34661 * @class Roo.LayoutDialog
34662 * @extends Roo.BasicDialog
34663 * @children Roo.ContentPanel
34664 * @parent builder none
34665 * Dialog which provides adjustments for working with a layout in a Dialog.
34666 * Add your necessary layout config options to the dialog's config.<br>
34667 * Example usage (including a nested layout):
34670 dialog = new Roo.LayoutDialog("download-dlg", {
34679 // layout config merges with the dialog config
34681 tabPosition: "top",
34682 alwaysShowTabs: true
34685 dialog.addKeyListener(27, dialog.hide, dialog);
34686 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34687 dialog.addButton("Build It!", this.getDownload, this);
34689 // we can even add nested layouts
34690 var innerLayout = new Roo.BorderLayout("dl-inner", {
34700 innerLayout.beginUpdate();
34701 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34702 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34703 innerLayout.endUpdate(true);
34705 var layout = dialog.getLayout();
34706 layout.beginUpdate();
34707 layout.add("center", new Roo.ContentPanel("standard-panel",
34708 {title: "Download the Source", fitToFrame:true}));
34709 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34710 {title: "Build your own roo.js"}));
34711 layout.getRegion("center").showPanel(sp);
34712 layout.endUpdate();
34716 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34717 * @param {Object} config configuration options
34719 Roo.LayoutDialog = function(el, cfg){
34722 if (typeof(cfg) == 'undefined') {
34723 config = Roo.apply({}, el);
34724 // not sure why we use documentElement here.. - it should always be body.
34725 // IE7 borks horribly if we use documentElement.
34726 // webkit also does not like documentElement - it creates a body element...
34727 el = Roo.get( document.body || document.documentElement ).createChild();
34728 //config.autoCreate = true;
34732 config.autoTabs = false;
34733 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34734 this.body.setStyle({overflow:"hidden", position:"relative"});
34735 this.layout = new Roo.BorderLayout(this.body.dom, config);
34736 this.layout.monitorWindowResize = false;
34737 this.el.addClass("x-dlg-auto-layout");
34738 // fix case when center region overwrites center function
34739 this.center = Roo.BasicDialog.prototype.center;
34740 this.on("show", this.layout.layout, this.layout, true);
34741 if (config.items) {
34742 var xitems = config.items;
34743 delete config.items;
34744 Roo.each(xitems, this.addxtype, this);
34749 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34753 * @cfg {Roo.LayoutRegion} east
34756 * @cfg {Roo.LayoutRegion} west
34759 * @cfg {Roo.LayoutRegion} south
34762 * @cfg {Roo.LayoutRegion} north
34765 * @cfg {Roo.LayoutRegion} center
34768 * @cfg {Roo.Button} buttons[] Bottom buttons..
34773 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34776 endUpdate : function(){
34777 this.layout.endUpdate();
34781 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34784 beginUpdate : function(){
34785 this.layout.beginUpdate();
34789 * Get the BorderLayout for this dialog
34790 * @return {Roo.BorderLayout}
34792 getLayout : function(){
34793 return this.layout;
34796 showEl : function(){
34797 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34799 this.layout.layout();
34804 // Use the syncHeightBeforeShow config option to control this automatically
34805 syncBodyHeight : function(){
34806 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34807 if(this.layout){this.layout.layout();}
34811 * Add an xtype element (actually adds to the layout.)
34812 * @return {Object} xdata xtype object data.
34815 addxtype : function(c) {
34816 return this.layout.addxtype(c);
34820 * Ext JS Library 1.1.1
34821 * Copyright(c) 2006-2007, Ext JS, LLC.
34823 * Originally Released Under LGPL - original licence link has changed is not relivant.
34826 * <script type="text/javascript">
34830 * @class Roo.MessageBox
34832 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
34836 Roo.Msg.alert('Status', 'Changes saved successfully.');
34838 // Prompt for user data:
34839 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34841 // process text value...
34845 // Show a dialog using config options:
34847 title:'Save Changes?',
34848 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34849 buttons: Roo.Msg.YESNOCANCEL,
34856 Roo.MessageBox = function(){
34857 var dlg, opt, mask, waitTimer;
34858 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34859 var buttons, activeTextEl, bwidth;
34862 var handleButton = function(button){
34864 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34868 var handleHide = function(){
34869 if(opt && opt.cls){
34870 dlg.el.removeClass(opt.cls);
34873 Roo.TaskMgr.stop(waitTimer);
34879 var updateButtons = function(b){
34882 buttons["ok"].hide();
34883 buttons["cancel"].hide();
34884 buttons["yes"].hide();
34885 buttons["no"].hide();
34886 dlg.footer.dom.style.display = 'none';
34889 dlg.footer.dom.style.display = '';
34890 for(var k in buttons){
34891 if(typeof buttons[k] != "function"){
34894 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34895 width += buttons[k].el.getWidth()+15;
34905 var handleEsc = function(d, k, e){
34906 if(opt && opt.closable !== false){
34916 * Returns a reference to the underlying {@link Roo.BasicDialog} element
34917 * @return {Roo.BasicDialog} The BasicDialog element
34919 getDialog : function(){
34921 dlg = new Roo.BasicDialog("x-msg-box", {
34926 constraintoviewport:false,
34928 collapsible : false,
34931 width:400, height:100,
34932 buttonAlign:"center",
34933 closeClick : function(){
34934 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34935 handleButton("no");
34937 handleButton("cancel");
34942 dlg.on("hide", handleHide);
34944 dlg.addKeyListener(27, handleEsc);
34946 var bt = this.buttonText;
34947 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34948 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34949 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34950 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34951 bodyEl = dlg.body.createChild({
34953 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>'
34955 msgEl = bodyEl.dom.firstChild;
34956 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34957 textboxEl.enableDisplayMode();
34958 textboxEl.addKeyListener([10,13], function(){
34959 if(dlg.isVisible() && opt && opt.buttons){
34960 if(opt.buttons.ok){
34961 handleButton("ok");
34962 }else if(opt.buttons.yes){
34963 handleButton("yes");
34967 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34968 textareaEl.enableDisplayMode();
34969 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34970 progressEl.enableDisplayMode();
34971 var pf = progressEl.dom.firstChild;
34973 pp = Roo.get(pf.firstChild);
34974 pp.setHeight(pf.offsetHeight);
34982 * Updates the message box body text
34983 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34984 * the XHTML-compliant non-breaking space character '&#160;')
34985 * @return {Roo.MessageBox} This message box
34987 updateText : function(text){
34988 if(!dlg.isVisible() && !opt.width){
34989 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34991 msgEl.innerHTML = text || ' ';
34993 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34994 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34996 Math.min(opt.width || cw , this.maxWidth),
34997 Math.max(opt.minWidth || this.minWidth, bwidth)
35000 activeTextEl.setWidth(w);
35002 if(dlg.isVisible()){
35003 dlg.fixedcenter = false;
35005 // to big, make it scroll. = But as usual stupid IE does not support
35008 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
35009 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
35010 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
35012 bodyEl.dom.style.height = '';
35013 bodyEl.dom.style.overflowY = '';
35016 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
35018 bodyEl.dom.style.overflowX = '';
35021 dlg.setContentSize(w, bodyEl.getHeight());
35022 if(dlg.isVisible()){
35023 dlg.fixedcenter = true;
35029 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
35030 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35031 * @param {Number} value Any number between 0 and 1 (e.g., .5)
35032 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35033 * @return {Roo.MessageBox} This message box
35035 updateProgress : function(value, text){
35037 this.updateText(text);
35039 if (pp) { // weird bug on my firefox - for some reason this is not defined
35040 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35046 * Returns true if the message box is currently displayed
35047 * @return {Boolean} True if the message box is visible, else false
35049 isVisible : function(){
35050 return dlg && dlg.isVisible();
35054 * Hides the message box if it is displayed
35057 if(this.isVisible()){
35063 * Displays a new message box, or reinitializes an existing message box, based on the config options
35064 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35065 * The following config object properties are supported:
35067 Property Type Description
35068 ---------- --------------- ------------------------------------------------------------------------------------
35069 animEl String/Element An id or Element from which the message box should animate as it opens and
35070 closes (defaults to undefined)
35071 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35072 cancel:'Bar'}), or false to not show any buttons (defaults to false)
35073 closable Boolean False to hide the top-right close button (defaults to true). Note that
35074 progress and wait dialogs will ignore this property and always hide the
35075 close button as they can only be closed programmatically.
35076 cls String A custom CSS class to apply to the message box element
35077 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
35078 displayed (defaults to 75)
35079 fn Function A callback function to execute after closing the dialog. The arguments to the
35080 function will be btn (the name of the button that was clicked, if applicable,
35081 e.g. "ok"), and text (the value of the active text field, if applicable).
35082 Progress and wait dialogs will ignore this option since they do not respond to
35083 user actions and can only be closed programmatically, so any required function
35084 should be called by the same code after it closes the dialog.
35085 icon String A CSS class that provides a background image to be used as an icon for
35086 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35087 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
35088 minWidth Number The minimum width in pixels of the message box (defaults to 100)
35089 modal Boolean False to allow user interaction with the page while the message box is
35090 displayed (defaults to true)
35091 msg String A string that will replace the existing message box body text (defaults
35092 to the XHTML-compliant non-breaking space character ' ')
35093 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
35094 progress Boolean True to display a progress bar (defaults to false)
35095 progressText String The text to display inside the progress bar if progress = true (defaults to '')
35096 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
35097 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
35098 title String The title text
35099 value String The string value to set into the active textbox element if displayed
35100 wait Boolean True to display a progress bar (defaults to false)
35101 width Number The width of the dialog in pixels
35108 msg: 'Please enter your address:',
35110 buttons: Roo.MessageBox.OKCANCEL,
35113 animEl: 'addAddressBtn'
35116 * @param {Object} config Configuration options
35117 * @return {Roo.MessageBox} This message box
35119 show : function(options)
35122 // this causes nightmares if you show one dialog after another
35123 // especially on callbacks..
35125 if(this.isVisible()){
35128 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35129 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
35130 Roo.log("New Dialog Message:" + options.msg )
35131 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35132 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35135 var d = this.getDialog();
35137 d.setTitle(opt.title || " ");
35138 d.close.setDisplayed(opt.closable !== false);
35139 activeTextEl = textboxEl;
35140 opt.prompt = opt.prompt || (opt.multiline ? true : false);
35145 textareaEl.setHeight(typeof opt.multiline == "number" ?
35146 opt.multiline : this.defaultTextHeight);
35147 activeTextEl = textareaEl;
35156 progressEl.setDisplayed(opt.progress === true);
35157 this.updateProgress(0);
35158 activeTextEl.dom.value = opt.value || "";
35160 dlg.setDefaultButton(activeTextEl);
35162 var bs = opt.buttons;
35165 db = buttons["ok"];
35166 }else if(bs && bs.yes){
35167 db = buttons["yes"];
35169 dlg.setDefaultButton(db);
35171 bwidth = updateButtons(opt.buttons);
35172 this.updateText(opt.msg);
35174 d.el.addClass(opt.cls);
35176 d.proxyDrag = opt.proxyDrag === true;
35177 d.modal = opt.modal !== false;
35178 d.mask = opt.modal !== false ? mask : false;
35179 if(!d.isVisible()){
35180 // force it to the end of the z-index stack so it gets a cursor in FF
35181 document.body.appendChild(dlg.el.dom);
35182 d.animateTarget = null;
35183 d.show(options.animEl);
35190 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
35191 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35192 * and closing the message box when the process is complete.
35193 * @param {String} title The title bar text
35194 * @param {String} msg The message box body text
35195 * @return {Roo.MessageBox} This message box
35197 progress : function(title, msg){
35204 minWidth: this.minProgressWidth,
35211 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35212 * If a callback function is passed it will be called after the user clicks the button, and the
35213 * id of the button that was clicked will be passed as the only parameter to the callback
35214 * (could also be the top-right close button).
35215 * @param {String} title The title bar text
35216 * @param {String} msg The message box body text
35217 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35218 * @param {Object} scope (optional) The scope of the callback function
35219 * @return {Roo.MessageBox} This message box
35221 alert : function(title, msg, fn, scope){
35234 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
35235 * interaction while waiting for a long-running process to complete that does not have defined intervals.
35236 * You are responsible for closing the message box when the process is complete.
35237 * @param {String} msg The message box body text
35238 * @param {String} title (optional) The title bar text
35239 * @return {Roo.MessageBox} This message box
35241 wait : function(msg, title){
35252 waitTimer = Roo.TaskMgr.start({
35254 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35262 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35263 * If a callback function is passed it will be called after the user clicks either button, and the id of the
35264 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35265 * @param {String} title The title bar text
35266 * @param {String} msg The message box body text
35267 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35268 * @param {Object} scope (optional) The scope of the callback function
35269 * @return {Roo.MessageBox} This message box
35271 confirm : function(title, msg, fn, scope){
35275 buttons: this.YESNO,
35284 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35285 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
35286 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35287 * (could also be the top-right close button) and the text that was entered will be passed as the two
35288 * parameters to the callback.
35289 * @param {String} title The title bar text
35290 * @param {String} msg The message box body text
35291 * @param {Function} fn (optional) The callback function invoked after the message box is closed
35292 * @param {Object} scope (optional) The scope of the callback function
35293 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35294 * property, or the height in pixels to create the textbox (defaults to false / single-line)
35295 * @return {Roo.MessageBox} This message box
35297 prompt : function(title, msg, fn, scope, multiline){
35301 buttons: this.OKCANCEL,
35306 multiline: multiline,
35313 * Button config that displays a single OK button
35318 * Button config that displays Yes and No buttons
35321 YESNO : {yes:true, no:true},
35323 * Button config that displays OK and Cancel buttons
35326 OKCANCEL : {ok:true, cancel:true},
35328 * Button config that displays Yes, No and Cancel buttons
35331 YESNOCANCEL : {yes:true, no:true, cancel:true},
35334 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35337 defaultTextHeight : 75,
35339 * The maximum width in pixels of the message box (defaults to 600)
35344 * The minimum width in pixels of the message box (defaults to 100)
35349 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
35350 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35353 minProgressWidth : 250,
35355 * An object containing the default button text strings that can be overriden for localized language support.
35356 * Supported properties are: ok, cancel, yes and no.
35357 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35370 * Shorthand for {@link Roo.MessageBox}
35372 Roo.Msg = Roo.MessageBox;/*
35374 * Ext JS Library 1.1.1
35375 * Copyright(c) 2006-2007, Ext JS, LLC.
35377 * Originally Released Under LGPL - original licence link has changed is not relivant.
35380 * <script type="text/javascript">
35383 * @class Roo.QuickTips
35384 * Provides attractive and customizable tooltips for any element.
35387 Roo.QuickTips = function(){
35388 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35389 var ce, bd, xy, dd;
35390 var visible = false, disabled = true, inited = false;
35391 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35393 var onOver = function(e){
35397 var t = e.getTarget();
35398 if(!t || t.nodeType !== 1 || t == document || t == document.body){
35401 if(ce && t == ce.el){
35402 clearTimeout(hideProc);
35405 if(t && tagEls[t.id]){
35406 tagEls[t.id].el = t;
35407 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35410 var ttp, et = Roo.fly(t);
35411 var ns = cfg.namespace;
35412 if(tm.interceptTitles && t.title){
35415 t.removeAttribute("title");
35416 e.preventDefault();
35418 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35421 showProc = show.defer(tm.showDelay, tm, [{
35423 text: ttp.replace(/\\n/g,'<br/>'),
35424 width: et.getAttributeNS(ns, cfg.width),
35425 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35426 title: et.getAttributeNS(ns, cfg.title),
35427 cls: et.getAttributeNS(ns, cfg.cls)
35432 var onOut = function(e){
35433 clearTimeout(showProc);
35434 var t = e.getTarget();
35435 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35436 hideProc = setTimeout(hide, tm.hideDelay);
35440 var onMove = function(e){
35446 if(tm.trackMouse && ce){
35451 var onDown = function(e){
35452 clearTimeout(showProc);
35453 clearTimeout(hideProc);
35455 if(tm.hideOnClick){
35458 tm.enable.defer(100, tm);
35463 var getPad = function(){
35464 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35467 var show = function(o){
35471 clearTimeout(dismissProc);
35473 if(removeCls){ // in case manually hidden
35474 el.removeClass(removeCls);
35478 el.addClass(ce.cls);
35479 removeCls = ce.cls;
35482 tipTitle.update(ce.title);
35485 tipTitle.update('');
35488 el.dom.style.width = tm.maxWidth+'px';
35489 //tipBody.dom.style.width = '';
35490 tipBodyText.update(o.text);
35491 var p = getPad(), w = ce.width;
35493 var td = tipBodyText.dom;
35494 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35495 if(aw > tm.maxWidth){
35497 }else if(aw < tm.minWidth){
35503 //tipBody.setWidth(w);
35504 el.setWidth(parseInt(w, 10) + p);
35505 if(ce.autoHide === false){
35506 close.setDisplayed(true);
35511 close.setDisplayed(false);
35517 el.avoidY = xy[1]-18;
35522 el.setStyle("visibility", "visible");
35523 el.fadeIn({callback: afterShow});
35529 var afterShow = function(){
35533 if(tm.autoDismiss && ce.autoHide !== false){
35534 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35539 var hide = function(noanim){
35540 clearTimeout(dismissProc);
35541 clearTimeout(hideProc);
35543 if(el.isVisible()){
35545 if(noanim !== true && tm.animate){
35546 el.fadeOut({callback: afterHide});
35553 var afterHide = function(){
35556 el.removeClass(removeCls);
35563 * @cfg {Number} minWidth
35564 * The minimum width of the quick tip (defaults to 40)
35568 * @cfg {Number} maxWidth
35569 * The maximum width of the quick tip (defaults to 300)
35573 * @cfg {Boolean} interceptTitles
35574 * True to automatically use the element's DOM title value if available (defaults to false)
35576 interceptTitles : false,
35578 * @cfg {Boolean} trackMouse
35579 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35581 trackMouse : false,
35583 * @cfg {Boolean} hideOnClick
35584 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35586 hideOnClick : true,
35588 * @cfg {Number} showDelay
35589 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35593 * @cfg {Number} hideDelay
35594 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35598 * @cfg {Boolean} autoHide
35599 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35600 * Used in conjunction with hideDelay.
35605 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35606 * (defaults to true). Used in conjunction with autoDismissDelay.
35608 autoDismiss : true,
35611 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35613 autoDismissDelay : 5000,
35615 * @cfg {Boolean} animate
35616 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35621 * @cfg {String} title
35622 * Title text to display (defaults to ''). This can be any valid HTML markup.
35626 * @cfg {String} text
35627 * Body text to display (defaults to ''). This can be any valid HTML markup.
35631 * @cfg {String} cls
35632 * A CSS class to apply to the base quick tip element (defaults to '').
35636 * @cfg {Number} width
35637 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
35638 * minWidth or maxWidth.
35643 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
35644 * or display QuickTips in a page.
35647 tm = Roo.QuickTips;
35648 cfg = tm.tagConfig;
35650 if(!Roo.isReady){ // allow calling of init() before onReady
35651 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35654 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35655 el.fxDefaults = {stopFx: true};
35656 // maximum custom styling
35657 //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>');
35658 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>');
35659 tipTitle = el.child('h3');
35660 tipTitle.enableDisplayMode("block");
35661 tipBody = el.child('div.x-tip-bd');
35662 tipBodyText = el.child('div.x-tip-bd-inner');
35663 //bdLeft = el.child('div.x-tip-bd-left');
35664 //bdRight = el.child('div.x-tip-bd-right');
35665 close = el.child('div.x-tip-close');
35666 close.enableDisplayMode("block");
35667 close.on("click", hide);
35668 var d = Roo.get(document);
35669 d.on("mousedown", onDown);
35670 d.on("mouseover", onOver);
35671 d.on("mouseout", onOut);
35672 d.on("mousemove", onMove);
35673 esc = d.addKeyListener(27, hide);
35676 dd = el.initDD("default", null, {
35677 onDrag : function(){
35681 dd.setHandleElId(tipTitle.id);
35690 * Configures a new quick tip instance and assigns it to a target element. The following config options
35693 Property Type Description
35694 ---------- --------------------- ------------------------------------------------------------------------
35695 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
35697 * @param {Object} config The config object
35699 register : function(config){
35700 var cs = config instanceof Array ? config : arguments;
35701 for(var i = 0, len = cs.length; i < len; i++) {
35703 var target = c.target;
35705 if(target instanceof Array){
35706 for(var j = 0, jlen = target.length; j < jlen; j++){
35707 tagEls[target[j]] = c;
35710 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35717 * Removes this quick tip from its element and destroys it.
35718 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35720 unregister : function(el){
35721 delete tagEls[Roo.id(el)];
35725 * Enable this quick tip.
35727 enable : function(){
35728 if(inited && disabled){
35730 if(locks.length < 1){
35737 * Disable this quick tip.
35739 disable : function(){
35741 clearTimeout(showProc);
35742 clearTimeout(hideProc);
35743 clearTimeout(dismissProc);
35751 * Returns true if the quick tip is enabled, else false.
35753 isEnabled : function(){
35759 namespace : "roo", // was ext?? this may break..
35760 alt_namespace : "ext",
35761 attribute : "qtip",
35771 // backwards compat
35772 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35774 * Ext JS Library 1.1.1
35775 * Copyright(c) 2006-2007, Ext JS, LLC.
35777 * Originally Released Under LGPL - original licence link has changed is not relivant.
35780 * <script type="text/javascript">
35785 * @class Roo.tree.TreePanel
35786 * @extends Roo.data.Tree
35787 * @cfg {Roo.tree.TreeNode} root The root node
35788 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35789 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35790 * @cfg {Boolean} enableDD true to enable drag and drop
35791 * @cfg {Boolean} enableDrag true to enable just drag
35792 * @cfg {Boolean} enableDrop true to enable just drop
35793 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35794 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35795 * @cfg {String} ddGroup The DD group this TreePanel belongs to
35796 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35797 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35798 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35799 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35800 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35801 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35802 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35803 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35804 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35805 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35806 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35807 * @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>
35808 * @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>
35811 * @param {String/HTMLElement/Element} el The container element
35812 * @param {Object} config
35814 Roo.tree.TreePanel = function(el, config){
35816 var loader = false;
35818 root = config.root;
35819 delete config.root;
35821 if (config.loader) {
35822 loader = config.loader;
35823 delete config.loader;
35826 Roo.apply(this, config);
35827 Roo.tree.TreePanel.superclass.constructor.call(this);
35828 this.el = Roo.get(el);
35829 this.el.addClass('x-tree');
35830 //console.log(root);
35832 this.setRootNode( Roo.factory(root, Roo.tree));
35835 this.loader = Roo.factory(loader, Roo.tree);
35838 * Read-only. The id of the container element becomes this TreePanel's id.
35840 this.id = this.el.id;
35843 * @event beforeload
35844 * Fires before a node is loaded, return false to cancel
35845 * @param {Node} node The node being loaded
35847 "beforeload" : true,
35850 * Fires when a node is loaded
35851 * @param {Node} node The node that was loaded
35855 * @event textchange
35856 * Fires when the text for a node is changed
35857 * @param {Node} node The node
35858 * @param {String} text The new text
35859 * @param {String} oldText The old text
35861 "textchange" : true,
35863 * @event beforeexpand
35864 * Fires before a node is expanded, return false to cancel.
35865 * @param {Node} node The node
35866 * @param {Boolean} deep
35867 * @param {Boolean} anim
35869 "beforeexpand" : true,
35871 * @event beforecollapse
35872 * Fires before a node is collapsed, return false to cancel.
35873 * @param {Node} node The node
35874 * @param {Boolean} deep
35875 * @param {Boolean} anim
35877 "beforecollapse" : true,
35880 * Fires when a node is expanded
35881 * @param {Node} node The node
35885 * @event disabledchange
35886 * Fires when the disabled status of a node changes
35887 * @param {Node} node The node
35888 * @param {Boolean} disabled
35890 "disabledchange" : true,
35893 * Fires when a node is collapsed
35894 * @param {Node} node The node
35898 * @event beforeclick
35899 * Fires before click processing on a node. Return false to cancel the default action.
35900 * @param {Node} node The node
35901 * @param {Roo.EventObject} e The event object
35903 "beforeclick":true,
35905 * @event checkchange
35906 * Fires when a node with a checkbox's checked property changes
35907 * @param {Node} this This node
35908 * @param {Boolean} checked
35910 "checkchange":true,
35913 * Fires when a node is clicked
35914 * @param {Node} node The node
35915 * @param {Roo.EventObject} e The event object
35920 * Fires when a node is double clicked
35921 * @param {Node} node The node
35922 * @param {Roo.EventObject} e The event object
35926 * @event contextmenu
35927 * Fires when a node is right clicked
35928 * @param {Node} node The node
35929 * @param {Roo.EventObject} e The event object
35931 "contextmenu":true,
35933 * @event beforechildrenrendered
35934 * Fires right before the child nodes for a node are rendered
35935 * @param {Node} node The node
35937 "beforechildrenrendered":true,
35940 * Fires when a node starts being dragged
35941 * @param {Roo.tree.TreePanel} this
35942 * @param {Roo.tree.TreeNode} node
35943 * @param {event} e The raw browser event
35945 "startdrag" : true,
35948 * Fires when a drag operation is complete
35949 * @param {Roo.tree.TreePanel} this
35950 * @param {Roo.tree.TreeNode} node
35951 * @param {event} e The raw browser event
35956 * Fires when a dragged node is dropped on a valid DD target
35957 * @param {Roo.tree.TreePanel} this
35958 * @param {Roo.tree.TreeNode} node
35959 * @param {DD} dd The dd it was dropped on
35960 * @param {event} e The raw browser event
35964 * @event beforenodedrop
35965 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35966 * passed to handlers has the following properties:<br />
35967 * <ul style="padding:5px;padding-left:16px;">
35968 * <li>tree - The TreePanel</li>
35969 * <li>target - The node being targeted for the drop</li>
35970 * <li>data - The drag data from the drag source</li>
35971 * <li>point - The point of the drop - append, above or below</li>
35972 * <li>source - The drag source</li>
35973 * <li>rawEvent - Raw mouse event</li>
35974 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35975 * to be inserted by setting them on this object.</li>
35976 * <li>cancel - Set this to true to cancel the drop.</li>
35978 * @param {Object} dropEvent
35980 "beforenodedrop" : true,
35983 * Fires after a DD object is dropped on a node in this tree. The dropEvent
35984 * passed to handlers has the following properties:<br />
35985 * <ul style="padding:5px;padding-left:16px;">
35986 * <li>tree - The TreePanel</li>
35987 * <li>target - The node being targeted for the drop</li>
35988 * <li>data - The drag data from the drag source</li>
35989 * <li>point - The point of the drop - append, above or below</li>
35990 * <li>source - The drag source</li>
35991 * <li>rawEvent - Raw mouse event</li>
35992 * <li>dropNode - Dropped node(s).</li>
35994 * @param {Object} dropEvent
35998 * @event nodedragover
35999 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
36000 * passed to handlers has the following properties:<br />
36001 * <ul style="padding:5px;padding-left:16px;">
36002 * <li>tree - The TreePanel</li>
36003 * <li>target - The node being targeted for the drop</li>
36004 * <li>data - The drag data from the drag source</li>
36005 * <li>point - The point of the drop - append, above or below</li>
36006 * <li>source - The drag source</li>
36007 * <li>rawEvent - Raw mouse event</li>
36008 * <li>dropNode - Drop node(s) provided by the source.</li>
36009 * <li>cancel - Set this to true to signal drop not allowed.</li>
36011 * @param {Object} dragOverEvent
36013 "nodedragover" : true,
36015 * @event appendnode
36016 * Fires when append node to the tree
36017 * @param {Roo.tree.TreePanel} this
36018 * @param {Roo.tree.TreeNode} node
36019 * @param {Number} index The index of the newly appended node
36021 "appendnode" : true
36024 if(this.singleExpand){
36025 this.on("beforeexpand", this.restrictExpand, this);
36028 this.editor.tree = this;
36029 this.editor = Roo.factory(this.editor, Roo.tree);
36032 if (this.selModel) {
36033 this.selModel = Roo.factory(this.selModel, Roo.tree);
36037 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36038 rootVisible : true,
36039 animate: Roo.enableFx,
36042 hlDrop : Roo.enableFx,
36046 rendererTip: false,
36048 restrictExpand : function(node){
36049 var p = node.parentNode;
36051 if(p.expandedChild && p.expandedChild.parentNode == p){
36052 p.expandedChild.collapse();
36054 p.expandedChild = node;
36058 // private override
36059 setRootNode : function(node){
36060 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36061 if(!this.rootVisible){
36062 node.ui = new Roo.tree.RootTreeNodeUI(node);
36068 * Returns the container element for this TreePanel
36070 getEl : function(){
36075 * Returns the default TreeLoader for this TreePanel
36077 getLoader : function(){
36078 return this.loader;
36084 expandAll : function(){
36085 this.root.expand(true);
36089 * Collapse all nodes
36091 collapseAll : function(){
36092 this.root.collapse(true);
36096 * Returns the selection model used by this TreePanel
36098 getSelectionModel : function(){
36099 if(!this.selModel){
36100 this.selModel = new Roo.tree.DefaultSelectionModel();
36102 return this.selModel;
36106 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36107 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36108 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36111 getChecked : function(a, startNode){
36112 startNode = startNode || this.root;
36114 var f = function(){
36115 if(this.attributes.checked){
36116 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36119 startNode.cascade(f);
36124 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36125 * @param {String} path
36126 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36127 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36128 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36130 expandPath : function(path, attr, callback){
36131 attr = attr || "id";
36132 var keys = path.split(this.pathSeparator);
36133 var curNode = this.root;
36134 if(curNode.attributes[attr] != keys[1]){ // invalid root
36136 callback(false, null);
36141 var f = function(){
36142 if(++index == keys.length){
36144 callback(true, curNode);
36148 var c = curNode.findChild(attr, keys[index]);
36151 callback(false, curNode);
36156 c.expand(false, false, f);
36158 curNode.expand(false, false, f);
36162 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36163 * @param {String} path
36164 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36165 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36166 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36168 selectPath : function(path, attr, callback){
36169 attr = attr || "id";
36170 var keys = path.split(this.pathSeparator);
36171 var v = keys.pop();
36172 if(keys.length > 0){
36173 var f = function(success, node){
36174 if(success && node){
36175 var n = node.findChild(attr, v);
36181 }else if(callback){
36182 callback(false, n);
36186 callback(false, n);
36190 this.expandPath(keys.join(this.pathSeparator), attr, f);
36192 this.root.select();
36194 callback(true, this.root);
36199 getTreeEl : function(){
36204 * Trigger rendering of this TreePanel
36206 render : function(){
36207 if (this.innerCt) {
36208 return this; // stop it rendering more than once!!
36211 this.innerCt = this.el.createChild({tag:"ul",
36212 cls:"x-tree-root-ct " +
36213 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36215 if(this.containerScroll){
36216 Roo.dd.ScrollManager.register(this.el);
36218 if((this.enableDD || this.enableDrop) && !this.dropZone){
36220 * The dropZone used by this tree if drop is enabled
36221 * @type Roo.tree.TreeDropZone
36223 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36224 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36227 if((this.enableDD || this.enableDrag) && !this.dragZone){
36229 * The dragZone used by this tree if drag is enabled
36230 * @type Roo.tree.TreeDragZone
36232 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36233 ddGroup: this.ddGroup || "TreeDD",
36234 scroll: this.ddScroll
36237 this.getSelectionModel().init(this);
36239 Roo.log("ROOT not set in tree");
36242 this.root.render();
36243 if(!this.rootVisible){
36244 this.root.renderChildren();
36250 * Ext JS Library 1.1.1
36251 * Copyright(c) 2006-2007, Ext JS, LLC.
36253 * Originally Released Under LGPL - original licence link has changed is not relivant.
36256 * <script type="text/javascript">
36261 * @class Roo.tree.DefaultSelectionModel
36262 * @extends Roo.util.Observable
36263 * The default single selection for a TreePanel.
36264 * @param {Object} cfg Configuration
36266 Roo.tree.DefaultSelectionModel = function(cfg){
36267 this.selNode = null;
36273 * @event selectionchange
36274 * Fires when the selected node changes
36275 * @param {DefaultSelectionModel} this
36276 * @param {TreeNode} node the new selection
36278 "selectionchange" : true,
36281 * @event beforeselect
36282 * Fires before the selected node changes, return false to cancel the change
36283 * @param {DefaultSelectionModel} this
36284 * @param {TreeNode} node the new selection
36285 * @param {TreeNode} node the old selection
36287 "beforeselect" : true
36290 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36293 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36294 init : function(tree){
36296 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36297 tree.on("click", this.onNodeClick, this);
36300 onNodeClick : function(node, e){
36301 if (e.ctrlKey && this.selNode == node) {
36302 this.unselect(node);
36310 * @param {TreeNode} node The node to select
36311 * @return {TreeNode} The selected node
36313 select : function(node){
36314 var last = this.selNode;
36315 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36317 last.ui.onSelectedChange(false);
36319 this.selNode = node;
36320 node.ui.onSelectedChange(true);
36321 this.fireEvent("selectionchange", this, node, last);
36328 * @param {TreeNode} node The node to unselect
36330 unselect : function(node){
36331 if(this.selNode == node){
36332 this.clearSelections();
36337 * Clear all selections
36339 clearSelections : function(){
36340 var n = this.selNode;
36342 n.ui.onSelectedChange(false);
36343 this.selNode = null;
36344 this.fireEvent("selectionchange", this, null);
36350 * Get the selected node
36351 * @return {TreeNode} The selected node
36353 getSelectedNode : function(){
36354 return this.selNode;
36358 * Returns true if the node is selected
36359 * @param {TreeNode} node The node to check
36360 * @return {Boolean}
36362 isSelected : function(node){
36363 return this.selNode == node;
36367 * Selects the node above the selected node in the tree, intelligently walking the nodes
36368 * @return TreeNode The new selection
36370 selectPrevious : function(){
36371 var s = this.selNode || this.lastSelNode;
36375 var ps = s.previousSibling;
36377 if(!ps.isExpanded() || ps.childNodes.length < 1){
36378 return this.select(ps);
36380 var lc = ps.lastChild;
36381 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36384 return this.select(lc);
36386 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36387 return this.select(s.parentNode);
36393 * Selects the node above the selected node in the tree, intelligently walking the nodes
36394 * @return TreeNode The new selection
36396 selectNext : function(){
36397 var s = this.selNode || this.lastSelNode;
36401 if(s.firstChild && s.isExpanded()){
36402 return this.select(s.firstChild);
36403 }else if(s.nextSibling){
36404 return this.select(s.nextSibling);
36405 }else if(s.parentNode){
36407 s.parentNode.bubble(function(){
36408 if(this.nextSibling){
36409 newS = this.getOwnerTree().selModel.select(this.nextSibling);
36418 onKeyDown : function(e){
36419 var s = this.selNode || this.lastSelNode;
36420 // undesirable, but required
36425 var k = e.getKey();
36433 this.selectPrevious();
36436 e.preventDefault();
36437 if(s.hasChildNodes()){
36438 if(!s.isExpanded()){
36440 }else if(s.firstChild){
36441 this.select(s.firstChild, e);
36446 e.preventDefault();
36447 if(s.hasChildNodes() && s.isExpanded()){
36449 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36450 this.select(s.parentNode, e);
36458 * @class Roo.tree.MultiSelectionModel
36459 * @extends Roo.util.Observable
36460 * Multi selection for a TreePanel.
36461 * @param {Object} cfg Configuration
36463 Roo.tree.MultiSelectionModel = function(){
36464 this.selNodes = [];
36468 * @event selectionchange
36469 * Fires when the selected nodes change
36470 * @param {MultiSelectionModel} this
36471 * @param {Array} nodes Array of the selected nodes
36473 "selectionchange" : true
36475 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36479 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36480 init : function(tree){
36482 tree.getTreeEl().on("keydown", this.onKeyDown, this);
36483 tree.on("click", this.onNodeClick, this);
36486 onNodeClick : function(node, e){
36487 this.select(node, e, e.ctrlKey);
36492 * @param {TreeNode} node The node to select
36493 * @param {EventObject} e (optional) An event associated with the selection
36494 * @param {Boolean} keepExisting True to retain existing selections
36495 * @return {TreeNode} The selected node
36497 select : function(node, e, keepExisting){
36498 if(keepExisting !== true){
36499 this.clearSelections(true);
36501 if(this.isSelected(node)){
36502 this.lastSelNode = node;
36505 this.selNodes.push(node);
36506 this.selMap[node.id] = node;
36507 this.lastSelNode = node;
36508 node.ui.onSelectedChange(true);
36509 this.fireEvent("selectionchange", this, this.selNodes);
36515 * @param {TreeNode} node The node to unselect
36517 unselect : function(node){
36518 if(this.selMap[node.id]){
36519 node.ui.onSelectedChange(false);
36520 var sn = this.selNodes;
36523 index = sn.indexOf(node);
36525 for(var i = 0, len = sn.length; i < len; i++){
36533 this.selNodes.splice(index, 1);
36535 delete this.selMap[node.id];
36536 this.fireEvent("selectionchange", this, this.selNodes);
36541 * Clear all selections
36543 clearSelections : function(suppressEvent){
36544 var sn = this.selNodes;
36546 for(var i = 0, len = sn.length; i < len; i++){
36547 sn[i].ui.onSelectedChange(false);
36549 this.selNodes = [];
36551 if(suppressEvent !== true){
36552 this.fireEvent("selectionchange", this, this.selNodes);
36558 * Returns true if the node is selected
36559 * @param {TreeNode} node The node to check
36560 * @return {Boolean}
36562 isSelected : function(node){
36563 return this.selMap[node.id] ? true : false;
36567 * Returns an array of the selected nodes
36570 getSelectedNodes : function(){
36571 return this.selNodes;
36574 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36576 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36578 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36581 * Ext JS Library 1.1.1
36582 * Copyright(c) 2006-2007, Ext JS, LLC.
36584 * Originally Released Under LGPL - original licence link has changed is not relivant.
36587 * <script type="text/javascript">
36591 * @class Roo.tree.TreeNode
36592 * @extends Roo.data.Node
36593 * @cfg {String} text The text for this node
36594 * @cfg {Boolean} expanded true to start the node expanded
36595 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36596 * @cfg {Boolean} allowDrop false if this node cannot be drop on
36597 * @cfg {Boolean} disabled true to start the node disabled
36598 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36599 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
36600 * @cfg {String} cls A css class to be added to the node
36601 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36602 * @cfg {String} href URL of the link used for the node (defaults to #)
36603 * @cfg {String} hrefTarget target frame for the link
36604 * @cfg {String} qtip An Ext QuickTip for the node
36605 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36606 * @cfg {Boolean} singleClickExpand True for single click expand on this node
36607 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36608 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36609 * (defaults to undefined with no checkbox rendered)
36611 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36613 Roo.tree.TreeNode = function(attributes){
36614 attributes = attributes || {};
36615 if(typeof attributes == "string"){
36616 attributes = {text: attributes};
36618 this.childrenRendered = false;
36619 this.rendered = false;
36620 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36621 this.expanded = attributes.expanded === true;
36622 this.isTarget = attributes.isTarget !== false;
36623 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36624 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36627 * Read-only. The text for this node. To change it use setText().
36630 this.text = attributes.text;
36632 * True if this node is disabled.
36635 this.disabled = attributes.disabled === true;
36639 * @event textchange
36640 * Fires when the text for this node is changed
36641 * @param {Node} this This node
36642 * @param {String} text The new text
36643 * @param {String} oldText The old text
36645 "textchange" : true,
36647 * @event beforeexpand
36648 * Fires before this node is expanded, return false to cancel.
36649 * @param {Node} this This node
36650 * @param {Boolean} deep
36651 * @param {Boolean} anim
36653 "beforeexpand" : true,
36655 * @event beforecollapse
36656 * Fires before this node is collapsed, return false to cancel.
36657 * @param {Node} this This node
36658 * @param {Boolean} deep
36659 * @param {Boolean} anim
36661 "beforecollapse" : true,
36664 * Fires when this node is expanded
36665 * @param {Node} this This node
36669 * @event disabledchange
36670 * Fires when the disabled status of this node changes
36671 * @param {Node} this This node
36672 * @param {Boolean} disabled
36674 "disabledchange" : true,
36677 * Fires when this node is collapsed
36678 * @param {Node} this This node
36682 * @event beforeclick
36683 * Fires before click processing. Return false to cancel the default action.
36684 * @param {Node} this This node
36685 * @param {Roo.EventObject} e The event object
36687 "beforeclick":true,
36689 * @event checkchange
36690 * Fires when a node with a checkbox's checked property changes
36691 * @param {Node} this This node
36692 * @param {Boolean} checked
36694 "checkchange":true,
36697 * Fires when this node is clicked
36698 * @param {Node} this This node
36699 * @param {Roo.EventObject} e The event object
36704 * Fires when this node is double clicked
36705 * @param {Node} this This node
36706 * @param {Roo.EventObject} e The event object
36710 * @event contextmenu
36711 * Fires when this node is right clicked
36712 * @param {Node} this This node
36713 * @param {Roo.EventObject} e The event object
36715 "contextmenu":true,
36717 * @event beforechildrenrendered
36718 * Fires right before the child nodes for this node are rendered
36719 * @param {Node} this This node
36721 "beforechildrenrendered":true
36724 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36727 * Read-only. The UI for this node
36730 this.ui = new uiClass(this);
36732 // finally support items[]
36733 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36738 Roo.each(this.attributes.items, function(c) {
36739 this.appendChild(Roo.factory(c,Roo.Tree));
36741 delete this.attributes.items;
36746 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36747 preventHScroll: true,
36749 * Returns true if this node is expanded
36750 * @return {Boolean}
36752 isExpanded : function(){
36753 return this.expanded;
36757 * Returns the UI object for this node
36758 * @return {TreeNodeUI}
36760 getUI : function(){
36764 // private override
36765 setFirstChild : function(node){
36766 var of = this.firstChild;
36767 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36768 if(this.childrenRendered && of && node != of){
36769 of.renderIndent(true, true);
36772 this.renderIndent(true, true);
36776 // private override
36777 setLastChild : function(node){
36778 var ol = this.lastChild;
36779 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36780 if(this.childrenRendered && ol && node != ol){
36781 ol.renderIndent(true, true);
36784 this.renderIndent(true, true);
36788 // these methods are overridden to provide lazy rendering support
36789 // private override
36790 appendChild : function()
36792 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36793 if(node && this.childrenRendered){
36796 this.ui.updateExpandIcon();
36800 // private override
36801 removeChild : function(node){
36802 this.ownerTree.getSelectionModel().unselect(node);
36803 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36804 // if it's been rendered remove dom node
36805 if(this.childrenRendered){
36808 if(this.childNodes.length < 1){
36809 this.collapse(false, false);
36811 this.ui.updateExpandIcon();
36813 if(!this.firstChild) {
36814 this.childrenRendered = false;
36819 // private override
36820 insertBefore : function(node, refNode){
36821 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36822 if(newNode && refNode && this.childrenRendered){
36825 this.ui.updateExpandIcon();
36830 * Sets the text for this node
36831 * @param {String} text
36833 setText : function(text){
36834 var oldText = this.text;
36836 this.attributes.text = text;
36837 if(this.rendered){ // event without subscribing
36838 this.ui.onTextChange(this, text, oldText);
36840 this.fireEvent("textchange", this, text, oldText);
36844 * Triggers selection of this node
36846 select : function(){
36847 this.getOwnerTree().getSelectionModel().select(this);
36851 * Triggers deselection of this node
36853 unselect : function(){
36854 this.getOwnerTree().getSelectionModel().unselect(this);
36858 * Returns true if this node is selected
36859 * @return {Boolean}
36861 isSelected : function(){
36862 return this.getOwnerTree().getSelectionModel().isSelected(this);
36866 * Expand this node.
36867 * @param {Boolean} deep (optional) True to expand all children as well
36868 * @param {Boolean} anim (optional) false to cancel the default animation
36869 * @param {Function} callback (optional) A callback to be called when
36870 * expanding this node completes (does not wait for deep expand to complete).
36871 * Called with 1 parameter, this node.
36873 expand : function(deep, anim, callback){
36874 if(!this.expanded){
36875 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36878 if(!this.childrenRendered){
36879 this.renderChildren();
36881 this.expanded = true;
36883 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36884 this.ui.animExpand(function(){
36885 this.fireEvent("expand", this);
36886 if(typeof callback == "function"){
36890 this.expandChildNodes(true);
36892 }.createDelegate(this));
36896 this.fireEvent("expand", this);
36897 if(typeof callback == "function"){
36902 if(typeof callback == "function"){
36907 this.expandChildNodes(true);
36911 isHiddenRoot : function(){
36912 return this.isRoot && !this.getOwnerTree().rootVisible;
36916 * Collapse this node.
36917 * @param {Boolean} deep (optional) True to collapse all children as well
36918 * @param {Boolean} anim (optional) false to cancel the default animation
36920 collapse : function(deep, anim){
36921 if(this.expanded && !this.isHiddenRoot()){
36922 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36925 this.expanded = false;
36926 if((this.getOwnerTree().animate && anim !== false) || anim){
36927 this.ui.animCollapse(function(){
36928 this.fireEvent("collapse", this);
36930 this.collapseChildNodes(true);
36932 }.createDelegate(this));
36935 this.ui.collapse();
36936 this.fireEvent("collapse", this);
36940 var cs = this.childNodes;
36941 for(var i = 0, len = cs.length; i < len; i++) {
36942 cs[i].collapse(true, false);
36948 delayedExpand : function(delay){
36949 if(!this.expandProcId){
36950 this.expandProcId = this.expand.defer(delay, this);
36955 cancelExpand : function(){
36956 if(this.expandProcId){
36957 clearTimeout(this.expandProcId);
36959 this.expandProcId = false;
36963 * Toggles expanded/collapsed state of the node
36965 toggle : function(){
36974 * Ensures all parent nodes are expanded
36976 ensureVisible : function(callback){
36977 var tree = this.getOwnerTree();
36978 tree.expandPath(this.parentNode.getPath(), false, function(){
36979 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36980 Roo.callback(callback);
36981 }.createDelegate(this));
36985 * Expand all child nodes
36986 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36988 expandChildNodes : function(deep){
36989 var cs = this.childNodes;
36990 for(var i = 0, len = cs.length; i < len; i++) {
36991 cs[i].expand(deep);
36996 * Collapse all child nodes
36997 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36999 collapseChildNodes : function(deep){
37000 var cs = this.childNodes;
37001 for(var i = 0, len = cs.length; i < len; i++) {
37002 cs[i].collapse(deep);
37007 * Disables this node
37009 disable : function(){
37010 this.disabled = true;
37012 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37013 this.ui.onDisableChange(this, true);
37015 this.fireEvent("disabledchange", this, true);
37019 * Enables this node
37021 enable : function(){
37022 this.disabled = false;
37023 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37024 this.ui.onDisableChange(this, false);
37026 this.fireEvent("disabledchange", this, false);
37030 renderChildren : function(suppressEvent){
37031 if(suppressEvent !== false){
37032 this.fireEvent("beforechildrenrendered", this);
37034 var cs = this.childNodes;
37035 for(var i = 0, len = cs.length; i < len; i++){
37036 cs[i].render(true);
37038 this.childrenRendered = true;
37042 sort : function(fn, scope){
37043 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37044 if(this.childrenRendered){
37045 var cs = this.childNodes;
37046 for(var i = 0, len = cs.length; i < len; i++){
37047 cs[i].render(true);
37053 render : function(bulkRender){
37054 this.ui.render(bulkRender);
37055 if(!this.rendered){
37056 this.rendered = true;
37058 this.expanded = false;
37059 this.expand(false, false);
37065 renderIndent : function(deep, refresh){
37067 this.ui.childIndent = null;
37069 this.ui.renderIndent();
37070 if(deep === true && this.childrenRendered){
37071 var cs = this.childNodes;
37072 for(var i = 0, len = cs.length; i < len; i++){
37073 cs[i].renderIndent(true, refresh);
37079 * Ext JS Library 1.1.1
37080 * Copyright(c) 2006-2007, Ext JS, LLC.
37082 * Originally Released Under LGPL - original licence link has changed is not relivant.
37085 * <script type="text/javascript">
37089 * @class Roo.tree.AsyncTreeNode
37090 * @extends Roo.tree.TreeNode
37091 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37093 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
37095 Roo.tree.AsyncTreeNode = function(config){
37096 this.loaded = false;
37097 this.loading = false;
37098 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37100 * @event beforeload
37101 * Fires before this node is loaded, return false to cancel
37102 * @param {Node} this This node
37104 this.addEvents({'beforeload':true, 'load': true});
37107 * Fires when this node is loaded
37108 * @param {Node} this This node
37111 * The loader used by this node (defaults to using the tree's defined loader)
37116 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37117 expand : function(deep, anim, callback){
37118 if(this.loading){ // if an async load is already running, waiting til it's done
37120 var f = function(){
37121 if(!this.loading){ // done loading
37122 clearInterval(timer);
37123 this.expand(deep, anim, callback);
37125 }.createDelegate(this);
37126 timer = setInterval(f, 200);
37130 if(this.fireEvent("beforeload", this) === false){
37133 this.loading = true;
37134 this.ui.beforeLoad(this);
37135 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37137 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37141 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37145 * Returns true if this node is currently loading
37146 * @return {Boolean}
37148 isLoading : function(){
37149 return this.loading;
37152 loadComplete : function(deep, anim, callback){
37153 this.loading = false;
37154 this.loaded = true;
37155 this.ui.afterLoad(this);
37156 this.fireEvent("load", this);
37157 this.expand(deep, anim, callback);
37161 * Returns true if this node has been loaded
37162 * @return {Boolean}
37164 isLoaded : function(){
37165 return this.loaded;
37168 hasChildNodes : function(){
37169 if(!this.isLeaf() && !this.loaded){
37172 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37177 * Trigger a reload for this node
37178 * @param {Function} callback
37180 reload : function(callback){
37181 this.collapse(false, false);
37182 while(this.firstChild){
37183 this.removeChild(this.firstChild);
37185 this.childrenRendered = false;
37186 this.loaded = false;
37187 if(this.isHiddenRoot()){
37188 this.expanded = false;
37190 this.expand(false, false, callback);
37194 * Ext JS Library 1.1.1
37195 * Copyright(c) 2006-2007, Ext JS, LLC.
37197 * Originally Released Under LGPL - original licence link has changed is not relivant.
37200 * <script type="text/javascript">
37204 * @class Roo.tree.TreeNodeUI
37206 * @param {Object} node The node to render
37207 * The TreeNode UI implementation is separate from the
37208 * tree implementation. Unless you are customizing the tree UI,
37209 * you should never have to use this directly.
37211 Roo.tree.TreeNodeUI = function(node){
37213 this.rendered = false;
37214 this.animating = false;
37215 this.emptyIcon = Roo.BLANK_IMAGE_URL;
37218 Roo.tree.TreeNodeUI.prototype = {
37219 removeChild : function(node){
37221 this.ctNode.removeChild(node.ui.getEl());
37225 beforeLoad : function(){
37226 this.addClass("x-tree-node-loading");
37229 afterLoad : function(){
37230 this.removeClass("x-tree-node-loading");
37233 onTextChange : function(node, text, oldText){
37235 this.textNode.innerHTML = text;
37239 onDisableChange : function(node, state){
37240 this.disabled = state;
37242 this.addClass("x-tree-node-disabled");
37244 this.removeClass("x-tree-node-disabled");
37248 onSelectedChange : function(state){
37251 this.addClass("x-tree-selected");
37254 this.removeClass("x-tree-selected");
37258 onMove : function(tree, node, oldParent, newParent, index, refNode){
37259 this.childIndent = null;
37261 var targetNode = newParent.ui.getContainer();
37262 if(!targetNode){//target not rendered
37263 this.holder = document.createElement("div");
37264 this.holder.appendChild(this.wrap);
37267 var insertBefore = refNode ? refNode.ui.getEl() : null;
37269 targetNode.insertBefore(this.wrap, insertBefore);
37271 targetNode.appendChild(this.wrap);
37273 this.node.renderIndent(true);
37277 addClass : function(cls){
37279 Roo.fly(this.elNode).addClass(cls);
37283 removeClass : function(cls){
37285 Roo.fly(this.elNode).removeClass(cls);
37289 remove : function(){
37291 this.holder = document.createElement("div");
37292 this.holder.appendChild(this.wrap);
37296 fireEvent : function(){
37297 return this.node.fireEvent.apply(this.node, arguments);
37300 initEvents : function(){
37301 this.node.on("move", this.onMove, this);
37302 var E = Roo.EventManager;
37303 var a = this.anchor;
37305 var el = Roo.fly(a, '_treeui');
37307 if(Roo.isOpera){ // opera render bug ignores the CSS
37308 el.setStyle("text-decoration", "none");
37311 el.on("click", this.onClick, this);
37312 el.on("dblclick", this.onDblClick, this);
37315 Roo.EventManager.on(this.checkbox,
37316 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37319 el.on("contextmenu", this.onContextMenu, this);
37321 var icon = Roo.fly(this.iconNode);
37322 icon.on("click", this.onClick, this);
37323 icon.on("dblclick", this.onDblClick, this);
37324 icon.on("contextmenu", this.onContextMenu, this);
37325 E.on(this.ecNode, "click", this.ecClick, this, true);
37327 if(this.node.disabled){
37328 this.addClass("x-tree-node-disabled");
37330 if(this.node.hidden){
37331 this.addClass("x-tree-node-disabled");
37333 var ot = this.node.getOwnerTree();
37334 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37335 if(dd && (!this.node.isRoot || ot.rootVisible)){
37336 Roo.dd.Registry.register(this.elNode, {
37338 handles: this.getDDHandles(),
37344 getDDHandles : function(){
37345 return [this.iconNode, this.textNode];
37350 this.wrap.style.display = "none";
37356 this.wrap.style.display = "";
37360 onContextMenu : function(e){
37361 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37362 e.preventDefault();
37364 this.fireEvent("contextmenu", this.node, e);
37368 onClick : function(e){
37373 if(this.fireEvent("beforeclick", this.node, e) !== false){
37374 if(!this.disabled && this.node.attributes.href){
37375 this.fireEvent("click", this.node, e);
37378 e.preventDefault();
37383 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37384 this.node.toggle();
37387 this.fireEvent("click", this.node, e);
37393 onDblClick : function(e){
37394 e.preventDefault();
37399 this.toggleCheck();
37401 if(!this.animating && this.node.hasChildNodes()){
37402 this.node.toggle();
37404 this.fireEvent("dblclick", this.node, e);
37407 onCheckChange : function(){
37408 var checked = this.checkbox.checked;
37409 this.node.attributes.checked = checked;
37410 this.fireEvent('checkchange', this.node, checked);
37413 ecClick : function(e){
37414 if(!this.animating && this.node.hasChildNodes()){
37415 this.node.toggle();
37419 startDrop : function(){
37420 this.dropping = true;
37423 // delayed drop so the click event doesn't get fired on a drop
37424 endDrop : function(){
37425 setTimeout(function(){
37426 this.dropping = false;
37427 }.createDelegate(this), 50);
37430 expand : function(){
37431 this.updateExpandIcon();
37432 this.ctNode.style.display = "";
37435 focus : function(){
37436 if(!this.node.preventHScroll){
37437 try{this.anchor.focus();
37439 }else if(!Roo.isIE){
37441 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37442 var l = noscroll.scrollLeft;
37443 this.anchor.focus();
37444 noscroll.scrollLeft = l;
37449 toggleCheck : function(value){
37450 var cb = this.checkbox;
37452 cb.checked = (value === undefined ? !cb.checked : value);
37458 this.anchor.blur();
37462 animExpand : function(callback){
37463 var ct = Roo.get(this.ctNode);
37465 if(!this.node.hasChildNodes()){
37466 this.updateExpandIcon();
37467 this.ctNode.style.display = "";
37468 Roo.callback(callback);
37471 this.animating = true;
37472 this.updateExpandIcon();
37475 callback : function(){
37476 this.animating = false;
37477 Roo.callback(callback);
37480 duration: this.node.ownerTree.duration || .25
37484 highlight : function(){
37485 var tree = this.node.getOwnerTree();
37486 Roo.fly(this.wrap).highlight(
37487 tree.hlColor || "C3DAF9",
37488 {endColor: tree.hlBaseColor}
37492 collapse : function(){
37493 this.updateExpandIcon();
37494 this.ctNode.style.display = "none";
37497 animCollapse : function(callback){
37498 var ct = Roo.get(this.ctNode);
37499 ct.enableDisplayMode('block');
37502 this.animating = true;
37503 this.updateExpandIcon();
37506 callback : function(){
37507 this.animating = false;
37508 Roo.callback(callback);
37511 duration: this.node.ownerTree.duration || .25
37515 getContainer : function(){
37516 return this.ctNode;
37519 getEl : function(){
37523 appendDDGhost : function(ghostNode){
37524 ghostNode.appendChild(this.elNode.cloneNode(true));
37527 getDDRepairXY : function(){
37528 return Roo.lib.Dom.getXY(this.iconNode);
37531 onRender : function(){
37535 render : function(bulkRender){
37536 var n = this.node, a = n.attributes;
37537 var targetNode = n.parentNode ?
37538 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37540 if(!this.rendered){
37541 this.rendered = true;
37543 this.renderElements(n, a, targetNode, bulkRender);
37546 if(this.textNode.setAttributeNS){
37547 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37549 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37552 this.textNode.setAttribute("ext:qtip", a.qtip);
37554 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37557 }else if(a.qtipCfg){
37558 a.qtipCfg.target = Roo.id(this.textNode);
37559 Roo.QuickTips.register(a.qtipCfg);
37562 if(!this.node.expanded){
37563 this.updateExpandIcon();
37566 if(bulkRender === true) {
37567 targetNode.appendChild(this.wrap);
37572 renderElements : function(n, a, targetNode, bulkRender)
37574 // add some indent caching, this helps performance when rendering a large tree
37575 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37576 var t = n.getOwnerTree();
37577 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37578 if (typeof(n.attributes.html) != 'undefined') {
37579 txt = n.attributes.html;
37581 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37582 var cb = typeof a.checked == 'boolean';
37583 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37584 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37585 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37586 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37587 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37588 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37589 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37590 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
37591 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37592 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37595 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37596 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37597 n.nextSibling.ui.getEl(), buf.join(""));
37599 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37602 this.elNode = this.wrap.childNodes[0];
37603 this.ctNode = this.wrap.childNodes[1];
37604 var cs = this.elNode.childNodes;
37605 this.indentNode = cs[0];
37606 this.ecNode = cs[1];
37607 this.iconNode = cs[2];
37610 this.checkbox = cs[3];
37613 this.anchor = cs[index];
37614 this.textNode = cs[index].firstChild;
37617 getAnchor : function(){
37618 return this.anchor;
37621 getTextEl : function(){
37622 return this.textNode;
37625 getIconEl : function(){
37626 return this.iconNode;
37629 isChecked : function(){
37630 return this.checkbox ? this.checkbox.checked : false;
37633 updateExpandIcon : function(){
37635 var n = this.node, c1, c2;
37636 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37637 var hasChild = n.hasChildNodes();
37641 c1 = "x-tree-node-collapsed";
37642 c2 = "x-tree-node-expanded";
37645 c1 = "x-tree-node-expanded";
37646 c2 = "x-tree-node-collapsed";
37649 this.removeClass("x-tree-node-leaf");
37650 this.wasLeaf = false;
37652 if(this.c1 != c1 || this.c2 != c2){
37653 Roo.fly(this.elNode).replaceClass(c1, c2);
37654 this.c1 = c1; this.c2 = c2;
37657 // this changes non-leafs into leafs if they have no children.
37658 // it's not very rational behaviour..
37660 if(!this.wasLeaf && this.node.leaf){
37661 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37664 this.wasLeaf = true;
37667 var ecc = "x-tree-ec-icon "+cls;
37668 if(this.ecc != ecc){
37669 this.ecNode.className = ecc;
37675 getChildIndent : function(){
37676 if(!this.childIndent){
37680 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37682 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37684 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37689 this.childIndent = buf.join("");
37691 return this.childIndent;
37694 renderIndent : function(){
37697 var p = this.node.parentNode;
37699 indent = p.ui.getChildIndent();
37701 if(this.indentMarkup != indent){ // don't rerender if not required
37702 this.indentNode.innerHTML = indent;
37703 this.indentMarkup = indent;
37705 this.updateExpandIcon();
37710 Roo.tree.RootTreeNodeUI = function(){
37711 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37713 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37714 render : function(){
37715 if(!this.rendered){
37716 var targetNode = this.node.ownerTree.innerCt.dom;
37717 this.node.expanded = true;
37718 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37719 this.wrap = this.ctNode = targetNode.firstChild;
37722 collapse : function(){
37724 expand : function(){
37728 * Ext JS Library 1.1.1
37729 * Copyright(c) 2006-2007, Ext JS, LLC.
37731 * Originally Released Under LGPL - original licence link has changed is not relivant.
37734 * <script type="text/javascript">
37737 * @class Roo.tree.TreeLoader
37738 * @extends Roo.util.Observable
37739 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37740 * nodes from a specified URL. The response must be a javascript Array definition
37741 * who's elements are node definition objects. eg:
37746 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37747 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37754 * The old style respose with just an array is still supported, but not recommended.
37757 * A server request is sent, and child nodes are loaded only when a node is expanded.
37758 * The loading node's id is passed to the server under the parameter name "node" to
37759 * enable the server to produce the correct child nodes.
37761 * To pass extra parameters, an event handler may be attached to the "beforeload"
37762 * event, and the parameters specified in the TreeLoader's baseParams property:
37764 myTreeLoader.on("beforeload", function(treeLoader, node) {
37765 this.baseParams.category = node.attributes.category;
37770 * This would pass an HTTP parameter called "category" to the server containing
37771 * the value of the Node's "category" attribute.
37773 * Creates a new Treeloader.
37774 * @param {Object} config A config object containing config properties.
37776 Roo.tree.TreeLoader = function(config){
37777 this.baseParams = {};
37778 this.requestMethod = "POST";
37779 Roo.apply(this, config);
37784 * @event beforeload
37785 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37786 * @param {Object} This TreeLoader object.
37787 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37788 * @param {Object} callback The callback function specified in the {@link #load} call.
37793 * Fires when the node has been successfuly loaded.
37794 * @param {Object} This TreeLoader object.
37795 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37796 * @param {Object} response The response object containing the data from the server.
37800 * @event loadexception
37801 * Fires if the network request failed.
37802 * @param {Object} This TreeLoader object.
37803 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37804 * @param {Object} response The response object containing the data from the server.
37806 loadexception : true,
37809 * Fires before a node is created, enabling you to return custom Node types
37810 * @param {Object} This TreeLoader object.
37811 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37816 Roo.tree.TreeLoader.superclass.constructor.call(this);
37819 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37821 * @cfg {String} dataUrl The URL from which to request a Json string which
37822 * specifies an array of node definition object representing the child nodes
37826 * @cfg {String} requestMethod either GET or POST
37827 * defaults to POST (due to BC)
37831 * @cfg {Object} baseParams (optional) An object containing properties which
37832 * specify HTTP parameters to be passed to each request for child nodes.
37835 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37836 * created by this loader. If the attributes sent by the server have an attribute in this object,
37837 * they take priority.
37840 * @cfg {Object} uiProviders (optional) An object containing properties which
37842 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37843 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37844 * <i>uiProvider</i> attribute of a returned child node is a string rather
37845 * than a reference to a TreeNodeUI implementation, this that string value
37846 * is used as a property name in the uiProviders object. You can define the provider named
37847 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37852 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37853 * child nodes before loading.
37855 clearOnLoad : true,
37858 * @cfg {String} root (optional) Default to false. Use this to read data from an object
37859 * property on loading, rather than expecting an array. (eg. more compatible to a standard
37860 * Grid query { data : [ .....] }
37865 * @cfg {String} queryParam (optional)
37866 * Name of the query as it will be passed on the querystring (defaults to 'node')
37867 * eg. the request will be ?node=[id]
37874 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37875 * This is called automatically when a node is expanded, but may be used to reload
37876 * a node (or append new children if the {@link #clearOnLoad} option is false.)
37877 * @param {Roo.tree.TreeNode} node
37878 * @param {Function} callback
37880 load : function(node, callback){
37881 if(this.clearOnLoad){
37882 while(node.firstChild){
37883 node.removeChild(node.firstChild);
37886 if(node.attributes.children){ // preloaded json children
37887 var cs = node.attributes.children;
37888 for(var i = 0, len = cs.length; i < len; i++){
37889 node.appendChild(this.createNode(cs[i]));
37891 if(typeof callback == "function"){
37894 }else if(this.dataUrl){
37895 this.requestData(node, callback);
37899 getParams: function(node){
37900 var buf = [], bp = this.baseParams;
37901 for(var key in bp){
37902 if(typeof bp[key] != "function"){
37903 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37906 var n = this.queryParam === false ? 'node' : this.queryParam;
37907 buf.push(n + "=", encodeURIComponent(node.id));
37908 return buf.join("");
37911 requestData : function(node, callback){
37912 if(this.fireEvent("beforeload", this, node, callback) !== false){
37913 this.transId = Roo.Ajax.request({
37914 method:this.requestMethod,
37915 url: this.dataUrl||this.url,
37916 success: this.handleResponse,
37917 failure: this.handleFailure,
37919 argument: {callback: callback, node: node},
37920 params: this.getParams(node)
37923 // if the load is cancelled, make sure we notify
37924 // the node that we are done
37925 if(typeof callback == "function"){
37931 isLoading : function(){
37932 return this.transId ? true : false;
37935 abort : function(){
37936 if(this.isLoading()){
37937 Roo.Ajax.abort(this.transId);
37942 createNode : function(attr)
37944 // apply baseAttrs, nice idea Corey!
37945 if(this.baseAttrs){
37946 Roo.applyIf(attr, this.baseAttrs);
37948 if(this.applyLoader !== false){
37949 attr.loader = this;
37951 // uiProvider = depreciated..
37953 if(typeof(attr.uiProvider) == 'string'){
37954 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
37955 /** eval:var:attr */ eval(attr.uiProvider);
37957 if(typeof(this.uiProviders['default']) != 'undefined') {
37958 attr.uiProvider = this.uiProviders['default'];
37961 this.fireEvent('create', this, attr);
37963 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37965 new Roo.tree.TreeNode(attr) :
37966 new Roo.tree.AsyncTreeNode(attr));
37969 processResponse : function(response, node, callback)
37971 var json = response.responseText;
37974 var o = Roo.decode(json);
37976 if (this.root === false && typeof(o.success) != undefined) {
37977 this.root = 'data'; // the default behaviour for list like data..
37980 if (this.root !== false && !o.success) {
37981 // it's a failure condition.
37982 var a = response.argument;
37983 this.fireEvent("loadexception", this, a.node, response);
37984 Roo.log("Load failed - should have a handler really");
37990 if (this.root !== false) {
37994 for(var i = 0, len = o.length; i < len; i++){
37995 var n = this.createNode(o[i]);
37997 node.appendChild(n);
38000 if(typeof callback == "function"){
38001 callback(this, node);
38004 this.handleFailure(response);
38008 handleResponse : function(response){
38009 this.transId = false;
38010 var a = response.argument;
38011 this.processResponse(response, a.node, a.callback);
38012 this.fireEvent("load", this, a.node, response);
38015 handleFailure : function(response)
38017 // should handle failure better..
38018 this.transId = false;
38019 var a = response.argument;
38020 this.fireEvent("loadexception", this, a.node, response);
38021 if(typeof a.callback == "function"){
38022 a.callback(this, a.node);
38027 * Ext JS Library 1.1.1
38028 * Copyright(c) 2006-2007, Ext JS, LLC.
38030 * Originally Released Under LGPL - original licence link has changed is not relivant.
38033 * <script type="text/javascript">
38037 * @class Roo.tree.TreeFilter
38038 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38039 * @param {TreePanel} tree
38040 * @param {Object} config (optional)
38042 Roo.tree.TreeFilter = function(tree, config){
38044 this.filtered = {};
38045 Roo.apply(this, config);
38048 Roo.tree.TreeFilter.prototype = {
38055 * Filter the data by a specific attribute.
38056 * @param {String/RegExp} value Either string that the attribute value
38057 * should start with or a RegExp to test against the attribute
38058 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38059 * @param {TreeNode} startNode (optional) The node to start the filter at.
38061 filter : function(value, attr, startNode){
38062 attr = attr || "text";
38064 if(typeof value == "string"){
38065 var vlen = value.length;
38066 // auto clear empty filter
38067 if(vlen == 0 && this.clearBlank){
38071 value = value.toLowerCase();
38073 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38075 }else if(value.exec){ // regex?
38077 return value.test(n.attributes[attr]);
38080 throw 'Illegal filter type, must be string or regex';
38082 this.filterBy(f, null, startNode);
38086 * Filter by a function. The passed function will be called with each
38087 * node in the tree (or from the startNode). If the function returns true, the node is kept
38088 * otherwise it is filtered. If a node is filtered, its children are also filtered.
38089 * @param {Function} fn The filter function
38090 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38092 filterBy : function(fn, scope, startNode){
38093 startNode = startNode || this.tree.root;
38094 if(this.autoClear){
38097 var af = this.filtered, rv = this.reverse;
38098 var f = function(n){
38099 if(n == startNode){
38105 var m = fn.call(scope || n, n);
38113 startNode.cascade(f);
38116 if(typeof id != "function"){
38118 if(n && n.parentNode){
38119 n.parentNode.removeChild(n);
38127 * Clears the current filter. Note: with the "remove" option
38128 * set a filter cannot be cleared.
38130 clear : function(){
38132 var af = this.filtered;
38134 if(typeof id != "function"){
38141 this.filtered = {};
38146 * Ext JS Library 1.1.1
38147 * Copyright(c) 2006-2007, Ext JS, LLC.
38149 * Originally Released Under LGPL - original licence link has changed is not relivant.
38152 * <script type="text/javascript">
38157 * @class Roo.tree.TreeSorter
38158 * Provides sorting of nodes in a TreePanel
38160 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38161 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38162 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38163 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38164 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38165 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38167 * @param {TreePanel} tree
38168 * @param {Object} config
38170 Roo.tree.TreeSorter = function(tree, config){
38171 Roo.apply(this, config);
38172 tree.on("beforechildrenrendered", this.doSort, this);
38173 tree.on("append", this.updateSort, this);
38174 tree.on("insert", this.updateSort, this);
38176 var dsc = this.dir && this.dir.toLowerCase() == "desc";
38177 var p = this.property || "text";
38178 var sortType = this.sortType;
38179 var fs = this.folderSort;
38180 var cs = this.caseSensitive === true;
38181 var leafAttr = this.leafAttr || 'leaf';
38183 this.sortFn = function(n1, n2){
38185 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38188 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38192 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38193 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38195 return dsc ? +1 : -1;
38197 return dsc ? -1 : +1;
38204 Roo.tree.TreeSorter.prototype = {
38205 doSort : function(node){
38206 node.sort(this.sortFn);
38209 compareNodes : function(n1, n2){
38210 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38213 updateSort : function(tree, node){
38214 if(node.childrenRendered){
38215 this.doSort.defer(1, this, [node]);
38220 * Ext JS Library 1.1.1
38221 * Copyright(c) 2006-2007, Ext JS, LLC.
38223 * Originally Released Under LGPL - original licence link has changed is not relivant.
38226 * <script type="text/javascript">
38229 if(Roo.dd.DropZone){
38231 Roo.tree.TreeDropZone = function(tree, config){
38232 this.allowParentInsert = false;
38233 this.allowContainerDrop = false;
38234 this.appendOnly = false;
38235 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38237 this.lastInsertClass = "x-tree-no-status";
38238 this.dragOverData = {};
38241 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38242 ddGroup : "TreeDD",
38245 expandDelay : 1000,
38247 expandNode : function(node){
38248 if(node.hasChildNodes() && !node.isExpanded()){
38249 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38253 queueExpand : function(node){
38254 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38257 cancelExpand : function(){
38258 if(this.expandProcId){
38259 clearTimeout(this.expandProcId);
38260 this.expandProcId = false;
38264 isValidDropPoint : function(n, pt, dd, e, data){
38265 if(!n || !data){ return false; }
38266 var targetNode = n.node;
38267 var dropNode = data.node;
38268 // default drop rules
38269 if(!(targetNode && targetNode.isTarget && pt)){
38272 if(pt == "append" && targetNode.allowChildren === false){
38275 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38278 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38281 // reuse the object
38282 var overEvent = this.dragOverData;
38283 overEvent.tree = this.tree;
38284 overEvent.target = targetNode;
38285 overEvent.data = data;
38286 overEvent.point = pt;
38287 overEvent.source = dd;
38288 overEvent.rawEvent = e;
38289 overEvent.dropNode = dropNode;
38290 overEvent.cancel = false;
38291 var result = this.tree.fireEvent("nodedragover", overEvent);
38292 return overEvent.cancel === false && result !== false;
38295 getDropPoint : function(e, n, dd)
38299 return tn.allowChildren !== false ? "append" : false; // always append for root
38301 var dragEl = n.ddel;
38302 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38303 var y = Roo.lib.Event.getPageY(e);
38304 //var noAppend = tn.allowChildren === false || tn.isLeaf();
38306 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38307 var noAppend = tn.allowChildren === false;
38308 if(this.appendOnly || tn.parentNode.allowChildren === false){
38309 return noAppend ? false : "append";
38311 var noBelow = false;
38312 if(!this.allowParentInsert){
38313 noBelow = tn.hasChildNodes() && tn.isExpanded();
38315 var q = (b - t) / (noAppend ? 2 : 3);
38316 if(y >= t && y < (t + q)){
38318 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38325 onNodeEnter : function(n, dd, e, data)
38327 this.cancelExpand();
38330 onNodeOver : function(n, dd, e, data)
38333 var pt = this.getDropPoint(e, n, dd);
38336 // auto node expand check
38337 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38338 this.queueExpand(node);
38339 }else if(pt != "append"){
38340 this.cancelExpand();
38343 // set the insert point style on the target node
38344 var returnCls = this.dropNotAllowed;
38345 if(this.isValidDropPoint(n, pt, dd, e, data)){
38350 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38351 cls = "x-tree-drag-insert-above";
38352 }else if(pt == "below"){
38353 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38354 cls = "x-tree-drag-insert-below";
38356 returnCls = "x-tree-drop-ok-append";
38357 cls = "x-tree-drag-append";
38359 if(this.lastInsertClass != cls){
38360 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38361 this.lastInsertClass = cls;
38368 onNodeOut : function(n, dd, e, data){
38370 this.cancelExpand();
38371 this.removeDropIndicators(n);
38374 onNodeDrop : function(n, dd, e, data){
38375 var point = this.getDropPoint(e, n, dd);
38376 var targetNode = n.node;
38377 targetNode.ui.startDrop();
38378 if(!this.isValidDropPoint(n, point, dd, e, data)){
38379 targetNode.ui.endDrop();
38382 // first try to find the drop node
38383 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38386 target: targetNode,
38391 dropNode: dropNode,
38394 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38395 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38396 targetNode.ui.endDrop();
38399 // allow target changing
38400 targetNode = dropEvent.target;
38401 if(point == "append" && !targetNode.isExpanded()){
38402 targetNode.expand(false, null, function(){
38403 this.completeDrop(dropEvent);
38404 }.createDelegate(this));
38406 this.completeDrop(dropEvent);
38411 completeDrop : function(de){
38412 var ns = de.dropNode, p = de.point, t = de.target;
38413 if(!(ns instanceof Array)){
38417 for(var i = 0, len = ns.length; i < len; i++){
38420 t.parentNode.insertBefore(n, t);
38421 }else if(p == "below"){
38422 t.parentNode.insertBefore(n, t.nextSibling);
38428 if(this.tree.hlDrop){
38432 this.tree.fireEvent("nodedrop", de);
38435 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38436 if(this.tree.hlDrop){
38437 dropNode.ui.focus();
38438 dropNode.ui.highlight();
38440 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38443 getTree : function(){
38447 removeDropIndicators : function(n){
38450 Roo.fly(el).removeClass([
38451 "x-tree-drag-insert-above",
38452 "x-tree-drag-insert-below",
38453 "x-tree-drag-append"]);
38454 this.lastInsertClass = "_noclass";
38458 beforeDragDrop : function(target, e, id){
38459 this.cancelExpand();
38463 afterRepair : function(data){
38464 if(data && Roo.enableFx){
38465 data.node.ui.highlight();
38475 * Ext JS Library 1.1.1
38476 * Copyright(c) 2006-2007, Ext JS, LLC.
38478 * Originally Released Under LGPL - original licence link has changed is not relivant.
38481 * <script type="text/javascript">
38485 if(Roo.dd.DragZone){
38486 Roo.tree.TreeDragZone = function(tree, config){
38487 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38491 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38492 ddGroup : "TreeDD",
38494 onBeforeDrag : function(data, e){
38496 return n && n.draggable && !n.disabled;
38500 onInitDrag : function(e){
38501 var data = this.dragData;
38502 this.tree.getSelectionModel().select(data.node);
38503 this.proxy.update("");
38504 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38505 this.tree.fireEvent("startdrag", this.tree, data.node, e);
38508 getRepairXY : function(e, data){
38509 return data.node.ui.getDDRepairXY();
38512 onEndDrag : function(data, e){
38513 this.tree.fireEvent("enddrag", this.tree, data.node, e);
38518 onValidDrop : function(dd, e, id){
38519 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38523 beforeInvalidDrop : function(e, id){
38524 // this scrolls the original position back into view
38525 var sm = this.tree.getSelectionModel();
38526 sm.clearSelections();
38527 sm.select(this.dragData.node);
38532 * Ext JS Library 1.1.1
38533 * Copyright(c) 2006-2007, Ext JS, LLC.
38535 * Originally Released Under LGPL - original licence link has changed is not relivant.
38538 * <script type="text/javascript">
38541 * @class Roo.tree.TreeEditor
38542 * @extends Roo.Editor
38543 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
38544 * as the editor field.
38546 * @param {Object} config (used to be the tree panel.)
38547 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38549 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38550 * @cfg {Roo.form.TextField} field [required] The field configuration
38554 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38557 if (oldconfig) { // old style..
38558 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38561 tree = config.tree;
38562 config.field = config.field || {};
38563 config.field.xtype = 'TextField';
38564 field = Roo.factory(config.field, Roo.form);
38566 config = config || {};
38571 * @event beforenodeedit
38572 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
38573 * false from the handler of this event.
38574 * @param {Editor} this
38575 * @param {Roo.tree.Node} node
38577 "beforenodeedit" : true
38581 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38585 tree.on('beforeclick', this.beforeNodeClick, this);
38586 tree.getTreeEl().on('mousedown', this.hide, this);
38587 this.on('complete', this.updateNode, this);
38588 this.on('beforestartedit', this.fitToTree, this);
38589 this.on('startedit', this.bindScroll, this, {delay:10});
38590 this.on('specialkey', this.onSpecialKey, this);
38593 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38595 * @cfg {String} alignment
38596 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38602 * @cfg {Boolean} hideEl
38603 * True to hide the bound element while the editor is displayed (defaults to false)
38607 * @cfg {String} cls
38608 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38610 cls: "x-small-editor x-tree-editor",
38612 * @cfg {Boolean} shim
38613 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38619 * @cfg {Number} maxWidth
38620 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
38621 * the containing tree element's size, it will be automatically limited for you to the container width, taking
38622 * scroll and client offsets into account prior to each edit.
38629 fitToTree : function(ed, el){
38630 var td = this.tree.getTreeEl().dom, nd = el.dom;
38631 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
38632 td.scrollLeft = nd.offsetLeft;
38636 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38637 this.setSize(w, '');
38639 return this.fireEvent('beforenodeedit', this, this.editNode);
38644 triggerEdit : function(node){
38645 this.completeEdit();
38646 this.editNode = node;
38647 this.startEdit(node.ui.textNode, node.text);
38651 bindScroll : function(){
38652 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38656 beforeNodeClick : function(node, e){
38657 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38658 this.lastClick = new Date();
38659 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38661 this.triggerEdit(node);
38668 updateNode : function(ed, value){
38669 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38670 this.editNode.setText(value);
38674 onHide : function(){
38675 Roo.tree.TreeEditor.superclass.onHide.call(this);
38677 this.editNode.ui.focus();
38682 onSpecialKey : function(field, e){
38683 var k = e.getKey();
38687 }else if(k == e.ENTER && !e.hasModifier()){
38689 this.completeEdit();
38692 });//<Script type="text/javascript">
38695 * Ext JS Library 1.1.1
38696 * Copyright(c) 2006-2007, Ext JS, LLC.
38698 * Originally Released Under LGPL - original licence link has changed is not relivant.
38701 * <script type="text/javascript">
38705 * Not documented??? - probably should be...
38708 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38709 //focus: Roo.emptyFn, // prevent odd scrolling behavior
38711 renderElements : function(n, a, targetNode, bulkRender){
38712 //consel.log("renderElements?");
38713 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38715 var t = n.getOwnerTree();
38716 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38718 var cols = t.columns;
38719 var bw = t.borderWidth;
38721 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38722 var cb = typeof a.checked == "boolean";
38723 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38724 var colcls = 'x-t-' + tid + '-c0';
38726 '<li class="x-tree-node">',
38729 '<div class="x-tree-node-el ', a.cls,'">',
38731 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38734 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38735 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
38736 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38737 (a.icon ? ' x-tree-node-inline-icon' : ''),
38738 (a.iconCls ? ' '+a.iconCls : ''),
38739 '" unselectable="on" />',
38740 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
38741 (a.checked ? 'checked="checked" />' : ' />')) : ''),
38743 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38744 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38745 '<span unselectable="on" qtip="' + tx + '">',
38749 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38750 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38752 for(var i = 1, len = cols.length; i < len; i++){
38754 colcls = 'x-t-' + tid + '-c' +i;
38755 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38756 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38757 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38763 '<div class="x-clear"></div></div>',
38764 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38767 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38768 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38769 n.nextSibling.ui.getEl(), buf.join(""));
38771 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38773 var el = this.wrap.firstChild;
38775 this.elNode = el.firstChild;
38776 this.ranchor = el.childNodes[1];
38777 this.ctNode = this.wrap.childNodes[1];
38778 var cs = el.firstChild.childNodes;
38779 this.indentNode = cs[0];
38780 this.ecNode = cs[1];
38781 this.iconNode = cs[2];
38784 this.checkbox = cs[3];
38787 this.anchor = cs[index];
38789 this.textNode = cs[index].firstChild;
38791 //el.on("click", this.onClick, this);
38792 //el.on("dblclick", this.onDblClick, this);
38795 // console.log(this);
38797 initEvents : function(){
38798 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38801 var a = this.ranchor;
38803 var el = Roo.get(a);
38805 if(Roo.isOpera){ // opera render bug ignores the CSS
38806 el.setStyle("text-decoration", "none");
38809 el.on("click", this.onClick, this);
38810 el.on("dblclick", this.onDblClick, this);
38811 el.on("contextmenu", this.onContextMenu, this);
38815 /*onSelectedChange : function(state){
38818 this.addClass("x-tree-selected");
38821 this.removeClass("x-tree-selected");
38824 addClass : function(cls){
38826 Roo.fly(this.elRow).addClass(cls);
38832 removeClass : function(cls){
38834 Roo.fly(this.elRow).removeClass(cls);
38840 });//<Script type="text/javascript">
38844 * Ext JS Library 1.1.1
38845 * Copyright(c) 2006-2007, Ext JS, LLC.
38847 * Originally Released Under LGPL - original licence link has changed is not relivant.
38850 * <script type="text/javascript">
38855 * @class Roo.tree.ColumnTree
38856 * @extends Roo.tree.TreePanel
38857 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
38858 * @cfg {int} borderWidth compined right/left border allowance
38860 * @param {String/HTMLElement/Element} el The container element
38861 * @param {Object} config
38863 Roo.tree.ColumnTree = function(el, config)
38865 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38869 * Fire this event on a container when it resizes
38870 * @param {int} w Width
38871 * @param {int} h Height
38875 this.on('resize', this.onResize, this);
38878 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38882 borderWidth: Roo.isBorderBox ? 0 : 2,
38885 render : function(){
38886 // add the header.....
38888 Roo.tree.ColumnTree.superclass.render.apply(this);
38890 this.el.addClass('x-column-tree');
38892 this.headers = this.el.createChild(
38893 {cls:'x-tree-headers'},this.innerCt.dom);
38895 var cols = this.columns, c;
38896 var totalWidth = 0;
38898 var len = cols.length;
38899 for(var i = 0; i < len; i++){
38901 totalWidth += c.width;
38902 this.headEls.push(this.headers.createChild({
38903 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38905 cls:'x-tree-hd-text',
38908 style:'width:'+(c.width-this.borderWidth)+'px;'
38911 this.headers.createChild({cls:'x-clear'});
38912 // prevent floats from wrapping when clipped
38913 this.headers.setWidth(totalWidth);
38914 //this.innerCt.setWidth(totalWidth);
38915 this.innerCt.setStyle({ overflow: 'auto' });
38916 this.onResize(this.width, this.height);
38920 onResize : function(w,h)
38925 this.innerCt.setWidth(this.width);
38926 this.innerCt.setHeight(this.height-20);
38929 var cols = this.columns, c;
38930 var totalWidth = 0;
38932 var len = cols.length;
38933 for(var i = 0; i < len; i++){
38935 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38936 // it's the expander..
38937 expEl = this.headEls[i];
38940 totalWidth += c.width;
38944 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
38946 this.headers.setWidth(w-20);
38955 * Ext JS Library 1.1.1
38956 * Copyright(c) 2006-2007, Ext JS, LLC.
38958 * Originally Released Under LGPL - original licence link has changed is not relivant.
38961 * <script type="text/javascript">
38965 * @class Roo.menu.Menu
38966 * @extends Roo.util.Observable
38967 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38968 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
38969 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38971 * Creates a new Menu
38972 * @param {Object} config Configuration options
38974 Roo.menu.Menu = function(config){
38976 Roo.menu.Menu.superclass.constructor.call(this, config);
38978 this.id = this.id || Roo.id();
38981 * @event beforeshow
38982 * Fires before this menu is displayed
38983 * @param {Roo.menu.Menu} this
38987 * @event beforehide
38988 * Fires before this menu is hidden
38989 * @param {Roo.menu.Menu} this
38994 * Fires after this menu is displayed
38995 * @param {Roo.menu.Menu} this
39000 * Fires after this menu is hidden
39001 * @param {Roo.menu.Menu} this
39006 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
39007 * @param {Roo.menu.Menu} this
39008 * @param {Roo.menu.Item} menuItem The menu item that was clicked
39009 * @param {Roo.EventObject} e
39014 * Fires when the mouse is hovering over this menu
39015 * @param {Roo.menu.Menu} this
39016 * @param {Roo.EventObject} e
39017 * @param {Roo.menu.Item} menuItem The menu item that was clicked
39022 * Fires when the mouse exits this menu
39023 * @param {Roo.menu.Menu} this
39024 * @param {Roo.EventObject} e
39025 * @param {Roo.menu.Item} menuItem The menu item that was clicked
39030 * Fires when a menu item contained in this menu is clicked
39031 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39032 * @param {Roo.EventObject} e
39036 if (this.registerMenu) {
39037 Roo.menu.MenuMgr.register(this);
39040 var mis = this.items;
39041 this.items = new Roo.util.MixedCollection();
39043 this.add.apply(this, mis);
39047 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39049 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39053 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39054 * for bottom-right shadow (defaults to "sides")
39058 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39059 * this menu (defaults to "tl-tr?")
39061 subMenuAlign : "tl-tr?",
39063 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39064 * relative to its element of origin (defaults to "tl-bl?")
39066 defaultAlign : "tl-bl?",
39068 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39070 allowOtherMenus : false,
39072 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39074 registerMenu : true,
39079 render : function(){
39083 var el = this.el = new Roo.Layer({
39085 shadow:this.shadow,
39087 parentEl: this.parentEl || document.body,
39091 this.keyNav = new Roo.menu.MenuNav(this);
39094 el.addClass("x-menu-plain");
39097 el.addClass(this.cls);
39099 // generic focus element
39100 this.focusEl = el.createChild({
39101 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39103 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39104 //disabling touch- as it's causing issues ..
39105 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
39106 ul.on('click' , this.onClick, this);
39109 ul.on("mouseover", this.onMouseOver, this);
39110 ul.on("mouseout", this.onMouseOut, this);
39111 this.items.each(function(item){
39116 var li = document.createElement("li");
39117 li.className = "x-menu-list-item";
39118 ul.dom.appendChild(li);
39119 item.render(li, this);
39126 autoWidth : function(){
39127 var el = this.el, ul = this.ul;
39131 var w = this.width;
39134 }else if(Roo.isIE){
39135 el.setWidth(this.minWidth);
39136 var t = el.dom.offsetWidth; // force recalc
39137 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39142 delayAutoWidth : function(){
39145 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39147 this.awTask.delay(20);
39152 findTargetItem : function(e){
39153 var t = e.getTarget(".x-menu-list-item", this.ul, true);
39154 if(t && t.menuItemId){
39155 return this.items.get(t.menuItemId);
39160 onClick : function(e){
39161 Roo.log("menu.onClick");
39162 var t = this.findTargetItem(e);
39167 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
39168 if(t == this.activeItem && t.shouldDeactivate(e)){
39169 this.activeItem.deactivate();
39170 delete this.activeItem;
39174 this.setActiveItem(t, true);
39182 this.fireEvent("click", this, t, e);
39186 setActiveItem : function(item, autoExpand){
39187 if(item != this.activeItem){
39188 if(this.activeItem){
39189 this.activeItem.deactivate();
39191 this.activeItem = item;
39192 item.activate(autoExpand);
39193 }else if(autoExpand){
39199 tryActivate : function(start, step){
39200 var items = this.items;
39201 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39202 var item = items.get(i);
39203 if(!item.disabled && item.canActivate){
39204 this.setActiveItem(item, false);
39212 onMouseOver : function(e){
39214 if(t = this.findTargetItem(e)){
39215 if(t.canActivate && !t.disabled){
39216 this.setActiveItem(t, true);
39219 this.fireEvent("mouseover", this, e, t);
39223 onMouseOut : function(e){
39225 if(t = this.findTargetItem(e)){
39226 if(t == this.activeItem && t.shouldDeactivate(e)){
39227 this.activeItem.deactivate();
39228 delete this.activeItem;
39231 this.fireEvent("mouseout", this, e, t);
39235 * Read-only. Returns true if the menu is currently displayed, else false.
39238 isVisible : function(){
39239 return this.el && !this.hidden;
39243 * Displays this menu relative to another element
39244 * @param {String/HTMLElement/Roo.Element} element The element to align to
39245 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39246 * the element (defaults to this.defaultAlign)
39247 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39249 show : function(el, pos, parentMenu){
39250 this.parentMenu = parentMenu;
39254 this.fireEvent("beforeshow", this);
39255 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39259 * Displays this menu at a specific xy position
39260 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39261 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39263 showAt : function(xy, parentMenu, /* private: */_e){
39264 this.parentMenu = parentMenu;
39269 this.fireEvent("beforeshow", this);
39270 xy = this.el.adjustForConstraints(xy);
39274 this.hidden = false;
39276 this.fireEvent("show", this);
39279 focus : function(){
39281 this.doFocus.defer(50, this);
39285 doFocus : function(){
39287 this.focusEl.focus();
39292 * Hides this menu and optionally all parent menus
39293 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39295 hide : function(deep){
39296 if(this.el && this.isVisible()){
39297 this.fireEvent("beforehide", this);
39298 if(this.activeItem){
39299 this.activeItem.deactivate();
39300 this.activeItem = null;
39303 this.hidden = true;
39304 this.fireEvent("hide", this);
39306 if(deep === true && this.parentMenu){
39307 this.parentMenu.hide(true);
39312 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39313 * Any of the following are valid:
39315 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39316 * <li>An HTMLElement object which will be converted to a menu item</li>
39317 * <li>A menu item config object that will be created as a new menu item</li>
39318 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39319 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39324 var menu = new Roo.menu.Menu();
39326 // Create a menu item to add by reference
39327 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39329 // Add a bunch of items at once using different methods.
39330 // Only the last item added will be returned.
39331 var item = menu.add(
39332 menuItem, // add existing item by ref
39333 'Dynamic Item', // new TextItem
39334 '-', // new separator
39335 { text: 'Config Item' } // new item by config
39338 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39339 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39342 var a = arguments, l = a.length, item;
39343 for(var i = 0; i < l; i++){
39345 if ((typeof(el) == "object") && el.xtype && el.xns) {
39346 el = Roo.factory(el, Roo.menu);
39349 if(el.render){ // some kind of Item
39350 item = this.addItem(el);
39351 }else if(typeof el == "string"){ // string
39352 if(el == "separator" || el == "-"){
39353 item = this.addSeparator();
39355 item = this.addText(el);
39357 }else if(el.tagName || el.el){ // element
39358 item = this.addElement(el);
39359 }else if(typeof el == "object"){ // must be menu item config?
39360 item = this.addMenuItem(el);
39367 * Returns this menu's underlying {@link Roo.Element} object
39368 * @return {Roo.Element} The element
39370 getEl : function(){
39378 * Adds a separator bar to the menu
39379 * @return {Roo.menu.Item} The menu item that was added
39381 addSeparator : function(){
39382 return this.addItem(new Roo.menu.Separator());
39386 * Adds an {@link Roo.Element} object to the menu
39387 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39388 * @return {Roo.menu.Item} The menu item that was added
39390 addElement : function(el){
39391 return this.addItem(new Roo.menu.BaseItem(el));
39395 * Adds an existing object based on {@link Roo.menu.Item} to the menu
39396 * @param {Roo.menu.Item} item The menu item to add
39397 * @return {Roo.menu.Item} The menu item that was added
39399 addItem : function(item){
39400 this.items.add(item);
39402 var li = document.createElement("li");
39403 li.className = "x-menu-list-item";
39404 this.ul.dom.appendChild(li);
39405 item.render(li, this);
39406 this.delayAutoWidth();
39412 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39413 * @param {Object} config A MenuItem config object
39414 * @return {Roo.menu.Item} The menu item that was added
39416 addMenuItem : function(config){
39417 if(!(config instanceof Roo.menu.Item)){
39418 if(typeof config.checked == "boolean"){ // must be check menu item config?
39419 config = new Roo.menu.CheckItem(config);
39421 config = new Roo.menu.Item(config);
39424 return this.addItem(config);
39428 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39429 * @param {String} text The text to display in the menu item
39430 * @return {Roo.menu.Item} The menu item that was added
39432 addText : function(text){
39433 return this.addItem(new Roo.menu.TextItem({ text : text }));
39437 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39438 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39439 * @param {Roo.menu.Item} item The menu item to add
39440 * @return {Roo.menu.Item} The menu item that was added
39442 insert : function(index, item){
39443 this.items.insert(index, item);
39445 var li = document.createElement("li");
39446 li.className = "x-menu-list-item";
39447 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39448 item.render(li, this);
39449 this.delayAutoWidth();
39455 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39456 * @param {Roo.menu.Item} item The menu item to remove
39458 remove : function(item){
39459 this.items.removeKey(item.id);
39464 * Removes and destroys all items in the menu
39466 removeAll : function(){
39468 while(f = this.items.first()){
39474 // MenuNav is a private utility class used internally by the Menu
39475 Roo.menu.MenuNav = function(menu){
39476 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39477 this.scope = this.menu = menu;
39480 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39481 doRelay : function(e, h){
39482 var k = e.getKey();
39483 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39484 this.menu.tryActivate(0, 1);
39487 return h.call(this.scope || this, e, this.menu);
39490 up : function(e, m){
39491 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39492 m.tryActivate(m.items.length-1, -1);
39496 down : function(e, m){
39497 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39498 m.tryActivate(0, 1);
39502 right : function(e, m){
39504 m.activeItem.expandMenu(true);
39508 left : function(e, m){
39510 if(m.parentMenu && m.parentMenu.activeItem){
39511 m.parentMenu.activeItem.activate();
39515 enter : function(e, m){
39517 e.stopPropagation();
39518 m.activeItem.onClick(e);
39519 m.fireEvent("click", this, m.activeItem);
39525 * Ext JS Library 1.1.1
39526 * Copyright(c) 2006-2007, Ext JS, LLC.
39528 * Originally Released Under LGPL - original licence link has changed is not relivant.
39531 * <script type="text/javascript">
39535 * @class Roo.menu.MenuMgr
39536 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39539 Roo.menu.MenuMgr = function(){
39540 var menus, active, groups = {}, attached = false, lastShow = new Date();
39542 // private - called when first menu is created
39545 active = new Roo.util.MixedCollection();
39546 Roo.get(document).addKeyListener(27, function(){
39547 if(active.length > 0){
39554 function hideAll(){
39555 if(active && active.length > 0){
39556 var c = active.clone();
39557 c.each(function(m){
39564 function onHide(m){
39566 if(active.length < 1){
39567 Roo.get(document).un("mousedown", onMouseDown);
39573 function onShow(m){
39574 var last = active.last();
39575 lastShow = new Date();
39578 Roo.get(document).on("mousedown", onMouseDown);
39582 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39583 m.parentMenu.activeChild = m;
39584 }else if(last && last.isVisible()){
39585 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39590 function onBeforeHide(m){
39592 m.activeChild.hide();
39594 if(m.autoHideTimer){
39595 clearTimeout(m.autoHideTimer);
39596 delete m.autoHideTimer;
39601 function onBeforeShow(m){
39602 var pm = m.parentMenu;
39603 if(!pm && !m.allowOtherMenus){
39605 }else if(pm && pm.activeChild && active != m){
39606 pm.activeChild.hide();
39611 function onMouseDown(e){
39612 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39618 function onBeforeCheck(mi, state){
39620 var g = groups[mi.group];
39621 for(var i = 0, l = g.length; i < l; i++){
39623 g[i].setChecked(false);
39632 * Hides all menus that are currently visible
39634 hideAll : function(){
39639 register : function(menu){
39643 menus[menu.id] = menu;
39644 menu.on("beforehide", onBeforeHide);
39645 menu.on("hide", onHide);
39646 menu.on("beforeshow", onBeforeShow);
39647 menu.on("show", onShow);
39648 var g = menu.group;
39649 if(g && menu.events["checkchange"]){
39653 groups[g].push(menu);
39654 menu.on("checkchange", onCheck);
39659 * Returns a {@link Roo.menu.Menu} object
39660 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39661 * be used to generate and return a new Menu instance.
39663 get : function(menu){
39664 if(typeof menu == "string"){ // menu id
39665 return menus[menu];
39666 }else if(menu.events){ // menu instance
39668 }else if(typeof menu.length == 'number'){ // array of menu items?
39669 return new Roo.menu.Menu({items:menu});
39670 }else{ // otherwise, must be a config
39671 return new Roo.menu.Menu(menu);
39676 unregister : function(menu){
39677 delete menus[menu.id];
39678 menu.un("beforehide", onBeforeHide);
39679 menu.un("hide", onHide);
39680 menu.un("beforeshow", onBeforeShow);
39681 menu.un("show", onShow);
39682 var g = menu.group;
39683 if(g && menu.events["checkchange"]){
39684 groups[g].remove(menu);
39685 menu.un("checkchange", onCheck);
39690 registerCheckable : function(menuItem){
39691 var g = menuItem.group;
39696 groups[g].push(menuItem);
39697 menuItem.on("beforecheckchange", onBeforeCheck);
39702 unregisterCheckable : function(menuItem){
39703 var g = menuItem.group;
39705 groups[g].remove(menuItem);
39706 menuItem.un("beforecheckchange", onBeforeCheck);
39712 * Ext JS Library 1.1.1
39713 * Copyright(c) 2006-2007, Ext JS, LLC.
39715 * Originally Released Under LGPL - original licence link has changed is not relivant.
39718 * <script type="text/javascript">
39723 * @class Roo.menu.BaseItem
39724 * @extends Roo.Component
39726 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
39727 * management and base configuration options shared by all menu components.
39729 * Creates a new BaseItem
39730 * @param {Object} config Configuration options
39732 Roo.menu.BaseItem = function(config){
39733 Roo.menu.BaseItem.superclass.constructor.call(this, config);
39738 * Fires when this item is clicked
39739 * @param {Roo.menu.BaseItem} this
39740 * @param {Roo.EventObject} e
39745 * Fires when this item is activated
39746 * @param {Roo.menu.BaseItem} this
39750 * @event deactivate
39751 * Fires when this item is deactivated
39752 * @param {Roo.menu.BaseItem} this
39758 this.on("click", this.handler, this.scope, true);
39762 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39764 * @cfg {Function} handler
39765 * A function that will handle the click event of this menu item (defaults to undefined)
39768 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39770 canActivate : false,
39773 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39778 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39780 activeClass : "x-menu-item-active",
39782 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39784 hideOnClick : true,
39786 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39791 ctype: "Roo.menu.BaseItem",
39794 actionMode : "container",
39797 render : function(container, parentMenu){
39798 this.parentMenu = parentMenu;
39799 Roo.menu.BaseItem.superclass.render.call(this, container);
39800 this.container.menuItemId = this.id;
39804 onRender : function(container, position){
39805 this.el = Roo.get(this.el);
39806 container.dom.appendChild(this.el.dom);
39810 onClick : function(e){
39811 if(!this.disabled && this.fireEvent("click", this, e) !== false
39812 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39813 this.handleClick(e);
39820 activate : function(){
39824 var li = this.container;
39825 li.addClass(this.activeClass);
39826 this.region = li.getRegion().adjust(2, 2, -2, -2);
39827 this.fireEvent("activate", this);
39832 deactivate : function(){
39833 this.container.removeClass(this.activeClass);
39834 this.fireEvent("deactivate", this);
39838 shouldDeactivate : function(e){
39839 return !this.region || !this.region.contains(e.getPoint());
39843 handleClick : function(e){
39844 if(this.hideOnClick){
39845 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39850 expandMenu : function(autoActivate){
39855 hideMenu : function(){
39860 * Ext JS Library 1.1.1
39861 * Copyright(c) 2006-2007, Ext JS, LLC.
39863 * Originally Released Under LGPL - original licence link has changed is not relivant.
39866 * <script type="text/javascript">
39870 * @class Roo.menu.Adapter
39871 * @extends Roo.menu.BaseItem
39873 * 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.
39874 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39876 * Creates a new Adapter
39877 * @param {Object} config Configuration options
39879 Roo.menu.Adapter = function(component, config){
39880 Roo.menu.Adapter.superclass.constructor.call(this, config);
39881 this.component = component;
39883 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39885 canActivate : true,
39888 onRender : function(container, position){
39889 this.component.render(container);
39890 this.el = this.component.getEl();
39894 activate : function(){
39898 this.component.focus();
39899 this.fireEvent("activate", this);
39904 deactivate : function(){
39905 this.fireEvent("deactivate", this);
39909 disable : function(){
39910 this.component.disable();
39911 Roo.menu.Adapter.superclass.disable.call(this);
39915 enable : function(){
39916 this.component.enable();
39917 Roo.menu.Adapter.superclass.enable.call(this);
39921 * Ext JS Library 1.1.1
39922 * Copyright(c) 2006-2007, Ext JS, LLC.
39924 * Originally Released Under LGPL - original licence link has changed is not relivant.
39927 * <script type="text/javascript">
39931 * @class Roo.menu.TextItem
39932 * @extends Roo.menu.BaseItem
39933 * Adds a static text string to a menu, usually used as either a heading or group separator.
39934 * Note: old style constructor with text is still supported.
39937 * Creates a new TextItem
39938 * @param {Object} cfg Configuration
39940 Roo.menu.TextItem = function(cfg){
39941 if (typeof(cfg) == 'string') {
39944 Roo.apply(this,cfg);
39947 Roo.menu.TextItem.superclass.constructor.call(this);
39950 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39952 * @cfg {String} text Text to show on item.
39957 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39959 hideOnClick : false,
39961 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39963 itemCls : "x-menu-text",
39966 onRender : function(){
39967 var s = document.createElement("span");
39968 s.className = this.itemCls;
39969 s.innerHTML = this.text;
39971 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39975 * Ext JS Library 1.1.1
39976 * Copyright(c) 2006-2007, Ext JS, LLC.
39978 * Originally Released Under LGPL - original licence link has changed is not relivant.
39981 * <script type="text/javascript">
39985 * @class Roo.menu.Separator
39986 * @extends Roo.menu.BaseItem
39987 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39988 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39990 * @param {Object} config Configuration options
39992 Roo.menu.Separator = function(config){
39993 Roo.menu.Separator.superclass.constructor.call(this, config);
39996 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39998 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
40000 itemCls : "x-menu-sep",
40002 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
40004 hideOnClick : false,
40007 onRender : function(li){
40008 var s = document.createElement("span");
40009 s.className = this.itemCls;
40010 s.innerHTML = " ";
40012 li.addClass("x-menu-sep-li");
40013 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
40017 * Ext JS Library 1.1.1
40018 * Copyright(c) 2006-2007, Ext JS, LLC.
40020 * Originally Released Under LGPL - original licence link has changed is not relivant.
40023 * <script type="text/javascript">
40026 * @class Roo.menu.Item
40027 * @extends Roo.menu.BaseItem
40028 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40029 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40030 * activation and click handling.
40032 * Creates a new Item
40033 * @param {Object} config Configuration options
40035 Roo.menu.Item = function(config){
40036 Roo.menu.Item.superclass.constructor.call(this, config);
40038 this.menu = Roo.menu.MenuMgr.get(this.menu);
40041 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40043 * @cfg {Roo.menu.Menu} menu
40047 * @cfg {String} text
40048 * The text to show on the menu item.
40052 * @cfg {String} html to render in menu
40053 * The text to show on the menu item (HTML version).
40057 * @cfg {String} icon
40058 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40062 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40064 itemCls : "x-menu-item",
40066 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40068 canActivate : true,
40070 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40073 // doc'd in BaseItem
40077 ctype: "Roo.menu.Item",
40080 onRender : function(container, position){
40081 var el = document.createElement("a");
40082 el.hideFocus = true;
40083 el.unselectable = "on";
40084 el.href = this.href || "#";
40085 if(this.hrefTarget){
40086 el.target = this.hrefTarget;
40088 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
40090 var html = this.html.length ? this.html : String.format('{0}',this.text);
40092 el.innerHTML = String.format(
40093 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40094 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40096 Roo.menu.Item.superclass.onRender.call(this, container, position);
40100 * Sets the text to display in this menu item
40101 * @param {String} text The text to display
40102 * @param {Boolean} isHTML true to indicate text is pure html.
40104 setText : function(text, isHTML){
40112 var html = this.html.length ? this.html : String.format('{0}',this.text);
40114 this.el.update(String.format(
40115 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40116 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40117 this.parentMenu.autoWidth();
40122 handleClick : function(e){
40123 if(!this.href){ // if no link defined, stop the event automatically
40126 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40130 activate : function(autoExpand){
40131 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40141 shouldDeactivate : function(e){
40142 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40143 if(this.menu && this.menu.isVisible()){
40144 return !this.menu.getEl().getRegion().contains(e.getPoint());
40152 deactivate : function(){
40153 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40158 expandMenu : function(autoActivate){
40159 if(!this.disabled && this.menu){
40160 clearTimeout(this.hideTimer);
40161 delete this.hideTimer;
40162 if(!this.menu.isVisible() && !this.showTimer){
40163 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40164 }else if (this.menu.isVisible() && autoActivate){
40165 this.menu.tryActivate(0, 1);
40171 deferExpand : function(autoActivate){
40172 delete this.showTimer;
40173 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40175 this.menu.tryActivate(0, 1);
40180 hideMenu : function(){
40181 clearTimeout(this.showTimer);
40182 delete this.showTimer;
40183 if(!this.hideTimer && this.menu && this.menu.isVisible()){
40184 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40189 deferHide : function(){
40190 delete this.hideTimer;
40195 * Ext JS Library 1.1.1
40196 * Copyright(c) 2006-2007, Ext JS, LLC.
40198 * Originally Released Under LGPL - original licence link has changed is not relivant.
40201 * <script type="text/javascript">
40205 * @class Roo.menu.CheckItem
40206 * @extends Roo.menu.Item
40207 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40209 * Creates a new CheckItem
40210 * @param {Object} config Configuration options
40212 Roo.menu.CheckItem = function(config){
40213 Roo.menu.CheckItem.superclass.constructor.call(this, config);
40216 * @event beforecheckchange
40217 * Fires before the checked value is set, providing an opportunity to cancel if needed
40218 * @param {Roo.menu.CheckItem} this
40219 * @param {Boolean} checked The new checked value that will be set
40221 "beforecheckchange" : true,
40223 * @event checkchange
40224 * Fires after the checked value has been set
40225 * @param {Roo.menu.CheckItem} this
40226 * @param {Boolean} checked The checked value that was set
40228 "checkchange" : true
40230 if(this.checkHandler){
40231 this.on('checkchange', this.checkHandler, this.scope);
40234 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40236 * @cfg {String} group
40237 * All check items with the same group name will automatically be grouped into a single-select
40238 * radio button group (defaults to '')
40241 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40243 itemCls : "x-menu-item x-menu-check-item",
40245 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40247 groupClass : "x-menu-group-item",
40250 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
40251 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40252 * initialized with checked = true will be rendered as checked.
40257 ctype: "Roo.menu.CheckItem",
40260 onRender : function(c){
40261 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40263 this.el.addClass(this.groupClass);
40265 Roo.menu.MenuMgr.registerCheckable(this);
40267 this.checked = false;
40268 this.setChecked(true, true);
40273 destroy : function(){
40275 Roo.menu.MenuMgr.unregisterCheckable(this);
40277 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40281 * Set the checked state of this item
40282 * @param {Boolean} checked The new checked value
40283 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40285 setChecked : function(state, suppressEvent){
40286 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40287 if(this.container){
40288 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40290 this.checked = state;
40291 if(suppressEvent !== true){
40292 this.fireEvent("checkchange", this, state);
40298 handleClick : function(e){
40299 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40300 this.setChecked(!this.checked);
40302 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40306 * Ext JS Library 1.1.1
40307 * Copyright(c) 2006-2007, Ext JS, LLC.
40309 * Originally Released Under LGPL - original licence link has changed is not relivant.
40312 * <script type="text/javascript">
40316 * @class Roo.menu.DateItem
40317 * @extends Roo.menu.Adapter
40318 * A menu item that wraps the {@link Roo.DatPicker} component.
40320 * Creates a new DateItem
40321 * @param {Object} config Configuration options
40323 Roo.menu.DateItem = function(config){
40324 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40325 /** The Roo.DatePicker object @type Roo.DatePicker */
40326 this.picker = this.component;
40327 this.addEvents({select: true});
40329 this.picker.on("render", function(picker){
40330 picker.getEl().swallowEvent("click");
40331 picker.container.addClass("x-menu-date-item");
40334 this.picker.on("select", this.onSelect, this);
40337 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40339 onSelect : function(picker, date){
40340 this.fireEvent("select", this, date, picker);
40341 Roo.menu.DateItem.superclass.handleClick.call(this);
40345 * Ext JS Library 1.1.1
40346 * Copyright(c) 2006-2007, Ext JS, LLC.
40348 * Originally Released Under LGPL - original licence link has changed is not relivant.
40351 * <script type="text/javascript">
40355 * @class Roo.menu.ColorItem
40356 * @extends Roo.menu.Adapter
40357 * A menu item that wraps the {@link Roo.ColorPalette} component.
40359 * Creates a new ColorItem
40360 * @param {Object} config Configuration options
40362 Roo.menu.ColorItem = function(config){
40363 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40364 /** The Roo.ColorPalette object @type Roo.ColorPalette */
40365 this.palette = this.component;
40366 this.relayEvents(this.palette, ["select"]);
40367 if(this.selectHandler){
40368 this.on('select', this.selectHandler, this.scope);
40371 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40373 * Ext JS Library 1.1.1
40374 * Copyright(c) 2006-2007, Ext JS, LLC.
40376 * Originally Released Under LGPL - original licence link has changed is not relivant.
40379 * <script type="text/javascript">
40384 * @class Roo.menu.DateMenu
40385 * @extends Roo.menu.Menu
40386 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40388 * Creates a new DateMenu
40389 * @param {Object} config Configuration options
40391 Roo.menu.DateMenu = function(config){
40392 Roo.menu.DateMenu.superclass.constructor.call(this, config);
40394 var di = new Roo.menu.DateItem(config);
40397 * The {@link Roo.DatePicker} instance for this DateMenu
40400 this.picker = di.picker;
40403 * @param {DatePicker} picker
40404 * @param {Date} date
40406 this.relayEvents(di, ["select"]);
40407 this.on('beforeshow', function(){
40409 this.picker.hideMonthPicker(false);
40413 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40417 * Ext JS Library 1.1.1
40418 * Copyright(c) 2006-2007, Ext JS, LLC.
40420 * Originally Released Under LGPL - original licence link has changed is not relivant.
40423 * <script type="text/javascript">
40428 * @class Roo.menu.ColorMenu
40429 * @extends Roo.menu.Menu
40430 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40432 * Creates a new ColorMenu
40433 * @param {Object} config Configuration options
40435 Roo.menu.ColorMenu = function(config){
40436 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40438 var ci = new Roo.menu.ColorItem(config);
40441 * The {@link Roo.ColorPalette} instance for this ColorMenu
40442 * @type ColorPalette
40444 this.palette = ci.palette;
40447 * @param {ColorPalette} palette
40448 * @param {String} color
40450 this.relayEvents(ci, ["select"]);
40452 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40454 * Ext JS Library 1.1.1
40455 * Copyright(c) 2006-2007, Ext JS, LLC.
40457 * Originally Released Under LGPL - original licence link has changed is not relivant.
40460 * <script type="text/javascript">
40464 * @class Roo.form.TextItem
40465 * @extends Roo.BoxComponent
40466 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40468 * Creates a new TextItem
40469 * @param {Object} config Configuration options
40471 Roo.form.TextItem = function(config){
40472 Roo.form.TextItem.superclass.constructor.call(this, config);
40475 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
40478 * @cfg {String} tag the tag for this item (default div)
40482 * @cfg {String} html the content for this item
40486 getAutoCreate : function()
40499 onRender : function(ct, position)
40501 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40504 var cfg = this.getAutoCreate();
40506 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40508 if (!cfg.name.length) {
40511 this.el = ct.createChild(cfg, position);
40516 * @param {String} html update the Contents of the element.
40518 setHTML : function(html)
40520 this.fieldEl.dom.innerHTML = html;
40525 * Ext JS Library 1.1.1
40526 * Copyright(c) 2006-2007, Ext JS, LLC.
40528 * Originally Released Under LGPL - original licence link has changed is not relivant.
40531 * <script type="text/javascript">
40535 * @class Roo.form.Field
40536 * @extends Roo.BoxComponent
40537 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40539 * Creates a new Field
40540 * @param {Object} config Configuration options
40542 Roo.form.Field = function(config){
40543 Roo.form.Field.superclass.constructor.call(this, config);
40546 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
40548 * @cfg {String} fieldLabel Label to use when rendering a form.
40551 * @cfg {String} qtip Mouse over tip
40555 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40557 invalidClass : "x-form-invalid",
40559 * @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")
40561 invalidText : "The value in this field is invalid",
40563 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40565 focusClass : "x-form-focus",
40567 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40568 automatic validation (defaults to "keyup").
40570 validationEvent : "keyup",
40572 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40574 validateOnBlur : true,
40576 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40578 validationDelay : 250,
40580 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40581 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40583 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40585 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40587 fieldClass : "x-form-field",
40589 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
40592 ----------- ----------------------------------------------------------------------
40593 qtip Display a quick tip when the user hovers over the field
40594 title Display a default browser title attribute popup
40595 under Add a block div beneath the field containing the error text
40596 side Add an error icon to the right of the field with a popup on hover
40597 [element id] Add the error text directly to the innerHTML of the specified element
40600 msgTarget : 'qtip',
40602 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40607 * @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.
40612 * @cfg {Boolean} disabled True to disable the field (defaults to false).
40617 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40619 inputType : undefined,
40622 * @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).
40624 tabIndex : undefined,
40627 isFormField : true,
40632 * @property {Roo.Element} fieldEl
40633 * Element Containing the rendered Field (with label etc.)
40636 * @cfg {Mixed} value A value to initialize this field with.
40641 * @cfg {String} name The field's HTML name attribute.
40644 * @cfg {String} cls A CSS class to apply to the field's underlying element.
40647 loadedValue : false,
40651 initComponent : function(){
40652 Roo.form.Field.superclass.initComponent.call(this);
40656 * Fires when this field receives input focus.
40657 * @param {Roo.form.Field} this
40662 * Fires when this field loses input focus.
40663 * @param {Roo.form.Field} this
40667 * @event specialkey
40668 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
40669 * {@link Roo.EventObject#getKey} to determine which key was pressed.
40670 * @param {Roo.form.Field} this
40671 * @param {Roo.EventObject} e The event object
40676 * Fires just before the field blurs if the field value has changed.
40677 * @param {Roo.form.Field} this
40678 * @param {Mixed} newValue The new value
40679 * @param {Mixed} oldValue The original value
40684 * Fires after the field has been marked as invalid.
40685 * @param {Roo.form.Field} this
40686 * @param {String} msg The validation message
40691 * Fires after the field has been validated with no errors.
40692 * @param {Roo.form.Field} this
40697 * Fires after the key up
40698 * @param {Roo.form.Field} this
40699 * @param {Roo.EventObject} e The event Object
40706 * Returns the name attribute of the field if available
40707 * @return {String} name The field name
40709 getName: function(){
40710 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40714 onRender : function(ct, position){
40715 Roo.form.Field.superclass.onRender.call(this, ct, position);
40717 var cfg = this.getAutoCreate();
40719 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40721 if (!cfg.name.length) {
40724 if(this.inputType){
40725 cfg.type = this.inputType;
40727 this.el = ct.createChild(cfg, position);
40729 var type = this.el.dom.type;
40731 if(type == 'password'){
40734 this.el.addClass('x-form-'+type);
40737 this.el.dom.readOnly = true;
40739 if(this.tabIndex !== undefined){
40740 this.el.dom.setAttribute('tabIndex', this.tabIndex);
40743 this.el.addClass([this.fieldClass, this.cls]);
40748 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40749 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40750 * @return {Roo.form.Field} this
40752 applyTo : function(target){
40753 this.allowDomMove = false;
40754 this.el = Roo.get(target);
40755 this.render(this.el.dom.parentNode);
40760 initValue : function(){
40761 if(this.value !== undefined){
40762 this.setValue(this.value);
40763 }else if(this.el.dom.value.length > 0){
40764 this.setValue(this.el.dom.value);
40769 * Returns true if this field has been changed since it was originally loaded and is not disabled.
40770 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
40772 isDirty : function() {
40773 if(this.disabled) {
40776 return String(this.getValue()) !== String(this.originalValue);
40780 * stores the current value in loadedValue
40782 resetHasChanged : function()
40784 this.loadedValue = String(this.getValue());
40787 * checks the current value against the 'loaded' value.
40788 * Note - will return false if 'resetHasChanged' has not been called first.
40790 hasChanged : function()
40792 if(this.disabled || this.readOnly) {
40795 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40801 afterRender : function(){
40802 Roo.form.Field.superclass.afterRender.call(this);
40807 fireKey : function(e){
40808 //Roo.log('field ' + e.getKey());
40809 if(e.isNavKeyPress()){
40810 this.fireEvent("specialkey", this, e);
40815 * Resets the current field value to the originally loaded value and clears any validation messages
40817 reset : function(){
40818 this.setValue(this.resetValue);
40819 this.originalValue = this.getValue();
40820 this.clearInvalid();
40824 initEvents : function(){
40825 // safari killled keypress - so keydown is now used..
40826 this.el.on("keydown" , this.fireKey, this);
40827 this.el.on("focus", this.onFocus, this);
40828 this.el.on("blur", this.onBlur, this);
40829 this.el.relayEvent('keyup', this);
40831 // reference to original value for reset
40832 this.originalValue = this.getValue();
40833 this.resetValue = this.getValue();
40837 onFocus : function(){
40838 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40839 this.el.addClass(this.focusClass);
40841 if(!this.hasFocus){
40842 this.hasFocus = true;
40843 this.startValue = this.getValue();
40844 this.fireEvent("focus", this);
40848 beforeBlur : Roo.emptyFn,
40851 onBlur : function(){
40853 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40854 this.el.removeClass(this.focusClass);
40856 this.hasFocus = false;
40857 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40860 var v = this.getValue();
40861 if(String(v) !== String(this.startValue)){
40862 this.fireEvent('change', this, v, this.startValue);
40864 this.fireEvent("blur", this);
40868 * Returns whether or not the field value is currently valid
40869 * @param {Boolean} preventMark True to disable marking the field invalid
40870 * @return {Boolean} True if the value is valid, else false
40872 isValid : function(preventMark){
40876 var restore = this.preventMark;
40877 this.preventMark = preventMark === true;
40878 var v = this.validateValue(this.processValue(this.getRawValue()));
40879 this.preventMark = restore;
40884 * Validates the field value
40885 * @return {Boolean} True if the value is valid, else false
40887 validate : function(){
40888 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40889 this.clearInvalid();
40895 processValue : function(value){
40900 // Subclasses should provide the validation implementation by overriding this
40901 validateValue : function(value){
40906 * Mark this field as invalid
40907 * @param {String} msg The validation message
40909 markInvalid : function(msg){
40910 if(!this.rendered || this.preventMark){ // not rendered
40914 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40916 obj.el.addClass(this.invalidClass);
40917 msg = msg || this.invalidText;
40918 switch(this.msgTarget){
40920 obj.el.dom.qtip = msg;
40921 obj.el.dom.qclass = 'x-form-invalid-tip';
40922 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40923 Roo.QuickTips.enable();
40927 this.el.dom.title = msg;
40931 var elp = this.el.findParent('.x-form-element', 5, true);
40932 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40933 this.errorEl.setWidth(elp.getWidth(true)-20);
40935 this.errorEl.update(msg);
40936 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40939 if(!this.errorIcon){
40940 var elp = this.el.findParent('.x-form-element', 5, true);
40941 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40943 this.alignErrorIcon();
40944 this.errorIcon.dom.qtip = msg;
40945 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40946 this.errorIcon.show();
40947 this.on('resize', this.alignErrorIcon, this);
40950 var t = Roo.getDom(this.msgTarget);
40952 t.style.display = this.msgDisplay;
40955 this.fireEvent('invalid', this, msg);
40959 alignErrorIcon : function(){
40960 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40964 * Clear any invalid styles/messages for this field
40966 clearInvalid : function(){
40967 if(!this.rendered || this.preventMark){ // not rendered
40970 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40972 obj.el.removeClass(this.invalidClass);
40973 switch(this.msgTarget){
40975 obj.el.dom.qtip = '';
40978 this.el.dom.title = '';
40982 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40986 if(this.errorIcon){
40987 this.errorIcon.dom.qtip = '';
40988 this.errorIcon.hide();
40989 this.un('resize', this.alignErrorIcon, this);
40993 var t = Roo.getDom(this.msgTarget);
40995 t.style.display = 'none';
40998 this.fireEvent('valid', this);
41002 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
41003 * @return {Mixed} value The field value
41005 getRawValue : function(){
41006 var v = this.el.getValue();
41012 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
41013 * @return {Mixed} value The field value
41015 getValue : function(){
41016 var v = this.el.getValue();
41022 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
41023 * @param {Mixed} value The value to set
41025 setRawValue : function(v){
41026 return this.el.dom.value = (v === null || v === undefined ? '' : v);
41030 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
41031 * @param {Mixed} value The value to set
41033 setValue : function(v){
41036 this.el.dom.value = (v === null || v === undefined ? '' : v);
41041 adjustSize : function(w, h){
41042 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41043 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41047 adjustWidth : function(tag, w){
41048 tag = tag.toLowerCase();
41049 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41050 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41051 if(tag == 'input'){
41054 if(tag == 'textarea'){
41057 }else if(Roo.isOpera){
41058 if(tag == 'input'){
41061 if(tag == 'textarea'){
41071 // anything other than normal should be considered experimental
41072 Roo.form.Field.msgFx = {
41074 show: function(msgEl, f){
41075 msgEl.setDisplayed('block');
41078 hide : function(msgEl, f){
41079 msgEl.setDisplayed(false).update('');
41084 show: function(msgEl, f){
41085 msgEl.slideIn('t', {stopFx:true});
41088 hide : function(msgEl, f){
41089 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41094 show: function(msgEl, f){
41095 msgEl.fixDisplay();
41096 msgEl.alignTo(f.el, 'tl-tr');
41097 msgEl.slideIn('l', {stopFx:true});
41100 hide : function(msgEl, f){
41101 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41106 * Ext JS Library 1.1.1
41107 * Copyright(c) 2006-2007, Ext JS, LLC.
41109 * Originally Released Under LGPL - original licence link has changed is not relivant.
41112 * <script type="text/javascript">
41117 * @class Roo.form.TextField
41118 * @extends Roo.form.Field
41119 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
41120 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41122 * Creates a new TextField
41123 * @param {Object} config Configuration options
41125 Roo.form.TextField = function(config){
41126 Roo.form.TextField.superclass.constructor.call(this, config);
41130 * Fires when the autosize function is triggered. The field may or may not have actually changed size
41131 * according to the default logic, but this event provides a hook for the developer to apply additional
41132 * logic at runtime to resize the field if needed.
41133 * @param {Roo.form.Field} this This text field
41134 * @param {Number} width The new field width
41140 Roo.extend(Roo.form.TextField, Roo.form.Field, {
41142 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41146 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41150 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41154 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41158 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41162 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41164 disableKeyFilter : false,
41166 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41170 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41174 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41176 maxLength : Number.MAX_VALUE,
41178 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41180 minLengthText : "The minimum length for this field is {0}",
41182 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41184 maxLengthText : "The maximum length for this field is {0}",
41186 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41188 selectOnFocus : false,
41190 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
41192 allowLeadingSpace : false,
41194 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41196 blankText : "This field is required",
41198 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41199 * If available, this function will be called only after the basic validators all return true, and will be passed the
41200 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41204 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41205 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41206 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
41210 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41214 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41220 initEvents : function()
41222 if (this.emptyText) {
41223 this.el.attr('placeholder', this.emptyText);
41226 Roo.form.TextField.superclass.initEvents.call(this);
41227 if(this.validationEvent == 'keyup'){
41228 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41229 this.el.on('keyup', this.filterValidation, this);
41231 else if(this.validationEvent !== false){
41232 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41235 if(this.selectOnFocus){
41236 this.on("focus", this.preFocus, this);
41238 if (!this.allowLeadingSpace) {
41239 this.on('blur', this.cleanLeadingSpace, this);
41242 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41243 this.el.on("keypress", this.filterKeys, this);
41246 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
41247 this.el.on("click", this.autoSize, this);
41249 if(this.el.is('input[type=password]') && Roo.isSafari){
41250 this.el.on('keydown', this.SafariOnKeyDown, this);
41254 processValue : function(value){
41255 if(this.stripCharsRe){
41256 var newValue = value.replace(this.stripCharsRe, '');
41257 if(newValue !== value){
41258 this.setRawValue(newValue);
41265 filterValidation : function(e){
41266 if(!e.isNavKeyPress()){
41267 this.validationTask.delay(this.validationDelay);
41272 onKeyUp : function(e){
41273 if(!e.isNavKeyPress()){
41277 // private - clean the leading white space
41278 cleanLeadingSpace : function(e)
41280 if ( this.inputType == 'file') {
41284 this.setValue((this.getValue() + '').replace(/^\s+/,''));
41287 * Resets the current field value to the originally-loaded value and clears any validation messages.
41290 reset : function(){
41291 Roo.form.TextField.superclass.reset.call(this);
41295 preFocus : function(){
41297 if(this.selectOnFocus){
41298 this.el.dom.select();
41304 filterKeys : function(e){
41305 var k = e.getKey();
41306 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41309 var c = e.getCharCode(), cc = String.fromCharCode(c);
41310 if(Roo.isIE && (e.isSpecialKey() || !cc)){
41313 if(!this.maskRe.test(cc)){
41318 setValue : function(v){
41320 Roo.form.TextField.superclass.setValue.apply(this, arguments);
41326 * Validates a value according to the field's validation rules and marks the field as invalid
41327 * if the validation fails
41328 * @param {Mixed} value The value to validate
41329 * @return {Boolean} True if the value is valid, else false
41331 validateValue : function(value){
41332 if(value.length < 1) { // if it's blank
41333 if(this.allowBlank){
41334 this.clearInvalid();
41337 this.markInvalid(this.blankText);
41341 if(value.length < this.minLength){
41342 this.markInvalid(String.format(this.minLengthText, this.minLength));
41345 if(value.length > this.maxLength){
41346 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41350 var vt = Roo.form.VTypes;
41351 if(!vt[this.vtype](value, this)){
41352 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41356 if(typeof this.validator == "function"){
41357 var msg = this.validator(value);
41359 this.markInvalid(msg);
41363 if(this.regex && !this.regex.test(value)){
41364 this.markInvalid(this.regexText);
41371 * Selects text in this field
41372 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41373 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41375 selectText : function(start, end){
41376 var v = this.getRawValue();
41378 start = start === undefined ? 0 : start;
41379 end = end === undefined ? v.length : end;
41380 var d = this.el.dom;
41381 if(d.setSelectionRange){
41382 d.setSelectionRange(start, end);
41383 }else if(d.createTextRange){
41384 var range = d.createTextRange();
41385 range.moveStart("character", start);
41386 range.moveEnd("character", v.length-end);
41393 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41394 * This only takes effect if grow = true, and fires the autosize event.
41396 autoSize : function(){
41397 if(!this.grow || !this.rendered){
41401 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41404 var v = el.dom.value;
41405 var d = document.createElement('div');
41406 d.appendChild(document.createTextNode(v));
41410 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41411 this.el.setWidth(w);
41412 this.fireEvent("autosize", this, w);
41416 SafariOnKeyDown : function(event)
41418 // this is a workaround for a password hang bug on chrome/ webkit.
41420 var isSelectAll = false;
41422 if(this.el.dom.selectionEnd > 0){
41423 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41425 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41426 event.preventDefault();
41431 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41433 event.preventDefault();
41434 // this is very hacky as keydown always get's upper case.
41436 var cc = String.fromCharCode(event.getCharCode());
41439 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
41447 * Ext JS Library 1.1.1
41448 * Copyright(c) 2006-2007, Ext JS, LLC.
41450 * Originally Released Under LGPL - original licence link has changed is not relivant.
41453 * <script type="text/javascript">
41457 * @class Roo.form.Hidden
41458 * @extends Roo.form.TextField
41459 * Simple Hidden element used on forms
41461 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41464 * Creates a new Hidden form element.
41465 * @param {Object} config Configuration options
41470 // easy hidden field...
41471 Roo.form.Hidden = function(config){
41472 Roo.form.Hidden.superclass.constructor.call(this, config);
41475 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41477 inputType: 'hidden',
41480 labelSeparator: '',
41482 itemCls : 'x-form-item-display-none'
41490 * Ext JS Library 1.1.1
41491 * Copyright(c) 2006-2007, Ext JS, LLC.
41493 * Originally Released Under LGPL - original licence link has changed is not relivant.
41496 * <script type="text/javascript">
41500 * @class Roo.form.TriggerField
41501 * @extends Roo.form.TextField
41502 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41503 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41504 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41505 * for which you can provide a custom implementation. For example:
41507 var trigger = new Roo.form.TriggerField();
41508 trigger.onTriggerClick = myTriggerFn;
41509 trigger.applyTo('my-field');
41512 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41513 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41514 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
41515 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41517 * Create a new TriggerField.
41518 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41519 * to the base TextField)
41521 Roo.form.TriggerField = function(config){
41522 this.mimicing = false;
41523 Roo.form.TriggerField.superclass.constructor.call(this, config);
41526 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
41528 * @cfg {String} triggerClass A CSS class to apply to the trigger
41531 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41532 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41534 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41536 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41540 /** @cfg {Boolean} grow @hide */
41541 /** @cfg {Number} growMin @hide */
41542 /** @cfg {Number} growMax @hide */
41548 autoSize: Roo.emptyFn,
41552 deferHeight : true,
41555 actionMode : 'wrap',
41557 onResize : function(w, h){
41558 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41559 if(typeof w == 'number'){
41560 var x = w - this.trigger.getWidth();
41561 this.el.setWidth(this.adjustWidth('input', x));
41562 this.trigger.setStyle('left', x+'px');
41567 adjustSize : Roo.BoxComponent.prototype.adjustSize,
41570 getResizeEl : function(){
41575 getPositionEl : function(){
41580 alignErrorIcon : function(){
41581 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41585 onRender : function(ct, position){
41586 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41587 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41588 this.trigger = this.wrap.createChild(this.triggerConfig ||
41589 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41590 if(this.hideTrigger){
41591 this.trigger.setDisplayed(false);
41593 this.initTrigger();
41595 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41600 initTrigger : function(){
41601 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41602 this.trigger.addClassOnOver('x-form-trigger-over');
41603 this.trigger.addClassOnClick('x-form-trigger-click');
41607 onDestroy : function(){
41609 this.trigger.removeAllListeners();
41610 this.trigger.remove();
41613 this.wrap.remove();
41615 Roo.form.TriggerField.superclass.onDestroy.call(this);
41619 onFocus : function(){
41620 Roo.form.TriggerField.superclass.onFocus.call(this);
41621 if(!this.mimicing){
41622 this.wrap.addClass('x-trigger-wrap-focus');
41623 this.mimicing = true;
41624 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41625 if(this.monitorTab){
41626 this.el.on("keydown", this.checkTab, this);
41632 checkTab : function(e){
41633 if(e.getKey() == e.TAB){
41634 this.triggerBlur();
41639 onBlur : function(){
41644 mimicBlur : function(e, t){
41645 if(!this.wrap.contains(t) && this.validateBlur()){
41646 this.triggerBlur();
41651 triggerBlur : function(){
41652 this.mimicing = false;
41653 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41654 if(this.monitorTab){
41655 this.el.un("keydown", this.checkTab, this);
41657 this.wrap.removeClass('x-trigger-wrap-focus');
41658 Roo.form.TriggerField.superclass.onBlur.call(this);
41662 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41663 validateBlur : function(e, t){
41668 onDisable : function(){
41669 Roo.form.TriggerField.superclass.onDisable.call(this);
41671 this.wrap.addClass('x-item-disabled');
41676 onEnable : function(){
41677 Roo.form.TriggerField.superclass.onEnable.call(this);
41679 this.wrap.removeClass('x-item-disabled');
41684 onShow : function(){
41685 var ae = this.getActionEl();
41688 ae.dom.style.display = '';
41689 ae.dom.style.visibility = 'visible';
41695 onHide : function(){
41696 var ae = this.getActionEl();
41697 ae.dom.style.display = 'none';
41701 * The function that should handle the trigger's click event. This method does nothing by default until overridden
41702 * by an implementing function.
41704 * @param {EventObject} e
41706 onTriggerClick : Roo.emptyFn
41709 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
41710 // to be extended by an implementing class. For an example of implementing this class, see the custom
41711 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41712 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41713 initComponent : function(){
41714 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41716 this.triggerConfig = {
41717 tag:'span', cls:'x-form-twin-triggers', cn:[
41718 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41719 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41723 getTrigger : function(index){
41724 return this.triggers[index];
41727 initTrigger : function(){
41728 var ts = this.trigger.select('.x-form-trigger', true);
41729 this.wrap.setStyle('overflow', 'hidden');
41730 var triggerField = this;
41731 ts.each(function(t, all, index){
41732 t.hide = function(){
41733 var w = triggerField.wrap.getWidth();
41734 this.dom.style.display = 'none';
41735 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41737 t.show = function(){
41738 var w = triggerField.wrap.getWidth();
41739 this.dom.style.display = '';
41740 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41742 var triggerIndex = 'Trigger'+(index+1);
41744 if(this['hide'+triggerIndex]){
41745 t.dom.style.display = 'none';
41747 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41748 t.addClassOnOver('x-form-trigger-over');
41749 t.addClassOnClick('x-form-trigger-click');
41751 this.triggers = ts.elements;
41754 onTrigger1Click : Roo.emptyFn,
41755 onTrigger2Click : Roo.emptyFn
41758 * Ext JS Library 1.1.1
41759 * Copyright(c) 2006-2007, Ext JS, LLC.
41761 * Originally Released Under LGPL - original licence link has changed is not relivant.
41764 * <script type="text/javascript">
41768 * @class Roo.form.TextArea
41769 * @extends Roo.form.TextField
41770 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
41771 * support for auto-sizing.
41773 * Creates a new TextArea
41774 * @param {Object} config Configuration options
41776 Roo.form.TextArea = function(config){
41777 Roo.form.TextArea.superclass.constructor.call(this, config);
41778 // these are provided exchanges for backwards compat
41779 // minHeight/maxHeight were replaced by growMin/growMax to be
41780 // compatible with TextField growing config values
41781 if(this.minHeight !== undefined){
41782 this.growMin = this.minHeight;
41784 if(this.maxHeight !== undefined){
41785 this.growMax = this.maxHeight;
41789 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
41791 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41795 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41799 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41800 * in the field (equivalent to setting overflow: hidden, defaults to false)
41802 preventScrollbars: false,
41804 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41805 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41809 onRender : function(ct, position){
41811 this.defaultAutoCreate = {
41813 style:"width:300px;height:60px;",
41814 autocomplete: "new-password"
41817 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41819 this.textSizeEl = Roo.DomHelper.append(document.body, {
41820 tag: "pre", cls: "x-form-grow-sizer"
41822 if(this.preventScrollbars){
41823 this.el.setStyle("overflow", "hidden");
41825 this.el.setHeight(this.growMin);
41829 onDestroy : function(){
41830 if(this.textSizeEl){
41831 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41833 Roo.form.TextArea.superclass.onDestroy.call(this);
41837 onKeyUp : function(e){
41838 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41844 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41845 * This only takes effect if grow = true, and fires the autosize event if the height changes.
41847 autoSize : function(){
41848 if(!this.grow || !this.textSizeEl){
41852 var v = el.dom.value;
41853 var ts = this.textSizeEl;
41856 ts.appendChild(document.createTextNode(v));
41859 Roo.fly(ts).setWidth(this.el.getWidth());
41861 v = "  ";
41864 v = v.replace(/\n/g, '<p> </p>');
41866 v += " \n ";
41869 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41870 if(h != this.lastHeight){
41871 this.lastHeight = h;
41872 this.el.setHeight(h);
41873 this.fireEvent("autosize", this, h);
41878 * Ext JS Library 1.1.1
41879 * Copyright(c) 2006-2007, Ext JS, LLC.
41881 * Originally Released Under LGPL - original licence link has changed is not relivant.
41884 * <script type="text/javascript">
41889 * @class Roo.form.NumberField
41890 * @extends Roo.form.TextField
41891 * Numeric text field that provides automatic keystroke filtering and numeric validation.
41893 * Creates a new NumberField
41894 * @param {Object} config Configuration options
41896 Roo.form.NumberField = function(config){
41897 Roo.form.NumberField.superclass.constructor.call(this, config);
41900 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
41902 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41904 fieldClass: "x-form-field x-form-num-field",
41906 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41908 allowDecimals : true,
41910 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41912 decimalSeparator : ".",
41914 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41916 decimalPrecision : 2,
41918 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41920 allowNegative : true,
41922 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41924 minValue : Number.NEGATIVE_INFINITY,
41926 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41928 maxValue : Number.MAX_VALUE,
41930 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41932 minText : "The minimum value for this field is {0}",
41934 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41936 maxText : "The maximum value for this field is {0}",
41938 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41939 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41941 nanText : "{0} is not a valid number",
41944 initEvents : function(){
41945 Roo.form.NumberField.superclass.initEvents.call(this);
41946 var allowed = "0123456789";
41947 if(this.allowDecimals){
41948 allowed += this.decimalSeparator;
41950 if(this.allowNegative){
41953 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41954 var keyPress = function(e){
41955 var k = e.getKey();
41956 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41959 var c = e.getCharCode();
41960 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41964 this.el.on("keypress", keyPress, this);
41968 validateValue : function(value){
41969 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41972 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41975 var num = this.parseValue(value);
41977 this.markInvalid(String.format(this.nanText, value));
41980 if(num < this.minValue){
41981 this.markInvalid(String.format(this.minText, this.minValue));
41984 if(num > this.maxValue){
41985 this.markInvalid(String.format(this.maxText, this.maxValue));
41991 getValue : function(){
41992 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41996 parseValue : function(value){
41997 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41998 return isNaN(value) ? '' : value;
42002 fixPrecision : function(value){
42003 var nan = isNaN(value);
42004 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42005 return nan ? '' : value;
42007 return parseFloat(value).toFixed(this.decimalPrecision);
42010 setValue : function(v){
42011 v = this.fixPrecision(v);
42012 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
42016 decimalPrecisionFcn : function(v){
42017 return Math.floor(v);
42020 beforeBlur : function(){
42021 var v = this.parseValue(this.getRawValue());
42028 * Ext JS Library 1.1.1
42029 * Copyright(c) 2006-2007, Ext JS, LLC.
42031 * Originally Released Under LGPL - original licence link has changed is not relivant.
42034 * <script type="text/javascript">
42038 * @class Roo.form.DateField
42039 * @extends Roo.form.TriggerField
42040 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42042 * Create a new DateField
42043 * @param {Object} config
42045 Roo.form.DateField = function(config)
42047 Roo.form.DateField.superclass.constructor.call(this, config);
42053 * Fires when a date is selected
42054 * @param {Roo.form.DateField} combo This combo box
42055 * @param {Date} date The date selected
42062 if(typeof this.minValue == "string") {
42063 this.minValue = this.parseDate(this.minValue);
42065 if(typeof this.maxValue == "string") {
42066 this.maxValue = this.parseDate(this.maxValue);
42068 this.ddMatch = null;
42069 if(this.disabledDates){
42070 var dd = this.disabledDates;
42072 for(var i = 0; i < dd.length; i++){
42074 if(i != dd.length-1) {
42078 this.ddMatch = new RegExp(re + ")");
42082 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
42084 * @cfg {String} format
42085 * The default date format string which can be overriden for localization support. The format must be
42086 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42090 * @cfg {String} altFormats
42091 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42092 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42094 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42096 * @cfg {Array} disabledDays
42097 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42099 disabledDays : null,
42101 * @cfg {String} disabledDaysText
42102 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42104 disabledDaysText : "Disabled",
42106 * @cfg {Array} disabledDates
42107 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42108 * expression so they are very powerful. Some examples:
42110 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42111 * <li>["03/08", "09/16"] would disable those days for every year</li>
42112 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42113 * <li>["03/../2006"] would disable every day in March 2006</li>
42114 * <li>["^03"] would disable every day in every March</li>
42116 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42117 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42119 disabledDates : null,
42121 * @cfg {String} disabledDatesText
42122 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42124 disabledDatesText : "Disabled",
42128 * @cfg {Date/String} zeroValue
42129 * if the date is less that this number, then the field is rendered as empty
42132 zeroValue : '1800-01-01',
42136 * @cfg {Date/String} minValue
42137 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42138 * valid format (defaults to null).
42142 * @cfg {Date/String} maxValue
42143 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42144 * valid format (defaults to null).
42148 * @cfg {String} minText
42149 * The error text to display when the date in the cell is before minValue (defaults to
42150 * 'The date in this field must be after {minValue}').
42152 minText : "The date in this field must be equal to or after {0}",
42154 * @cfg {String} maxText
42155 * The error text to display when the date in the cell is after maxValue (defaults to
42156 * 'The date in this field must be before {maxValue}').
42158 maxText : "The date in this field must be equal to or before {0}",
42160 * @cfg {String} invalidText
42161 * The error text to display when the date in the field is invalid (defaults to
42162 * '{value} is not a valid date - it must be in the format {format}').
42164 invalidText : "{0} is not a valid date - it must be in the format {1}",
42166 * @cfg {String} triggerClass
42167 * An additional CSS class used to style the trigger button. The trigger will always get the
42168 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42169 * which displays a calendar icon).
42171 triggerClass : 'x-form-date-trigger',
42175 * @cfg {Boolean} useIso
42176 * if enabled, then the date field will use a hidden field to store the
42177 * real value as iso formated date. default (false)
42181 * @cfg {String/Object} autoCreate
42182 * A DomHelper element spec, or true for a default element spec (defaults to
42183 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42186 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42189 hiddenField: false,
42191 onRender : function(ct, position)
42193 Roo.form.DateField.superclass.onRender.call(this, ct, position);
42195 //this.el.dom.removeAttribute('name');
42196 Roo.log("Changing name?");
42197 this.el.dom.setAttribute('name', this.name + '____hidden___' );
42198 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42200 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42201 // prevent input submission
42202 this.hiddenName = this.name;
42209 validateValue : function(value)
42211 value = this.formatDate(value);
42212 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42213 Roo.log('super failed');
42216 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42219 var svalue = value;
42220 value = this.parseDate(value);
42222 Roo.log('parse date failed' + svalue);
42223 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42226 var time = value.getTime();
42227 if(this.minValue && time < this.minValue.getTime()){
42228 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42231 if(this.maxValue && time > this.maxValue.getTime()){
42232 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42235 if(this.disabledDays){
42236 var day = value.getDay();
42237 for(var i = 0; i < this.disabledDays.length; i++) {
42238 if(day === this.disabledDays[i]){
42239 this.markInvalid(this.disabledDaysText);
42244 var fvalue = this.formatDate(value);
42245 if(this.ddMatch && this.ddMatch.test(fvalue)){
42246 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42253 // Provides logic to override the default TriggerField.validateBlur which just returns true
42254 validateBlur : function(){
42255 return !this.menu || !this.menu.isVisible();
42258 getName: function()
42260 // returns hidden if it's set..
42261 if (!this.rendered) {return ''};
42262 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
42267 * Returns the current date value of the date field.
42268 * @return {Date} The date value
42270 getValue : function(){
42272 return this.hiddenField ?
42273 this.hiddenField.value :
42274 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42278 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42279 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42280 * (the default format used is "m/d/y").
42283 //All of these calls set the same date value (May 4, 2006)
42285 //Pass a date object:
42286 var dt = new Date('5/4/06');
42287 dateField.setValue(dt);
42289 //Pass a date string (default format):
42290 dateField.setValue('5/4/06');
42292 //Pass a date string (custom format):
42293 dateField.format = 'Y-m-d';
42294 dateField.setValue('2006-5-4');
42296 * @param {String/Date} date The date or valid date string
42298 setValue : function(date){
42299 if (this.hiddenField) {
42300 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42302 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42303 // make sure the value field is always stored as a date..
42304 this.value = this.parseDate(date);
42310 parseDate : function(value){
42312 if (value instanceof Date) {
42313 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42320 if(!value || value instanceof Date){
42323 var v = Date.parseDate(value, this.format);
42324 if (!v && this.useIso) {
42325 v = Date.parseDate(value, 'Y-m-d');
42327 if(!v && this.altFormats){
42328 if(!this.altFormatsArray){
42329 this.altFormatsArray = this.altFormats.split("|");
42331 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42332 v = Date.parseDate(value, this.altFormatsArray[i]);
42335 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42342 formatDate : function(date, fmt){
42343 return (!date || !(date instanceof Date)) ?
42344 date : date.dateFormat(fmt || this.format);
42349 select: function(m, d){
42352 this.fireEvent('select', this, d);
42354 show : function(){ // retain focus styling
42358 this.focus.defer(10, this);
42359 var ml = this.menuListeners;
42360 this.menu.un("select", ml.select, this);
42361 this.menu.un("show", ml.show, this);
42362 this.menu.un("hide", ml.hide, this);
42367 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42368 onTriggerClick : function(){
42372 if(this.menu == null){
42373 this.menu = new Roo.menu.DateMenu();
42375 Roo.apply(this.menu.picker, {
42376 showClear: this.allowBlank,
42377 minDate : this.minValue,
42378 maxDate : this.maxValue,
42379 disabledDatesRE : this.ddMatch,
42380 disabledDatesText : this.disabledDatesText,
42381 disabledDays : this.disabledDays,
42382 disabledDaysText : this.disabledDaysText,
42383 format : this.useIso ? 'Y-m-d' : this.format,
42384 minText : String.format(this.minText, this.formatDate(this.minValue)),
42385 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42387 this.menu.on(Roo.apply({}, this.menuListeners, {
42390 this.menu.picker.setValue(this.getValue() || new Date());
42391 this.menu.show(this.el, "tl-bl?");
42394 beforeBlur : function(){
42395 var v = this.parseDate(this.getRawValue());
42405 isDirty : function() {
42406 if(this.disabled) {
42410 if(typeof(this.startValue) === 'undefined'){
42414 return String(this.getValue()) !== String(this.startValue);
42418 cleanLeadingSpace : function(e)
42425 * Ext JS Library 1.1.1
42426 * Copyright(c) 2006-2007, Ext JS, LLC.
42428 * Originally Released Under LGPL - original licence link has changed is not relivant.
42431 * <script type="text/javascript">
42435 * @class Roo.form.MonthField
42436 * @extends Roo.form.TriggerField
42437 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42439 * Create a new MonthField
42440 * @param {Object} config
42442 Roo.form.MonthField = function(config){
42444 Roo.form.MonthField.superclass.constructor.call(this, config);
42450 * Fires when a date is selected
42451 * @param {Roo.form.MonthFieeld} combo This combo box
42452 * @param {Date} date The date selected
42459 if(typeof this.minValue == "string") {
42460 this.minValue = this.parseDate(this.minValue);
42462 if(typeof this.maxValue == "string") {
42463 this.maxValue = this.parseDate(this.maxValue);
42465 this.ddMatch = null;
42466 if(this.disabledDates){
42467 var dd = this.disabledDates;
42469 for(var i = 0; i < dd.length; i++){
42471 if(i != dd.length-1) {
42475 this.ddMatch = new RegExp(re + ")");
42479 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
42481 * @cfg {String} format
42482 * The default date format string which can be overriden for localization support. The format must be
42483 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42487 * @cfg {String} altFormats
42488 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42489 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42491 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42493 * @cfg {Array} disabledDays
42494 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42496 disabledDays : [0,1,2,3,4,5,6],
42498 * @cfg {String} disabledDaysText
42499 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42501 disabledDaysText : "Disabled",
42503 * @cfg {Array} disabledDates
42504 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42505 * expression so they are very powerful. Some examples:
42507 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42508 * <li>["03/08", "09/16"] would disable those days for every year</li>
42509 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42510 * <li>["03/../2006"] would disable every day in March 2006</li>
42511 * <li>["^03"] would disable every day in every March</li>
42513 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42514 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42516 disabledDates : null,
42518 * @cfg {String} disabledDatesText
42519 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42521 disabledDatesText : "Disabled",
42523 * @cfg {Date/String} minValue
42524 * The minimum allowed date. Can be either a Javascript date object or a string date in a
42525 * valid format (defaults to null).
42529 * @cfg {Date/String} maxValue
42530 * The maximum allowed date. Can be either a Javascript date object or a string date in a
42531 * valid format (defaults to null).
42535 * @cfg {String} minText
42536 * The error text to display when the date in the cell is before minValue (defaults to
42537 * 'The date in this field must be after {minValue}').
42539 minText : "The date in this field must be equal to or after {0}",
42541 * @cfg {String} maxTextf
42542 * The error text to display when the date in the cell is after maxValue (defaults to
42543 * 'The date in this field must be before {maxValue}').
42545 maxText : "The date in this field must be equal to or before {0}",
42547 * @cfg {String} invalidText
42548 * The error text to display when the date in the field is invalid (defaults to
42549 * '{value} is not a valid date - it must be in the format {format}').
42551 invalidText : "{0} is not a valid date - it must be in the format {1}",
42553 * @cfg {String} triggerClass
42554 * An additional CSS class used to style the trigger button. The trigger will always get the
42555 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42556 * which displays a calendar icon).
42558 triggerClass : 'x-form-date-trigger',
42562 * @cfg {Boolean} useIso
42563 * if enabled, then the date field will use a hidden field to store the
42564 * real value as iso formated date. default (true)
42568 * @cfg {String/Object} autoCreate
42569 * A DomHelper element spec, or true for a default element spec (defaults to
42570 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42573 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42576 hiddenField: false,
42578 hideMonthPicker : false,
42580 onRender : function(ct, position)
42582 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42584 this.el.dom.removeAttribute('name');
42585 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42587 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42588 // prevent input submission
42589 this.hiddenName = this.name;
42596 validateValue : function(value)
42598 value = this.formatDate(value);
42599 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42602 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42605 var svalue = value;
42606 value = this.parseDate(value);
42608 this.markInvalid(String.format(this.invalidText, svalue, this.format));
42611 var time = value.getTime();
42612 if(this.minValue && time < this.minValue.getTime()){
42613 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42616 if(this.maxValue && time > this.maxValue.getTime()){
42617 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42620 /*if(this.disabledDays){
42621 var day = value.getDay();
42622 for(var i = 0; i < this.disabledDays.length; i++) {
42623 if(day === this.disabledDays[i]){
42624 this.markInvalid(this.disabledDaysText);
42630 var fvalue = this.formatDate(value);
42631 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42632 this.markInvalid(String.format(this.disabledDatesText, fvalue));
42640 // Provides logic to override the default TriggerField.validateBlur which just returns true
42641 validateBlur : function(){
42642 return !this.menu || !this.menu.isVisible();
42646 * Returns the current date value of the date field.
42647 * @return {Date} The date value
42649 getValue : function(){
42653 return this.hiddenField ?
42654 this.hiddenField.value :
42655 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42659 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
42660 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42661 * (the default format used is "m/d/y").
42664 //All of these calls set the same date value (May 4, 2006)
42666 //Pass a date object:
42667 var dt = new Date('5/4/06');
42668 monthField.setValue(dt);
42670 //Pass a date string (default format):
42671 monthField.setValue('5/4/06');
42673 //Pass a date string (custom format):
42674 monthField.format = 'Y-m-d';
42675 monthField.setValue('2006-5-4');
42677 * @param {String/Date} date The date or valid date string
42679 setValue : function(date){
42680 Roo.log('month setValue' + date);
42681 // can only be first of month..
42683 var val = this.parseDate(date);
42685 if (this.hiddenField) {
42686 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42688 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42689 this.value = this.parseDate(date);
42693 parseDate : function(value){
42694 if(!value || value instanceof Date){
42695 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42698 var v = Date.parseDate(value, this.format);
42699 if (!v && this.useIso) {
42700 v = Date.parseDate(value, 'Y-m-d');
42704 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42708 if(!v && this.altFormats){
42709 if(!this.altFormatsArray){
42710 this.altFormatsArray = this.altFormats.split("|");
42712 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42713 v = Date.parseDate(value, this.altFormatsArray[i]);
42720 formatDate : function(date, fmt){
42721 return (!date || !(date instanceof Date)) ?
42722 date : date.dateFormat(fmt || this.format);
42727 select: function(m, d){
42729 this.fireEvent('select', this, d);
42731 show : function(){ // retain focus styling
42735 this.focus.defer(10, this);
42736 var ml = this.menuListeners;
42737 this.menu.un("select", ml.select, this);
42738 this.menu.un("show", ml.show, this);
42739 this.menu.un("hide", ml.hide, this);
42743 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42744 onTriggerClick : function(){
42748 if(this.menu == null){
42749 this.menu = new Roo.menu.DateMenu();
42753 Roo.apply(this.menu.picker, {
42755 showClear: this.allowBlank,
42756 minDate : this.minValue,
42757 maxDate : this.maxValue,
42758 disabledDatesRE : this.ddMatch,
42759 disabledDatesText : this.disabledDatesText,
42761 format : this.useIso ? 'Y-m-d' : this.format,
42762 minText : String.format(this.minText, this.formatDate(this.minValue)),
42763 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42766 this.menu.on(Roo.apply({}, this.menuListeners, {
42774 // hide month picker get's called when we called by 'before hide';
42776 var ignorehide = true;
42777 p.hideMonthPicker = function(disableAnim){
42781 if(this.monthPicker){
42782 Roo.log("hideMonthPicker called");
42783 if(disableAnim === true){
42784 this.monthPicker.hide();
42786 this.monthPicker.slideOut('t', {duration:.2});
42787 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42788 p.fireEvent("select", this, this.value);
42794 Roo.log('picker set value');
42795 Roo.log(this.getValue());
42796 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42797 m.show(this.el, 'tl-bl?');
42798 ignorehide = false;
42799 // this will trigger hideMonthPicker..
42802 // hidden the day picker
42803 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42809 p.showMonthPicker.defer(100, p);
42815 beforeBlur : function(){
42816 var v = this.parseDate(this.getRawValue());
42822 /** @cfg {Boolean} grow @hide */
42823 /** @cfg {Number} growMin @hide */
42824 /** @cfg {Number} growMax @hide */
42831 * Ext JS Library 1.1.1
42832 * Copyright(c) 2006-2007, Ext JS, LLC.
42834 * Originally Released Under LGPL - original licence link has changed is not relivant.
42837 * <script type="text/javascript">
42842 * @class Roo.form.ComboBox
42843 * @extends Roo.form.TriggerField
42844 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42846 * Create a new ComboBox.
42847 * @param {Object} config Configuration options
42849 Roo.form.ComboBox = function(config){
42850 Roo.form.ComboBox.superclass.constructor.call(this, config);
42854 * Fires when the dropdown list is expanded
42855 * @param {Roo.form.ComboBox} combo This combo box
42860 * Fires when the dropdown list is collapsed
42861 * @param {Roo.form.ComboBox} combo This combo box
42865 * @event beforeselect
42866 * Fires before a list item is selected. Return false to cancel the selection.
42867 * @param {Roo.form.ComboBox} combo This combo box
42868 * @param {Roo.data.Record} record The data record returned from the underlying store
42869 * @param {Number} index The index of the selected item in the dropdown list
42871 'beforeselect' : true,
42874 * Fires when a list item is selected
42875 * @param {Roo.form.ComboBox} combo This combo box
42876 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42877 * @param {Number} index The index of the selected item in the dropdown list
42881 * @event beforequery
42882 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42883 * The event object passed has these properties:
42884 * @param {Roo.form.ComboBox} combo This combo box
42885 * @param {String} query The query
42886 * @param {Boolean} forceAll true to force "all" query
42887 * @param {Boolean} cancel true to cancel the query
42888 * @param {Object} e The query event object
42890 'beforequery': true,
42893 * Fires when the 'add' icon is pressed (add a listener to enable add button)
42894 * @param {Roo.form.ComboBox} combo This combo box
42899 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42900 * @param {Roo.form.ComboBox} combo This combo box
42901 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42907 if(this.transform){
42908 this.allowDomMove = false;
42909 var s = Roo.getDom(this.transform);
42910 if(!this.hiddenName){
42911 this.hiddenName = s.name;
42914 this.mode = 'local';
42915 var d = [], opts = s.options;
42916 for(var i = 0, len = opts.length;i < len; i++){
42918 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42920 this.value = value;
42922 d.push([value, o.text]);
42924 this.store = new Roo.data.SimpleStore({
42926 fields: ['value', 'text'],
42929 this.valueField = 'value';
42930 this.displayField = 'text';
42932 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42933 if(!this.lazyRender){
42934 this.target = true;
42935 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42936 s.parentNode.removeChild(s); // remove it
42937 this.render(this.el.parentNode);
42939 s.parentNode.removeChild(s); // remove it
42944 this.store = Roo.factory(this.store, Roo.data);
42947 this.selectedIndex = -1;
42948 if(this.mode == 'local'){
42949 if(config.queryDelay === undefined){
42950 this.queryDelay = 10;
42952 if(config.minChars === undefined){
42958 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42960 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42963 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42964 * rendering into an Roo.Editor, defaults to false)
42967 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42968 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42971 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42974 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42975 * the dropdown list (defaults to undefined, with no header element)
42979 * @cfg {String/Roo.Template} tpl The template to use to render the output
42983 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42985 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42987 listWidth: undefined,
42989 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42990 * mode = 'remote' or 'text' if mode = 'local')
42992 displayField: undefined,
42994 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42995 * mode = 'remote' or 'value' if mode = 'local').
42996 * Note: use of a valueField requires the user make a selection
42997 * in order for a value to be mapped.
42999 valueField: undefined,
43003 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
43004 * field's data value (defaults to the underlying DOM element's name)
43006 hiddenName: undefined,
43008 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
43012 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
43014 selectedClass: 'x-combo-selected',
43016 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
43017 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
43018 * which displays a downward arrow icon).
43020 triggerClass : 'x-form-arrow-trigger',
43022 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
43026 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
43027 * anchor positions (defaults to 'tl-bl')
43029 listAlign: 'tl-bl?',
43031 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43035 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
43036 * query specified by the allQuery config option (defaults to 'query')
43038 triggerAction: 'query',
43040 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43041 * (defaults to 4, does not apply if editable = false)
43045 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43046 * delay (typeAheadDelay) if it matches a known value (defaults to false)
43050 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43051 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43055 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43056 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
43060 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
43061 * when editable = true (defaults to false)
43063 selectOnFocus:false,
43065 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43067 queryParam: 'query',
43069 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
43070 * when mode = 'remote' (defaults to 'Loading...')
43072 loadingText: 'Loading...',
43074 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43078 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43082 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43083 * traditional select (defaults to true)
43087 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43091 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43095 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43096 * listWidth has a higher value)
43100 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43101 * allow the user to set arbitrary text into the field (defaults to false)
43103 forceSelection:false,
43105 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43106 * if typeAhead = true (defaults to 250)
43108 typeAheadDelay : 250,
43110 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43111 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43113 valueNotFoundText : undefined,
43115 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43117 blockFocus : false,
43120 * @cfg {Boolean} disableClear Disable showing of clear button.
43122 disableClear : false,
43124 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
43126 alwaysQuery : false,
43132 // element that contains real text value.. (when hidden is used..)
43135 onRender : function(ct, position)
43137 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43139 if(this.hiddenName){
43140 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
43142 this.hiddenField.value =
43143 this.hiddenValue !== undefined ? this.hiddenValue :
43144 this.value !== undefined ? this.value : '';
43146 // prevent input submission
43147 this.el.dom.removeAttribute('name');
43153 this.el.dom.setAttribute('autocomplete', 'off');
43156 var cls = 'x-combo-list';
43158 this.list = new Roo.Layer({
43159 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43162 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43163 this.list.setWidth(lw);
43164 this.list.swallowEvent('mousewheel');
43165 this.assetHeight = 0;
43168 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43169 this.assetHeight += this.header.getHeight();
43172 this.innerList = this.list.createChild({cls:cls+'-inner'});
43173 this.innerList.on('mouseover', this.onViewOver, this);
43174 this.innerList.on('mousemove', this.onViewMove, this);
43175 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43177 if(this.allowBlank && !this.pageSize && !this.disableClear){
43178 this.footer = this.list.createChild({cls:cls+'-ft'});
43179 this.pageTb = new Roo.Toolbar(this.footer);
43183 this.footer = this.list.createChild({cls:cls+'-ft'});
43184 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43185 {pageSize: this.pageSize});
43189 if (this.pageTb && this.allowBlank && !this.disableClear) {
43191 this.pageTb.add(new Roo.Toolbar.Fill(), {
43192 cls: 'x-btn-icon x-btn-clear',
43194 handler: function()
43197 _this.clearValue();
43198 _this.onSelect(false, -1);
43203 this.assetHeight += this.footer.getHeight();
43208 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43211 this.view = new Roo.View(this.innerList, this.tpl, {
43214 selectedClass: this.selectedClass
43217 this.view.on('click', this.onViewClick, this);
43219 this.store.on('beforeload', this.onBeforeLoad, this);
43220 this.store.on('load', this.onLoad, this);
43221 this.store.on('loadexception', this.onLoadException, this);
43223 if(this.resizable){
43224 this.resizer = new Roo.Resizable(this.list, {
43225 pinned:true, handles:'se'
43227 this.resizer.on('resize', function(r, w, h){
43228 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43229 this.listWidth = w;
43230 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43231 this.restrictHeight();
43233 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43235 if(!this.editable){
43236 this.editable = true;
43237 this.setEditable(false);
43241 if (typeof(this.events.add.listeners) != 'undefined') {
43243 this.addicon = this.wrap.createChild(
43244 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
43246 this.addicon.on('click', function(e) {
43247 this.fireEvent('add', this);
43250 if (typeof(this.events.edit.listeners) != 'undefined') {
43252 this.editicon = this.wrap.createChild(
43253 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
43254 if (this.addicon) {
43255 this.editicon.setStyle('margin-left', '40px');
43257 this.editicon.on('click', function(e) {
43259 // we fire even if inothing is selected..
43260 this.fireEvent('edit', this, this.lastData );
43270 initEvents : function(){
43271 Roo.form.ComboBox.superclass.initEvents.call(this);
43273 this.keyNav = new Roo.KeyNav(this.el, {
43274 "up" : function(e){
43275 this.inKeyMode = true;
43279 "down" : function(e){
43280 if(!this.isExpanded()){
43281 this.onTriggerClick();
43283 this.inKeyMode = true;
43288 "enter" : function(e){
43289 this.onViewClick();
43293 "esc" : function(e){
43297 "tab" : function(e){
43298 this.onViewClick(false);
43299 this.fireEvent("specialkey", this, e);
43305 doRelay : function(foo, bar, hname){
43306 if(hname == 'down' || this.scope.isExpanded()){
43307 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43314 this.queryDelay = Math.max(this.queryDelay || 10,
43315 this.mode == 'local' ? 10 : 250);
43316 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43317 if(this.typeAhead){
43318 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43320 if(this.editable !== false){
43321 this.el.on("keyup", this.onKeyUp, this);
43323 if(this.forceSelection){
43324 this.on('blur', this.doForce, this);
43328 onDestroy : function(){
43330 this.view.setStore(null);
43331 this.view.el.removeAllListeners();
43332 this.view.el.remove();
43333 this.view.purgeListeners();
43336 this.list.destroy();
43339 this.store.un('beforeload', this.onBeforeLoad, this);
43340 this.store.un('load', this.onLoad, this);
43341 this.store.un('loadexception', this.onLoadException, this);
43343 Roo.form.ComboBox.superclass.onDestroy.call(this);
43347 fireKey : function(e){
43348 if(e.isNavKeyPress() && !this.list.isVisible()){
43349 this.fireEvent("specialkey", this, e);
43354 onResize: function(w, h){
43355 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43357 if(typeof w != 'number'){
43358 // we do not handle it!?!?
43361 var tw = this.trigger.getWidth();
43362 tw += this.addicon ? this.addicon.getWidth() : 0;
43363 tw += this.editicon ? this.editicon.getWidth() : 0;
43365 this.el.setWidth( this.adjustWidth('input', x));
43367 this.trigger.setStyle('left', x+'px');
43369 if(this.list && this.listWidth === undefined){
43370 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43371 this.list.setWidth(lw);
43372 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43380 * Allow or prevent the user from directly editing the field text. If false is passed,
43381 * the user will only be able to select from the items defined in the dropdown list. This method
43382 * is the runtime equivalent of setting the 'editable' config option at config time.
43383 * @param {Boolean} value True to allow the user to directly edit the field text
43385 setEditable : function(value){
43386 if(value == this.editable){
43389 this.editable = value;
43391 this.el.dom.setAttribute('readOnly', true);
43392 this.el.on('mousedown', this.onTriggerClick, this);
43393 this.el.addClass('x-combo-noedit');
43395 this.el.dom.setAttribute('readOnly', false);
43396 this.el.un('mousedown', this.onTriggerClick, this);
43397 this.el.removeClass('x-combo-noedit');
43402 onBeforeLoad : function(){
43403 if(!this.hasFocus){
43406 this.innerList.update(this.loadingText ?
43407 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43408 this.restrictHeight();
43409 this.selectedIndex = -1;
43413 onLoad : function(){
43414 if(!this.hasFocus){
43417 if(this.store.getCount() > 0){
43419 this.restrictHeight();
43420 if(this.lastQuery == this.allQuery){
43422 this.el.dom.select();
43424 if(!this.selectByValue(this.value, true)){
43425 this.select(0, true);
43429 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43430 this.taTask.delay(this.typeAheadDelay);
43434 this.onEmptyResults();
43439 onLoadException : function()
43442 Roo.log(this.store.reader.jsonData);
43443 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43444 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43450 onTypeAhead : function(){
43451 if(this.store.getCount() > 0){
43452 var r = this.store.getAt(0);
43453 var newValue = r.data[this.displayField];
43454 var len = newValue.length;
43455 var selStart = this.getRawValue().length;
43456 if(selStart != len){
43457 this.setRawValue(newValue);
43458 this.selectText(selStart, newValue.length);
43464 onSelect : function(record, index){
43465 if(this.fireEvent('beforeselect', this, record, index) !== false){
43466 this.setFromData(index > -1 ? record.data : false);
43468 this.fireEvent('select', this, record, index);
43473 * Returns the currently selected field value or empty string if no value is set.
43474 * @return {String} value The selected value
43476 getValue : function(){
43477 if(this.valueField){
43478 return typeof this.value != 'undefined' ? this.value : '';
43480 return Roo.form.ComboBox.superclass.getValue.call(this);
43484 * Clears any text/value currently set in the field
43486 clearValue : function(){
43487 if(this.hiddenField){
43488 this.hiddenField.value = '';
43491 this.setRawValue('');
43492 this.lastSelectionText = '';
43497 * Sets the specified value into the field. If the value finds a match, the corresponding record text
43498 * will be displayed in the field. If the value does not match the data value of an existing item,
43499 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43500 * Otherwise the field will be blank (although the value will still be set).
43501 * @param {String} value The value to match
43503 setValue : function(v){
43505 if(this.valueField){
43506 var r = this.findRecord(this.valueField, v);
43508 text = r.data[this.displayField];
43509 }else if(this.valueNotFoundText !== undefined){
43510 text = this.valueNotFoundText;
43513 this.lastSelectionText = text;
43514 if(this.hiddenField){
43515 this.hiddenField.value = v;
43517 Roo.form.ComboBox.superclass.setValue.call(this, text);
43521 * @property {Object} the last set data for the element
43526 * Sets the value of the field based on a object which is related to the record format for the store.
43527 * @param {Object} value the value to set as. or false on reset?
43529 setFromData : function(o){
43530 var dv = ''; // display value
43531 var vv = ''; // value value..
43533 if (this.displayField) {
43534 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43536 // this is an error condition!!!
43537 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
43540 if(this.valueField){
43541 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43543 if(this.hiddenField){
43544 this.hiddenField.value = vv;
43546 this.lastSelectionText = dv;
43547 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43551 // no hidden field.. - we store the value in 'value', but still display
43552 // display field!!!!
43553 this.lastSelectionText = dv;
43554 Roo.form.ComboBox.superclass.setValue.call(this, dv);
43560 reset : function(){
43561 // overridden so that last data is reset..
43562 this.setValue(this.resetValue);
43563 this.originalValue = this.getValue();
43564 this.clearInvalid();
43565 this.lastData = false;
43567 this.view.clearSelections();
43571 findRecord : function(prop, value){
43573 if(this.store.getCount() > 0){
43574 this.store.each(function(r){
43575 if(r.data[prop] == value){
43585 getName: function()
43587 // returns hidden if it's set..
43588 if (!this.rendered) {return ''};
43589 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
43593 onViewMove : function(e, t){
43594 this.inKeyMode = false;
43598 onViewOver : function(e, t){
43599 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43602 var item = this.view.findItemFromChild(t);
43604 var index = this.view.indexOf(item);
43605 this.select(index, false);
43610 onViewClick : function(doFocus)
43612 var index = this.view.getSelectedIndexes()[0];
43613 var r = this.store.getAt(index);
43615 this.onSelect(r, index);
43617 if(doFocus !== false && !this.blockFocus){
43623 restrictHeight : function(){
43624 this.innerList.dom.style.height = '';
43625 var inner = this.innerList.dom;
43626 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43627 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43628 this.list.beginUpdate();
43629 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43630 this.list.alignTo(this.el, this.listAlign);
43631 this.list.endUpdate();
43635 onEmptyResults : function(){
43640 * Returns true if the dropdown list is expanded, else false.
43642 isExpanded : function(){
43643 return this.list.isVisible();
43647 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43648 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43649 * @param {String} value The data value of the item to select
43650 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43651 * selected item if it is not currently in view (defaults to true)
43652 * @return {Boolean} True if the value matched an item in the list, else false
43654 selectByValue : function(v, scrollIntoView){
43655 if(v !== undefined && v !== null){
43656 var r = this.findRecord(this.valueField || this.displayField, v);
43658 this.select(this.store.indexOf(r), scrollIntoView);
43666 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43667 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43668 * @param {Number} index The zero-based index of the list item to select
43669 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43670 * selected item if it is not currently in view (defaults to true)
43672 select : function(index, scrollIntoView){
43673 this.selectedIndex = index;
43674 this.view.select(index);
43675 if(scrollIntoView !== false){
43676 var el = this.view.getNode(index);
43678 this.innerList.scrollChildIntoView(el, false);
43684 selectNext : function(){
43685 var ct = this.store.getCount();
43687 if(this.selectedIndex == -1){
43689 }else if(this.selectedIndex < ct-1){
43690 this.select(this.selectedIndex+1);
43696 selectPrev : function(){
43697 var ct = this.store.getCount();
43699 if(this.selectedIndex == -1){
43701 }else if(this.selectedIndex != 0){
43702 this.select(this.selectedIndex-1);
43708 onKeyUp : function(e){
43709 if(this.editable !== false && !e.isSpecialKey()){
43710 this.lastKey = e.getKey();
43711 this.dqTask.delay(this.queryDelay);
43716 validateBlur : function(){
43717 return !this.list || !this.list.isVisible();
43721 initQuery : function(){
43722 this.doQuery(this.getRawValue());
43726 doForce : function(){
43727 if(this.el.dom.value.length > 0){
43728 this.el.dom.value =
43729 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43735 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
43736 * query allowing the query action to be canceled if needed.
43737 * @param {String} query The SQL query to execute
43738 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43739 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
43740 * saved in the current store (defaults to false)
43742 doQuery : function(q, forceAll){
43743 if(q === undefined || q === null){
43748 forceAll: forceAll,
43752 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43756 forceAll = qe.forceAll;
43757 if(forceAll === true || (q.length >= this.minChars)){
43758 if(this.lastQuery != q || this.alwaysQuery){
43759 this.lastQuery = q;
43760 if(this.mode == 'local'){
43761 this.selectedIndex = -1;
43763 this.store.clearFilter();
43765 this.store.filter(this.displayField, q);
43769 this.store.baseParams[this.queryParam] = q;
43771 params: this.getParams(q)
43776 this.selectedIndex = -1;
43783 getParams : function(q){
43785 //p[this.queryParam] = q;
43788 p.limit = this.pageSize;
43794 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43796 collapse : function(){
43797 if(!this.isExpanded()){
43801 Roo.get(document).un('mousedown', this.collapseIf, this);
43802 Roo.get(document).un('mousewheel', this.collapseIf, this);
43803 if (!this.editable) {
43804 Roo.get(document).un('keydown', this.listKeyPress, this);
43806 this.fireEvent('collapse', this);
43810 collapseIf : function(e){
43811 if(!e.within(this.wrap) && !e.within(this.list)){
43817 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43819 expand : function(){
43820 if(this.isExpanded() || !this.hasFocus){
43823 this.list.alignTo(this.el, this.listAlign);
43825 Roo.get(document).on('mousedown', this.collapseIf, this);
43826 Roo.get(document).on('mousewheel', this.collapseIf, this);
43827 if (!this.editable) {
43828 Roo.get(document).on('keydown', this.listKeyPress, this);
43831 this.fireEvent('expand', this);
43835 // Implements the default empty TriggerField.onTriggerClick function
43836 onTriggerClick : function(){
43840 if(this.isExpanded()){
43842 if (!this.blockFocus) {
43847 this.hasFocus = true;
43848 if(this.triggerAction == 'all') {
43849 this.doQuery(this.allQuery, true);
43851 this.doQuery(this.getRawValue());
43853 if (!this.blockFocus) {
43858 listKeyPress : function(e)
43860 //Roo.log('listkeypress');
43861 // scroll to first matching element based on key pres..
43862 if (e.isSpecialKey()) {
43865 var k = String.fromCharCode(e.getKey()).toUpperCase();
43868 var csel = this.view.getSelectedNodes();
43869 var cselitem = false;
43871 var ix = this.view.indexOf(csel[0]);
43872 cselitem = this.store.getAt(ix);
43873 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43879 this.store.each(function(v) {
43881 // start at existing selection.
43882 if (cselitem.id == v.id) {
43888 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43889 match = this.store.indexOf(v);
43894 if (match === false) {
43895 return true; // no more action?
43898 this.view.select(match);
43899 var sn = Roo.get(this.view.getSelectedNodes()[0]);
43900 sn.scrollIntoView(sn.dom.parentNode, false);
43904 * @cfg {Boolean} grow
43908 * @cfg {Number} growMin
43912 * @cfg {Number} growMax
43920 * Copyright(c) 2010-2012, Roo J Solutions Limited
43927 * @class Roo.form.ComboBoxArray
43928 * @extends Roo.form.TextField
43929 * A facebook style adder... for lists of email / people / countries etc...
43930 * pick multiple items from a combo box, and shows each one.
43932 * Fred [x] Brian [x] [Pick another |v]
43935 * For this to work: it needs various extra information
43936 * - normal combo problay has
43938 * + displayField, valueField
43940 * For our purpose...
43943 * If we change from 'extends' to wrapping...
43950 * Create a new ComboBoxArray.
43951 * @param {Object} config Configuration options
43955 Roo.form.ComboBoxArray = function(config)
43959 * @event beforeremove
43960 * Fires before remove the value from the list
43961 * @param {Roo.form.ComboBoxArray} _self This combo box array
43962 * @param {Roo.form.ComboBoxArray.Item} item removed item
43964 'beforeremove' : true,
43967 * Fires when remove the value from the list
43968 * @param {Roo.form.ComboBoxArray} _self This combo box array
43969 * @param {Roo.form.ComboBoxArray.Item} item removed item
43976 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43978 this.items = new Roo.util.MixedCollection(false);
43980 // construct the child combo...
43990 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43993 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43998 // behavies liek a hiddne field
43999 inputType: 'hidden',
44001 * @cfg {Number} width The width of the box that displays the selected element
44008 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
44012 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
44014 hiddenName : false,
44016 * @cfg {String} seperator The value seperator normally ','
44020 // private the array of items that are displayed..
44022 // private - the hidden field el.
44024 // private - the filed el..
44027 //validateValue : function() { return true; }, // all values are ok!
44028 //onAddClick: function() { },
44030 onRender : function(ct, position)
44033 // create the standard hidden element
44034 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44037 // give fake names to child combo;
44038 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44039 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44041 this.combo = Roo.factory(this.combo, Roo.form);
44042 this.combo.onRender(ct, position);
44043 if (typeof(this.combo.width) != 'undefined') {
44044 this.combo.onResize(this.combo.width,0);
44047 this.combo.initEvents();
44049 // assigned so form know we need to do this..
44050 this.store = this.combo.store;
44051 this.valueField = this.combo.valueField;
44052 this.displayField = this.combo.displayField ;
44055 this.combo.wrap.addClass('x-cbarray-grp');
44057 var cbwrap = this.combo.wrap.createChild(
44058 {tag: 'div', cls: 'x-cbarray-cb'},
44063 this.hiddenEl = this.combo.wrap.createChild({
44064 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
44066 this.el = this.combo.wrap.createChild({
44067 tag: 'input', type:'hidden' , name: this.name, value : ''
44069 // this.el.dom.removeAttribute("name");
44072 this.outerWrap = this.combo.wrap;
44073 this.wrap = cbwrap;
44075 this.outerWrap.setWidth(this.width);
44076 this.outerWrap.dom.removeChild(this.el.dom);
44078 this.wrap.dom.appendChild(this.el.dom);
44079 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44080 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44082 this.combo.trigger.setStyle('position','relative');
44083 this.combo.trigger.setStyle('left', '0px');
44084 this.combo.trigger.setStyle('top', '2px');
44086 this.combo.el.setStyle('vertical-align', 'text-bottom');
44088 //this.trigger.setStyle('vertical-align', 'top');
44090 // this should use the code from combo really... on('add' ....)
44094 this.adder = this.outerWrap.createChild(
44095 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
44097 this.adder.on('click', function(e) {
44098 _t.fireEvent('adderclick', this, e);
44102 //this.adder.on('click', this.onAddClick, _t);
44105 this.combo.on('select', function(cb, rec, ix) {
44106 this.addItem(rec.data);
44109 cb.el.dom.value = '';
44110 //cb.lastData = rec.data;
44119 getName: function()
44121 // returns hidden if it's set..
44122 if (!this.rendered) {return ''};
44123 return this.hiddenName ? this.hiddenName : this.name;
44128 onResize: function(w, h){
44131 // not sure if this is needed..
44132 //this.combo.onResize(w,h);
44134 if(typeof w != 'number'){
44135 // we do not handle it!?!?
44138 var tw = this.combo.trigger.getWidth();
44139 tw += this.addicon ? this.addicon.getWidth() : 0;
44140 tw += this.editicon ? this.editicon.getWidth() : 0;
44142 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44144 this.combo.trigger.setStyle('left', '0px');
44146 if(this.list && this.listWidth === undefined){
44147 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44148 this.list.setWidth(lw);
44149 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44156 addItem: function(rec)
44158 var valueField = this.combo.valueField;
44159 var displayField = this.combo.displayField;
44161 if (this.items.indexOfKey(rec[valueField]) > -1) {
44162 //console.log("GOT " + rec.data.id);
44166 var x = new Roo.form.ComboBoxArray.Item({
44167 //id : rec[this.idField],
44169 displayField : displayField ,
44170 tipField : displayField ,
44174 this.items.add(rec[valueField],x);
44175 // add it before the element..
44176 this.updateHiddenEl();
44177 x.render(this.outerWrap, this.wrap.dom);
44178 // add the image handler..
44181 updateHiddenEl : function()
44184 if (!this.hiddenEl) {
44188 var idField = this.combo.valueField;
44190 this.items.each(function(f) {
44191 ar.push(f.data[idField]);
44193 this.hiddenEl.dom.value = ar.join(this.seperator);
44199 this.items.clear();
44201 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44205 this.el.dom.value = '';
44206 if (this.hiddenEl) {
44207 this.hiddenEl.dom.value = '';
44211 getValue: function()
44213 return this.hiddenEl ? this.hiddenEl.dom.value : '';
44215 setValue: function(v) // not a valid action - must use addItems..
44220 if (this.store.isLocal && (typeof(v) == 'string')) {
44221 // then we can use the store to find the values..
44222 // comma seperated at present.. this needs to allow JSON based encoding..
44223 this.hiddenEl.value = v;
44225 Roo.each(v.split(this.seperator), function(k) {
44226 Roo.log("CHECK " + this.valueField + ',' + k);
44227 var li = this.store.query(this.valueField, k);
44232 add[this.valueField] = k;
44233 add[this.displayField] = li.item(0).data[this.displayField];
44239 if (typeof(v) == 'object' ) {
44240 // then let's assume it's an array of objects..
44241 Roo.each(v, function(l) {
44243 if (typeof(l) == 'string') {
44245 add[this.valueField] = l;
44246 add[this.displayField] = l
44255 setFromData: function(v)
44257 // this recieves an object, if setValues is called.
44259 this.el.dom.value = v[this.displayField];
44260 this.hiddenEl.dom.value = v[this.valueField];
44261 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44264 var kv = v[this.valueField];
44265 var dv = v[this.displayField];
44266 kv = typeof(kv) != 'string' ? '' : kv;
44267 dv = typeof(dv) != 'string' ? '' : dv;
44270 var keys = kv.split(this.seperator);
44271 var display = dv.split(this.seperator);
44272 for (var i = 0 ; i < keys.length; i++) {
44274 add[this.valueField] = keys[i];
44275 add[this.displayField] = display[i];
44283 * Validates the combox array value
44284 * @return {Boolean} True if the value is valid, else false
44286 validate : function(){
44287 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44288 this.clearInvalid();
44294 validateValue : function(value){
44295 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44303 isDirty : function() {
44304 if(this.disabled) {
44309 var d = Roo.decode(String(this.originalValue));
44311 return String(this.getValue()) !== String(this.originalValue);
44314 var originalValue = [];
44316 for (var i = 0; i < d.length; i++){
44317 originalValue.push(d[i][this.valueField]);
44320 return String(this.getValue()) !== String(originalValue.join(this.seperator));
44329 * @class Roo.form.ComboBoxArray.Item
44330 * @extends Roo.BoxComponent
44331 * A selected item in the list
44332 * Fred [x] Brian [x] [Pick another |v]
44335 * Create a new item.
44336 * @param {Object} config Configuration options
44339 Roo.form.ComboBoxArray.Item = function(config) {
44340 config.id = Roo.id();
44341 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44344 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44347 displayField : false,
44351 defaultAutoCreate : {
44353 cls: 'x-cbarray-item',
44360 src : Roo.BLANK_IMAGE_URL ,
44368 onRender : function(ct, position)
44370 Roo.form.Field.superclass.onRender.call(this, ct, position);
44373 var cfg = this.getAutoCreate();
44374 this.el = ct.createChild(cfg, position);
44377 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44379 this.el.child('div').dom.innerHTML = this.cb.renderer ?
44380 this.cb.renderer(this.data) :
44381 String.format('{0}',this.data[this.displayField]);
44384 this.el.child('div').dom.setAttribute('qtip',
44385 String.format('{0}',this.data[this.tipField])
44388 this.el.child('img').on('click', this.remove, this);
44392 remove : function()
44394 if(this.cb.disabled){
44398 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44399 this.cb.items.remove(this);
44400 this.el.child('img').un('click', this.remove, this);
44402 this.cb.updateHiddenEl();
44404 this.cb.fireEvent('remove', this.cb, this);
44409 * RooJS Library 1.1.1
44410 * Copyright(c) 2008-2011 Alan Knowles
44417 * @class Roo.form.ComboNested
44418 * @extends Roo.form.ComboBox
44419 * A combobox for that allows selection of nested items in a list,
44434 * Create a new ComboNested
44435 * @param {Object} config Configuration options
44437 Roo.form.ComboNested = function(config){
44438 Roo.form.ComboCheck.superclass.constructor.call(this, config);
44439 // should verify some data...
44441 // hiddenName = required..
44442 // displayField = required
44443 // valudField == required
44444 var req= [ 'hiddenName', 'displayField', 'valueField' ];
44446 Roo.each(req, function(e) {
44447 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44448 throw "Roo.form.ComboNested : missing value for: " + e;
44455 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44458 * @config {Number} max Number of columns to show
44463 list : null, // the outermost div..
44464 innerLists : null, // the
44468 loadingChildren : false,
44470 onRender : function(ct, position)
44472 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44474 if(this.hiddenName){
44475 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
44477 this.hiddenField.value =
44478 this.hiddenValue !== undefined ? this.hiddenValue :
44479 this.value !== undefined ? this.value : '';
44481 // prevent input submission
44482 this.el.dom.removeAttribute('name');
44488 this.el.dom.setAttribute('autocomplete', 'off');
44491 var cls = 'x-combo-list';
44493 this.list = new Roo.Layer({
44494 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44497 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44498 this.list.setWidth(lw);
44499 this.list.swallowEvent('mousewheel');
44500 this.assetHeight = 0;
44503 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44504 this.assetHeight += this.header.getHeight();
44506 this.innerLists = [];
44509 for (var i =0 ; i < this.maxColumns; i++) {
44510 this.onRenderList( cls, i);
44513 // always needs footer, as we are going to have an 'OK' button.
44514 this.footer = this.list.createChild({cls:cls+'-ft'});
44515 this.pageTb = new Roo.Toolbar(this.footer);
44520 handler: function()
44526 if ( this.allowBlank && !this.disableClear) {
44528 this.pageTb.add(new Roo.Toolbar.Fill(), {
44529 cls: 'x-btn-icon x-btn-clear',
44531 handler: function()
44534 _this.clearValue();
44535 _this.onSelect(false, -1);
44540 this.assetHeight += this.footer.getHeight();
44544 onRenderList : function ( cls, i)
44547 var lw = Math.floor(
44548 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44551 this.list.setWidth(lw); // default to '1'
44553 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44554 //il.on('mouseover', this.onViewOver, this, { list: i });
44555 //il.on('mousemove', this.onViewMove, this, { list: i });
44557 il.setStyle({ 'overflow-x' : 'hidden'});
44560 this.tpl = new Roo.Template({
44561 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44562 isEmpty: function (value, allValues) {
44564 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44565 return dl ? 'has-children' : 'no-children'
44570 var store = this.store;
44572 store = new Roo.data.SimpleStore({
44573 //fields : this.store.reader.meta.fields,
44574 reader : this.store.reader,
44578 this.stores[i] = store;
44580 var view = this.views[i] = new Roo.View(
44586 selectedClass: this.selectedClass
44589 view.getEl().setWidth(lw);
44590 view.getEl().setStyle({
44591 position: i < 1 ? 'relative' : 'absolute',
44593 left: (i * lw ) + 'px',
44594 display : i > 0 ? 'none' : 'block'
44596 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44597 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44598 //view.on('click', this.onViewClick, this, { list : i });
44600 store.on('beforeload', this.onBeforeLoad, this);
44601 store.on('load', this.onLoad, this, { list : i});
44602 store.on('loadexception', this.onLoadException, this);
44604 // hide the other vies..
44610 restrictHeight : function()
44613 Roo.each(this.innerLists, function(il,i) {
44614 var el = this.views[i].getEl();
44615 el.dom.style.height = '';
44616 var inner = el.dom;
44617 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44618 // only adjust heights on other ones..
44619 mh = Math.max(h, mh);
44622 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44623 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44630 this.list.beginUpdate();
44631 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44632 this.list.alignTo(this.el, this.listAlign);
44633 this.list.endUpdate();
44638 // -- store handlers..
44640 onBeforeLoad : function()
44642 if(!this.hasFocus){
44645 this.innerLists[0].update(this.loadingText ?
44646 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44647 this.restrictHeight();
44648 this.selectedIndex = -1;
44651 onLoad : function(a,b,c,d)
44653 if (!this.loadingChildren) {
44654 // then we are loading the top level. - hide the children
44655 for (var i = 1;i < this.views.length; i++) {
44656 this.views[i].getEl().setStyle({ display : 'none' });
44658 var lw = Math.floor(
44659 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44662 this.list.setWidth(lw); // default to '1'
44666 if(!this.hasFocus){
44670 if(this.store.getCount() > 0) {
44672 this.restrictHeight();
44674 this.onEmptyResults();
44677 if (!this.loadingChildren) {
44678 this.selectActive();
44681 this.stores[1].loadData([]);
44682 this.stores[2].loadData([]);
44691 onLoadException : function()
44694 Roo.log(this.store.reader.jsonData);
44695 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44696 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44701 // no cleaning of leading spaces on blur here.
44702 cleanLeadingSpace : function(e) { },
44705 onSelectChange : function (view, sels, opts )
44707 var ix = view.getSelectedIndexes();
44709 if (opts.list > this.maxColumns - 2) {
44710 if (view.store.getCount()< 1) {
44711 this.views[opts.list ].getEl().setStyle({ display : 'none' });
44715 // used to clear ?? but if we are loading unselected
44716 this.setFromData(view.store.getAt(ix[0]).data);
44725 // this get's fired when trigger opens..
44726 // this.setFromData({});
44727 var str = this.stores[opts.list+1];
44728 str.data.clear(); // removeall wihtout the fire events..
44732 var rec = view.store.getAt(ix[0]);
44734 this.setFromData(rec.data);
44735 this.fireEvent('select', this, rec, ix[0]);
44737 var lw = Math.floor(
44739 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44740 ) / this.maxColumns
44742 this.loadingChildren = true;
44743 this.stores[opts.list+1].loadDataFromChildren( rec );
44744 this.loadingChildren = false;
44745 var dl = this.stores[opts.list+1]. getTotalCount();
44747 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44749 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44750 for (var i = opts.list+2; i < this.views.length;i++) {
44751 this.views[i].getEl().setStyle({ display : 'none' });
44754 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44755 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44757 if (this.isLoading) {
44758 // this.selectActive(opts.list);
44766 onDoubleClick : function()
44768 this.collapse(); //??
44776 recordToStack : function(store, prop, value, stack)
44778 var cstore = new Roo.data.SimpleStore({
44779 //fields : this.store.reader.meta.fields, // we need array reader.. for
44780 reader : this.store.reader,
44784 var record = false;
44786 if(store.getCount() < 1){
44789 store.each(function(r){
44790 if(r.data[prop] == value){
44795 if (r.data.cn && r.data.cn.length) {
44796 cstore.loadDataFromChildren( r);
44797 var cret = _this.recordToStack(cstore, prop, value, stack);
44798 if (cret !== false) {
44807 if (record == false) {
44810 stack.unshift(srec);
44815 * find the stack of stores that match our value.
44820 selectActive : function ()
44822 // if store is not loaded, then we will need to wait for that to happen first.
44824 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44825 for (var i = 0; i < stack.length; i++ ) {
44826 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44838 * Ext JS Library 1.1.1
44839 * Copyright(c) 2006-2007, Ext JS, LLC.
44841 * Originally Released Under LGPL - original licence link has changed is not relivant.
44844 * <script type="text/javascript">
44847 * @class Roo.form.Checkbox
44848 * @extends Roo.form.Field
44849 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
44851 * Creates a new Checkbox
44852 * @param {Object} config Configuration options
44854 Roo.form.Checkbox = function(config){
44855 Roo.form.Checkbox.superclass.constructor.call(this, config);
44859 * Fires when the checkbox is checked or unchecked.
44860 * @param {Roo.form.Checkbox} this This checkbox
44861 * @param {Boolean} checked The new checked value
44867 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
44869 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44871 focusClass : undefined,
44873 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44875 fieldClass: "x-form-field",
44877 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44881 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44882 * {tag: "input", type: "checkbox", autocomplete: "off"})
44884 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44886 * @cfg {String} boxLabel The text that appears beside the checkbox
44890 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44894 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44896 valueOff: '0', // value when not checked..
44898 actionMode : 'viewEl',
44901 itemCls : 'x-menu-check-item x-form-item',
44902 groupClass : 'x-menu-group-item',
44903 inputType : 'hidden',
44906 inSetChecked: false, // check that we are not calling self...
44908 inputElement: false, // real input element?
44909 basedOn: false, // ????
44911 isFormField: true, // not sure where this is needed!!!!
44913 onResize : function(){
44914 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44915 if(!this.boxLabel){
44916 this.el.alignTo(this.wrap, 'c-c');
44920 initEvents : function(){
44921 Roo.form.Checkbox.superclass.initEvents.call(this);
44922 this.el.on("click", this.onClick, this);
44923 this.el.on("change", this.onClick, this);
44927 getResizeEl : function(){
44931 getPositionEl : function(){
44936 onRender : function(ct, position){
44937 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44939 if(this.inputValue !== undefined){
44940 this.el.dom.value = this.inputValue;
44943 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44944 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44945 var viewEl = this.wrap.createChild({
44946 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44947 this.viewEl = viewEl;
44948 this.wrap.on('click', this.onClick, this);
44950 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
44951 this.el.on('propertychange', this.setFromHidden, this); //ie
44956 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44957 // viewEl.on('click', this.onClick, this);
44959 //if(this.checked){
44960 this.setChecked(this.checked);
44962 //this.checked = this.el.dom;
44968 initValue : Roo.emptyFn,
44971 * Returns the checked state of the checkbox.
44972 * @return {Boolean} True if checked, else false
44974 getValue : function(){
44976 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44978 return this.valueOff;
44983 onClick : function(){
44984 if (this.disabled) {
44987 this.setChecked(!this.checked);
44989 //if(this.el.dom.checked != this.checked){
44990 // this.setValue(this.el.dom.checked);
44995 * Sets the checked state of the checkbox.
44996 * On is always based on a string comparison between inputValue and the param.
44997 * @param {Boolean/String} value - the value to set
44998 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45000 setValue : function(v,suppressEvent){
45003 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
45004 //if(this.el && this.el.dom){
45005 // this.el.dom.checked = this.checked;
45006 // this.el.dom.defaultChecked = this.checked;
45008 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45009 //this.fireEvent("check", this, this.checked);
45012 setChecked : function(state,suppressEvent)
45014 if (this.inSetChecked) {
45015 this.checked = state;
45021 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45023 this.checked = state;
45024 if(suppressEvent !== true){
45025 this.fireEvent('check', this, state);
45027 this.inSetChecked = true;
45028 this.el.dom.value = state ? this.inputValue : this.valueOff;
45029 this.inSetChecked = false;
45032 // handle setting of hidden value by some other method!!?!?
45033 setFromHidden: function()
45038 //console.log("SET FROM HIDDEN");
45039 //alert('setFrom hidden');
45040 this.setValue(this.el.dom.value);
45043 onDestroy : function()
45046 Roo.get(this.viewEl).remove();
45049 Roo.form.Checkbox.superclass.onDestroy.call(this);
45052 setBoxLabel : function(str)
45054 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45059 * Ext JS Library 1.1.1
45060 * Copyright(c) 2006-2007, Ext JS, LLC.
45062 * Originally Released Under LGPL - original licence link has changed is not relivant.
45065 * <script type="text/javascript">
45069 * @class Roo.form.Radio
45070 * @extends Roo.form.Checkbox
45071 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
45072 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45074 * Creates a new Radio
45075 * @param {Object} config Configuration options
45077 Roo.form.Radio = function(){
45078 Roo.form.Radio.superclass.constructor.apply(this, arguments);
45080 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45081 inputType: 'radio',
45084 * If this radio is part of a group, it will return the selected value
45087 getGroupValue : function(){
45088 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45092 onRender : function(ct, position){
45093 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45095 if(this.inputValue !== undefined){
45096 this.el.dom.value = this.inputValue;
45099 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45100 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45101 //var viewEl = this.wrap.createChild({
45102 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45103 //this.viewEl = viewEl;
45104 //this.wrap.on('click', this.onClick, this);
45106 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
45107 //this.el.on('propertychange', this.setFromHidden, this); //ie
45112 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45113 // viewEl.on('click', this.onClick, this);
45116 this.el.dom.checked = 'checked' ;
45122 });Roo.rtf = {}; // namespace
45123 Roo.rtf.Hex = function(hex)
45127 Roo.rtf.Paragraph = function(opts)
45129 this.content = []; ///??? is that used?
45130 };Roo.rtf.Span = function(opts)
45132 this.value = opts.value;
45135 Roo.rtf.Group = function(parent)
45137 // we dont want to acutally store parent - it will make debug a nightmare..
45145 Roo.rtf.Group.prototype = {
45149 addContent : function(node) {
45150 // could set styles...
45151 this.content.push(node);
45153 addChild : function(cn)
45157 // only for images really...
45158 toDataURL : function()
45160 var mimetype = false;
45162 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
45163 mimetype = "image/png";
45165 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45166 mimetype = "image/jpeg";
45169 return 'about:blank'; // ?? error?
45173 var hexstring = this.content[this.content.length-1].value;
45175 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45176 return String.fromCharCode(parseInt(a, 16));
45181 // this looks like it's normally the {rtf{ .... }}
45182 Roo.rtf.Document = function()
45184 // we dont want to acutally store parent - it will make debug a nightmare..
45190 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
45191 addChild : function(cn)
45195 case 'rtlch': // most content seems to be inside this??
45198 this.rtlch.push(cn);
45201 this[cn.type] = cn;
45206 getElementsByType : function(type)
45209 this._getElementsByType(type, ret, this.cn, 'rtf');
45212 _getElementsByType : function (type, ret, search_array, path)
45214 search_array.forEach(function(n,i) {
45215 if (n.type == type) {
45216 n.path = path + '/' + n.type + ':' + i;
45219 if (n.cn.length > 0) {
45220 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45227 Roo.rtf.Ctrl = function(opts)
45229 this.value = opts.value;
45230 this.param = opts.param;
45235 * based on this https://github.com/iarna/rtf-parser
45236 * it's really only designed to extract pict from pasted RTF
45240 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45249 Roo.rtf.Parser = function(text) {
45250 //super({objectMode: true})
45252 this.parserState = this.parseText;
45254 // these are for interpeter...
45256 ///this.parserState = this.parseTop
45257 this.groupStack = [];
45258 this.hexStore = [];
45261 this.groups = []; // where we put the return.
45263 for (var ii = 0; ii < text.length; ++ii) {
45266 if (text[ii] === '\n') {
45272 this.parserState(text[ii]);
45278 Roo.rtf.Parser.prototype = {
45279 text : '', // string being parsed..
45281 controlWordParam : '',
45285 groupStack : false,
45290 row : 1, // reportin?
45294 push : function (el)
45296 var m = 'cmd'+ el.type;
45297 if (typeof(this[m]) == 'undefined') {
45298 Roo.log('invalid cmd:' + el.type);
45304 flushHexStore : function()
45306 if (this.hexStore.length < 1) {
45309 var hexstr = this.hexStore.map(
45314 this.group.addContent( new Roo.rtf.Hex( hexstr ));
45317 this.hexStore.splice(0)
45321 cmdgroupstart : function()
45323 this.flushHexStore();
45325 this.groupStack.push(this.group);
45328 if (this.doc === false) {
45329 this.group = this.doc = new Roo.rtf.Document();
45333 this.group = new Roo.rtf.Group(this.group);
45335 cmdignorable : function()
45337 this.flushHexStore();
45338 this.group.ignorable = true;
45340 cmdendparagraph : function()
45342 this.flushHexStore();
45343 this.group.addContent(new Roo.rtf.Paragraph());
45345 cmdgroupend : function ()
45347 this.flushHexStore();
45348 var endingGroup = this.group;
45351 this.group = this.groupStack.pop();
45353 this.group.addChild(endingGroup);
45358 var doc = this.group || this.doc;
45359 //if (endingGroup instanceof FontTable) {
45360 // doc.fonts = endingGroup.table
45361 //} else if (endingGroup instanceof ColorTable) {
45362 // doc.colors = endingGroup.table
45363 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45364 if (endingGroup.ignorable === false) {
45366 this.groups.push(endingGroup);
45367 // Roo.log( endingGroup );
45369 //Roo.each(endingGroup.content, function(item)) {
45370 // doc.addContent(item);
45372 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45375 cmdtext : function (cmd)
45377 this.flushHexStore();
45378 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45379 //this.group = this.doc
45380 return; // we really don't care about stray text...
45382 this.group.addContent(new Roo.rtf.Span(cmd));
45384 cmdcontrolword : function (cmd)
45386 this.flushHexStore();
45387 if (!this.group.type) {
45388 this.group.type = cmd.value;
45391 this.group.addContent(new Roo.rtf.Ctrl(cmd));
45392 // we actually don't care about ctrl words...
45395 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45396 if (this[method]) {
45397 this[method](cmd.param)
45399 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45403 cmdhexchar : function(cmd) {
45404 this.hexStore.push(cmd);
45406 cmderror : function(cmd) {
45412 if (this.text !== '\u0000') this.emitText()
45418 parseText : function(c)
45421 this.parserState = this.parseEscapes;
45422 } else if (c === '{') {
45423 this.emitStartGroup();
45424 } else if (c === '}') {
45425 this.emitEndGroup();
45426 } else if (c === '\x0A' || c === '\x0D') {
45427 // cr/lf are noise chars
45433 parseEscapes: function (c)
45435 if (c === '\\' || c === '{' || c === '}') {
45437 this.parserState = this.parseText;
45439 this.parserState = this.parseControlSymbol;
45440 this.parseControlSymbol(c);
45443 parseControlSymbol: function(c)
45446 this.text += '\u00a0'; // nbsp
45447 this.parserState = this.parseText
45448 } else if (c === '-') {
45449 this.text += '\u00ad'; // soft hyphen
45450 } else if (c === '_') {
45451 this.text += '\u2011'; // non-breaking hyphen
45452 } else if (c === '*') {
45453 this.emitIgnorable();
45454 this.parserState = this.parseText;
45455 } else if (c === "'") {
45456 this.parserState = this.parseHexChar;
45457 } else if (c === '|') { // formula cacter
45458 this.emitFormula();
45459 this.parserState = this.parseText;
45460 } else if (c === ':') { // subentry in an index entry
45461 this.emitIndexSubEntry();
45462 this.parserState = this.parseText;
45463 } else if (c === '\x0a') {
45464 this.emitEndParagraph();
45465 this.parserState = this.parseText;
45466 } else if (c === '\x0d') {
45467 this.emitEndParagraph();
45468 this.parserState = this.parseText;
45470 this.parserState = this.parseControlWord;
45471 this.parseControlWord(c);
45474 parseHexChar: function (c)
45476 if (/^[A-Fa-f0-9]$/.test(c)) {
45478 if (this.hexChar.length >= 2) {
45479 this.emitHexChar();
45480 this.parserState = this.parseText;
45484 this.emitError("Invalid character \"" + c + "\" in hex literal.");
45485 this.parserState = this.parseText;
45488 parseControlWord : function(c)
45491 this.emitControlWord();
45492 this.parserState = this.parseText;
45493 } else if (/^[-\d]$/.test(c)) {
45494 this.parserState = this.parseControlWordParam;
45495 this.controlWordParam += c;
45496 } else if (/^[A-Za-z]$/.test(c)) {
45497 this.controlWord += c;
45499 this.emitControlWord();
45500 this.parserState = this.parseText;
45504 parseControlWordParam : function (c) {
45505 if (/^\d$/.test(c)) {
45506 this.controlWordParam += c;
45507 } else if (c === ' ') {
45508 this.emitControlWord();
45509 this.parserState = this.parseText;
45511 this.emitControlWord();
45512 this.parserState = this.parseText;
45520 emitText : function () {
45521 if (this.text === '') {
45533 emitControlWord : function ()
45536 if (this.controlWord === '') {
45537 // do we want to track this - it seems just to cause problems.
45538 //this.emitError('empty control word');
45541 type: 'controlword',
45542 value: this.controlWord,
45543 param: this.controlWordParam !== '' && Number(this.controlWordParam),
45549 this.controlWord = '';
45550 this.controlWordParam = '';
45552 emitStartGroup : function ()
45556 type: 'groupstart',
45562 emitEndGroup : function ()
45572 emitIgnorable : function ()
45582 emitHexChar : function ()
45587 value: this.hexChar,
45594 emitError : function (message)
45602 char: this.cpos //,
45603 //stack: new Error().stack
45606 emitEndParagraph : function () {
45609 type: 'endparagraph',
45617 Roo.htmleditor = {};
45620 * @class Roo.htmleditor.Filter
45621 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45622 * @cfg {DomElement} node The node to iterate and filter
45623 * @cfg {boolean|String|Array} tag Tags to replace
45625 * Create a new Filter.
45626 * @param {Object} config Configuration options
45631 Roo.htmleditor.Filter = function(cfg) {
45632 Roo.apply(this.cfg);
45633 // this does not actually call walk as it's really just a abstract class
45637 Roo.htmleditor.Filter.prototype = {
45643 // overrride to do replace comments.
45644 replaceComment : false,
45646 // overrride to do replace or do stuff with tags..
45647 replaceTag : false,
45649 walk : function(dom)
45651 Roo.each( Array.from(dom.childNodes), function( e ) {
45654 case e.nodeType == 8 && this.replaceComment !== false: // comment
45655 this.replaceComment(e);
45658 case e.nodeType != 1: //not a node.
45661 case this.tag === true: // everything
45662 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
45663 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
45664 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45665 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45666 if (this.replaceTag && false === this.replaceTag(e)) {
45669 if (e.hasChildNodes()) {
45674 default: // tags .. that do not match.
45675 if (e.hasChildNodes()) {
45685 removeNodeKeepChildren : function( node)
45688 ar = Array.from(node.childNodes);
45689 for (var i = 0; i < ar.length; i++) {
45691 node.removeChild(ar[i]);
45692 // what if we need to walk these???
45693 node.parentNode.insertBefore(ar[i], node);
45696 node.parentNode.removeChild(node);
45701 * @class Roo.htmleditor.FilterAttributes
45702 * clean attributes and styles including http:// etc.. in attribute
45704 * Run a new Attribute Filter
45705 * @param {Object} config Configuration options
45707 Roo.htmleditor.FilterAttributes = function(cfg)
45709 Roo.apply(this, cfg);
45710 this.attrib_black = this.attrib_black || [];
45711 this.attrib_white = this.attrib_white || [];
45713 this.attrib_clean = this.attrib_clean || [];
45714 this.style_white = this.style_white || [];
45715 this.style_black = this.style_black || [];
45716 this.walk(cfg.node);
45719 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45721 tag: true, // all tags
45723 attrib_black : false, // array
45724 attrib_clean : false,
45725 attrib_white : false,
45727 style_white : false,
45728 style_black : false,
45731 replaceTag : function(node)
45733 if (!node.attributes || !node.attributes.length) {
45737 for (var i = node.attributes.length-1; i > -1 ; i--) {
45738 var a = node.attributes[i];
45740 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45741 node.removeAttribute(a.name);
45747 if (a.name.toLowerCase().substr(0,2)=='on') {
45748 node.removeAttribute(a.name);
45753 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45754 node.removeAttribute(a.name);
45757 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45758 this.cleanAttr(node,a.name,a.value); // fixme..
45761 if (a.name == 'style') {
45762 this.cleanStyle(node,a.name,a.value);
45765 /// clean up MS crap..
45766 // tecnically this should be a list of valid class'es..
45769 if (a.name == 'class') {
45770 if (a.value.match(/^Mso/)) {
45771 node.removeAttribute('class');
45774 if (a.value.match(/^body$/)) {
45775 node.removeAttribute('class');
45785 return true; // clean children
45788 cleanAttr: function(node, n,v)
45791 if (v.match(/^\./) || v.match(/^\//)) {
45794 if (v.match(/^(http|https):\/\//)
45795 || v.match(/^mailto:/)
45796 || v.match(/^ftp:/)
45797 || v.match(/^data:/)
45801 if (v.match(/^#/)) {
45804 if (v.match(/^\{/)) { // allow template editing.
45807 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45808 node.removeAttribute(n);
45811 cleanStyle : function(node, n,v)
45813 if (v.match(/expression/)) { //XSS?? should we even bother..
45814 node.removeAttribute(n);
45818 var parts = v.split(/;/);
45821 Roo.each(parts, function(p) {
45822 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45826 var l = p.split(':').shift().replace(/\s+/g,'');
45827 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45829 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45833 // only allow 'c whitelisted system attributes'
45834 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45842 if (clean.length) {
45843 node.setAttribute(n, clean.join(';'));
45845 node.removeAttribute(n);
45854 * @class Roo.htmleditor.FilterBlack
45855 * remove blacklisted elements.
45857 * Run a new Blacklisted Filter
45858 * @param {Object} config Configuration options
45861 Roo.htmleditor.FilterBlack = function(cfg)
45863 Roo.apply(this, cfg);
45864 this.walk(cfg.node);
45867 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45869 tag : true, // all elements.
45871 replaceTag : function(n)
45873 n.parentNode.removeChild(n);
45877 * @class Roo.htmleditor.FilterComment
45880 * Run a new Comments Filter
45881 * @param {Object} config Configuration options
45883 Roo.htmleditor.FilterComment = function(cfg)
45885 this.walk(cfg.node);
45888 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45891 replaceComment : function(n)
45893 n.parentNode.removeChild(n);
45896 * @class Roo.htmleditor.FilterKeepChildren
45897 * remove tags but keep children
45899 * Run a new Keep Children Filter
45900 * @param {Object} config Configuration options
45903 Roo.htmleditor.FilterKeepChildren = function(cfg)
45905 Roo.apply(this, cfg);
45906 if (this.tag === false) {
45907 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45910 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
45911 this.cleanNamespace = true;
45914 this.walk(cfg.node);
45917 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45919 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
45921 replaceTag : function(node)
45923 // walk children...
45924 //Roo.log(node.tagName);
45925 var ar = Array.from(node.childNodes);
45928 for (var i = 0; i < ar.length; i++) {
45930 if (e.nodeType == 1) {
45932 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
45933 || // array and it matches
45934 (typeof(this.tag) == 'string' && this.tag == e.tagName)
45936 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
45938 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
45940 this.replaceTag(ar[i]); // child is blacklisted as well...
45945 ar = Array.from(node.childNodes);
45946 for (var i = 0; i < ar.length; i++) {
45948 node.removeChild(ar[i]);
45949 // what if we need to walk these???
45950 node.parentNode.insertBefore(ar[i], node);
45951 if (this.tag !== false) {
45956 //Roo.log("REMOVE:" + node.tagName);
45957 node.parentNode.removeChild(node);
45958 return false; // don't walk children
45963 * @class Roo.htmleditor.FilterParagraph
45964 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45965 * like on 'push' to remove the <p> tags and replace them with line breaks.
45967 * Run a new Paragraph Filter
45968 * @param {Object} config Configuration options
45971 Roo.htmleditor.FilterParagraph = function(cfg)
45973 // no need to apply config.
45974 this.walk(cfg.node);
45977 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45984 replaceTag : function(node)
45987 if (node.childNodes.length == 1 &&
45988 node.childNodes[0].nodeType == 3 &&
45989 node.childNodes[0].textContent.trim().length < 1
45991 // remove and replace with '<BR>';
45992 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45993 return false; // no need to walk..
45995 var ar = Array.from(node.childNodes);
45996 for (var i = 0; i < ar.length; i++) {
45997 node.removeChild(ar[i]);
45998 // what if we need to walk these???
45999 node.parentNode.insertBefore(ar[i], node);
46001 // now what about this?
46005 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46006 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46007 node.parentNode.removeChild(node);
46014 * @class Roo.htmleditor.FilterSpan
46015 * filter span's with no attributes out..
46017 * Run a new Span Filter
46018 * @param {Object} config Configuration options
46021 Roo.htmleditor.FilterSpan = function(cfg)
46023 // no need to apply config.
46024 this.walk(cfg.node);
46027 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
46033 replaceTag : function(node)
46035 if (node.attributes && node.attributes.length > 0) {
46036 return true; // walk if there are any.
46038 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
46044 * @class Roo.htmleditor.FilterTableWidth
46045 try and remove table width data - as that frequently messes up other stuff.
46047 * was cleanTableWidths.
46049 * Quite often pasting from word etc.. results in tables with column and widths.
46050 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
46053 * Run a new Table Filter
46054 * @param {Object} config Configuration options
46057 Roo.htmleditor.FilterTableWidth = function(cfg)
46059 // no need to apply config.
46060 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46061 this.walk(cfg.node);
46064 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46069 replaceTag: function(node) {
46073 if (node.hasAttribute('width')) {
46074 node.removeAttribute('width');
46078 if (node.hasAttribute("style")) {
46081 var styles = node.getAttribute("style").split(";");
46083 Roo.each(styles, function(s) {
46084 if (!s.match(/:/)) {
46087 var kv = s.split(":");
46088 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46091 // what ever is left... we allow.
46094 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46095 if (!nstyle.length) {
46096 node.removeAttribute('style');
46100 return true; // continue doing children..
46103 * @class Roo.htmleditor.FilterWord
46104 * try and clean up all the mess that Word generates.
46106 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
46109 * Run a new Span Filter
46110 * @param {Object} config Configuration options
46113 Roo.htmleditor.FilterWord = function(cfg)
46115 // no need to apply config.
46116 this.replaceDocBullets(cfg.node);
46118 this.replaceAname(cfg.node);
46119 // this is disabled as the removal is done by other filters;
46120 // this.walk(cfg.node);
46125 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46131 * Clean up MS wordisms...
46133 replaceTag : function(node)
46136 // no idea what this does - span with text, replaceds with just text.
46138 node.nodeName == 'SPAN' &&
46139 !node.hasAttributes() &&
46140 node.childNodes.length == 1 &&
46141 node.firstChild.nodeName == "#text"
46143 var textNode = node.firstChild;
46144 node.removeChild(textNode);
46145 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46146 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46148 node.parentNode.insertBefore(textNode, node);
46149 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
46150 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46153 node.parentNode.removeChild(node);
46154 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46159 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46160 node.parentNode.removeChild(node);
46161 return false; // dont do chidlren
46163 //Roo.log(node.tagName);
46164 // remove - but keep children..
46165 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46166 //Roo.log('-- removed');
46167 while (node.childNodes.length) {
46168 var cn = node.childNodes[0];
46169 node.removeChild(cn);
46170 node.parentNode.insertBefore(cn, node);
46171 // move node to parent - and clean it..
46172 if (cn.nodeType == 1) {
46173 this.replaceTag(cn);
46177 node.parentNode.removeChild(node);
46178 /// no need to iterate chidlren = it's got none..
46179 //this.iterateChildren(node, this.cleanWord);
46180 return false; // no need to iterate children.
46183 if (node.className.length) {
46185 var cn = node.className.split(/\W+/);
46187 Roo.each(cn, function(cls) {
46188 if (cls.match(/Mso[a-zA-Z]+/)) {
46193 node.className = cna.length ? cna.join(' ') : '';
46195 node.removeAttribute("class");
46199 if (node.hasAttribute("lang")) {
46200 node.removeAttribute("lang");
46203 if (node.hasAttribute("style")) {
46205 var styles = node.getAttribute("style").split(";");
46207 Roo.each(styles, function(s) {
46208 if (!s.match(/:/)) {
46211 var kv = s.split(":");
46212 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46215 // what ever is left... we allow.
46218 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46219 if (!nstyle.length) {
46220 node.removeAttribute('style');
46223 return true; // do children
46229 styleToObject: function(node)
46231 var styles = (node.getAttribute("style") || '').split(";");
46233 Roo.each(styles, function(s) {
46234 if (!s.match(/:/)) {
46237 var kv = s.split(":");
46239 // what ever is left... we allow.
46240 ret[kv[0].trim()] = kv[1];
46246 replaceAname : function (doc)
46248 // replace all the a/name without..
46249 var aa = Array.from(doc.getElementsByTagName('a'));
46250 for (var i = 0; i < aa.length; i++) {
46252 if (a.hasAttribute("name")) {
46253 a.removeAttribute("name");
46255 if (a.hasAttribute("href")) {
46258 // reparent children.
46259 this.removeNodeKeepChildren(a);
46269 replaceDocBullets : function(doc)
46271 // this is a bit odd - but it appears some indents use ql-indent-1
46272 //Roo.log(doc.innerHTML);
46274 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
46275 for( var i = 0; i < listpara.length; i ++) {
46276 listpara[i].className = "MsoListParagraph";
46279 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
46280 for( var i = 0; i < listpara.length; i ++) {
46281 listpara[i].className = "MsoListParagraph";
46283 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
46284 for( var i = 0; i < listpara.length; i ++) {
46285 listpara[i].className = "MsoListParagraph";
46287 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
46288 for( var i = 0; i < listpara.length; i ++) {
46289 listpara[i].className = "MsoListParagraph";
46292 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
46293 var htwo = Array.from(doc.getElementsByTagName('h2'));
46294 for( var i = 0; i < htwo.length; i ++) {
46295 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
46296 htwo[i].className = "MsoListParagraph";
46299 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
46300 for( var i = 0; i < listpara.length; i ++) {
46301 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
46302 listpara[i].className = "MsoListParagraph";
46304 listpara[i].className = "MsoNormalx";
46308 listpara = doc.getElementsByClassName('MsoListParagraph');
46309 // Roo.log(doc.innerHTML);
46313 while(listpara.length) {
46315 this.replaceDocBullet(listpara.item(0));
46322 replaceDocBullet : function(p)
46324 // gather all the siblings.
46326 parent = p.parentNode,
46327 doc = parent.ownerDocument,
46330 var listtype = 'ul';
46332 if (ns.nodeType != 1) {
46333 ns = ns.nextSibling;
46336 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
46339 var spans = ns.getElementsByTagName('span');
46340 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
46342 ns = ns.nextSibling;
46344 if (spans.length && spans[0].hasAttribute('style')) {
46345 var style = this.styleToObject(spans[0]);
46346 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
46353 var spans = ns.getElementsByTagName('span');
46354 if (!spans.length) {
46357 var has_list = false;
46358 for(var i = 0; i < spans.length; i++) {
46359 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
46368 ns = ns.nextSibling;
46372 if (!items.length) {
46377 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
46378 parent.insertBefore(ul, p);
46380 var stack = [ ul ];
46381 var last_li = false;
46383 var margin_to_depth = {};
46386 items.forEach(function(n, ipos) {
46387 //Roo.log("got innertHMLT=" + n.innerHTML);
46389 var spans = n.getElementsByTagName('span');
46390 if (!spans.length) {
46391 //Roo.log("No spans found");
46393 parent.removeChild(n);
46396 return; // skip it...
46402 for(var i = 0; i < spans.length; i++) {
46404 style = this.styleToObject(spans[i]);
46405 if (typeof(style['mso-list']) == 'undefined') {
46408 if (listtype == 'ol') {
46409 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
46411 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
46414 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
46415 style = this.styleToObject(n); // mo-list is from the parent node.
46416 if (typeof(style['mso-list']) == 'undefined') {
46417 //Roo.log("parent is missing level");
46419 parent.removeChild(n);
46424 var margin = style['margin-left'];
46425 if (typeof(margin_to_depth[margin]) == 'undefined') {
46427 margin_to_depth[margin] = max_margins;
46429 nlvl = margin_to_depth[margin] ;
46433 var nul = doc.createElement(listtype); // what about number lists...
46435 last_li = doc.createElement('li');
46436 stack[lvl].appendChild(last_li);
46438 last_li.appendChild(nul);
46444 // not starting at 1..
46445 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
46446 stack[nlvl].setAttribute("start", num);
46449 var nli = stack[nlvl].appendChild(doc.createElement('li'));
46451 nli.innerHTML = n.innerHTML;
46452 //Roo.log("innerHTML = " + n.innerHTML);
46453 parent.removeChild(n);
46469 * @class Roo.htmleditor.FilterStyleToTag
46470 * part of the word stuff... - certain 'styles' should be converted to tags.
46472 * font-weight: bold -> bold
46473 * ?? super / subscrit etc..
46476 * Run a new style to tag filter.
46477 * @param {Object} config Configuration options
46479 Roo.htmleditor.FilterStyleToTag = function(cfg)
46483 B : [ 'fontWeight' , 'bold'],
46484 I : [ 'fontStyle' , 'italic'],
46485 //pre : [ 'font-style' , 'italic'],
46486 // h1.. h6 ?? font-size?
46487 SUP : [ 'verticalAlign' , 'super' ],
46488 SUB : [ 'verticalAlign' , 'sub' ]
46493 Roo.apply(this, cfg);
46496 this.walk(cfg.node);
46503 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46505 tag: true, // all tags
46510 replaceTag : function(node)
46514 if (node.getAttribute("style") === null) {
46518 for (var k in this.tags) {
46519 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46521 node.style.removeProperty(this.tags[k][0]);
46524 if (!inject.length) {
46527 var cn = Array.from(node.childNodes);
46529 Roo.each(inject, function(t) {
46530 var nc = node.ownerDocument.createElement(t);
46531 nn.appendChild(nc);
46534 for(var i = 0;i < cn.length;cn++) {
46535 node.removeChild(cn[i]);
46536 nn.appendChild(cn[i]);
46538 return true /// iterate thru
46542 * @class Roo.htmleditor.FilterLongBr
46543 * BR/BR/BR - keep a maximum of 2...
46545 * Run a new Long BR Filter
46546 * @param {Object} config Configuration options
46549 Roo.htmleditor.FilterLongBr = function(cfg)
46551 // no need to apply config.
46552 this.walk(cfg.node);
46555 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46562 replaceTag : function(node)
46565 var ps = node.nextSibling;
46566 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46567 ps = ps.nextSibling;
46570 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
46571 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46575 if (!ps || ps.nodeType != 1) {
46579 if (!ps || ps.tagName != 'BR') {
46588 if (!node.previousSibling) {
46591 var ps = node.previousSibling;
46593 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46594 ps = ps.previousSibling;
46596 if (!ps || ps.nodeType != 1) {
46599 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46600 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46604 node.parentNode.removeChild(node); // remove me...
46606 return false; // no need to do children
46613 * @class Roo.htmleditor.FilterBlock
46614 * removes id / data-block and contenteditable that are associated with blocks
46615 * usage should be done on a cloned copy of the dom
46617 * Run a new Attribute Filter { node : xxxx }}
46618 * @param {Object} config Configuration options
46620 Roo.htmleditor.FilterBlock = function(cfg)
46622 Roo.apply(this, cfg);
46623 var qa = cfg.node.querySelectorAll;
46624 this.removeAttributes('data-block');
46625 this.removeAttributes('contenteditable');
46626 this.removeAttributes('id');
46630 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46632 node: true, // all tags
46635 removeAttributes : function(attr)
46637 var ar = this.node.querySelectorAll('*[' + attr + ']');
46638 for (var i =0;i<ar.length;i++) {
46639 ar[i].removeAttribute(attr);
46648 * This is based loosely on tinymce
46649 * @class Roo.htmleditor.TidySerializer
46650 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46652 * @method Serializer
46653 * @param {Object} settings Name/value settings object.
46657 Roo.htmleditor.TidySerializer = function(settings)
46659 Roo.apply(this, settings);
46661 this.writer = new Roo.htmleditor.TidyWriter(settings);
46666 Roo.htmleditor.TidySerializer.prototype = {
46669 * @param {boolean} inner do the inner of the node.
46676 * Serializes the specified node into a string.
46679 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46680 * @method serialize
46681 * @param {DomElement} node Node instance to serialize.
46682 * @return {String} String with HTML based on DOM tree.
46684 serialize : function(node) {
46686 // = settings.validate;
46687 var writer = this.writer;
46691 3: function(node) {
46693 writer.text(node.nodeValue, node);
46696 8: function(node) {
46697 writer.comment(node.nodeValue);
46699 // Processing instruction
46700 7: function(node) {
46701 writer.pi(node.name, node.nodeValue);
46704 10: function(node) {
46705 writer.doctype(node.nodeValue);
46708 4: function(node) {
46709 writer.cdata(node.nodeValue);
46711 // Document fragment
46712 11: function(node) {
46713 node = node.firstChild;
46719 node = node.nextSibling
46724 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46725 return writer.getContent();
46728 walk: function(node)
46730 var attrName, attrValue, sortedAttrs, i, l, elementRule,
46731 handler = this.handlers[node.nodeType];
46738 var name = node.nodeName;
46739 var isEmpty = node.childNodes.length < 1;
46741 var writer = this.writer;
46742 var attrs = node.attributes;
46745 writer.start(node.nodeName, attrs, isEmpty, node);
46749 node = node.firstChild;
46756 node = node.nextSibling;
46762 // Serialize element and treat all non elements as fragments
46767 * This is based loosely on tinymce
46768 * @class Roo.htmleditor.TidyWriter
46769 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46772 * - not tested much with 'PRE' formated elements.
46778 Roo.htmleditor.TidyWriter = function(settings)
46781 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46782 Roo.apply(this, settings);
46786 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46789 Roo.htmleditor.TidyWriter.prototype = {
46796 // part of state...
46800 last_inline : false,
46805 * Writes the a start element such as <p id="a">.
46808 * @param {String} name Name of the element.
46809 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46810 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46812 start: function(name, attrs, empty, node)
46814 var i, l, attr, value;
46816 // there are some situations where adding line break && indentation will not work. will not work.
46817 // <span / b / i ... formating?
46819 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46820 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46822 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46824 var add_lb = name == 'BR' ? false : in_inline;
46826 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46830 var indentstr = this.indentstr;
46832 // e_inline = elements that can be inline, but still allow \n before and after?
46833 // only 'BR' ??? any others?
46835 // ADD LINE BEFORE tage
46836 if (!this.in_pre) {
46839 if (name == 'BR') {
46841 } else if (this.lastElementEndsWS()) {
46844 // otherwise - no new line. (and dont indent.)
46855 this.html.push(indentstr + '<', name.toLowerCase());
46858 for (i = 0, l = attrs.length; i < l; i++) {
46860 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46866 this.html[this.html.length] = '/>';
46868 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46870 var e_inline = name == 'BR' ? false : this.in_inline;
46872 if (!e_inline && !this.in_pre) {
46879 this.html[this.html.length] = '>';
46881 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46883 if (!in_inline && !in_pre) {
46884 var cn = node.firstChild;
46886 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46890 cn = cn.nextSibling;
46898 indentstr : in_pre ? '' : (this.indentstr + this.indent),
46900 in_inline : in_inline
46902 // add a line after if we are not in a
46904 if (!in_inline && !in_pre) {
46913 lastElementEndsWS : function()
46915 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46916 if (value === false) {
46919 return value.match(/\s+$/);
46924 * Writes the a end element such as </p>.
46927 * @param {String} name Name of the element.
46929 end: function(name) {
46932 var indentstr = '';
46933 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46935 if (!this.in_pre && !in_inline) {
46937 indentstr = this.indentstr;
46939 this.html.push(indentstr + '</', name.toLowerCase(), '>');
46940 this.last_inline = in_inline;
46942 // pop the indent state..
46945 * Writes a text node.
46947 * In pre - we should not mess with the contents.
46951 * @param {String} text String to write out.
46952 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46954 text: function(in_text, node)
46956 // if not in whitespace critical
46957 if (in_text.length < 1) {
46960 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
46963 this.html[this.html.length] = text;
46967 if (this.in_inline) {
46968 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
46970 text = text.replace(/\s+/,' '); // all white space to single white space
46973 // if next tag is '<BR>', then we can trim right..
46974 if (node.nextSibling &&
46975 node.nextSibling.nodeType == 1 &&
46976 node.nextSibling.nodeName == 'BR' )
46978 text = text.replace(/\s+$/g,'');
46980 // if previous tag was a BR, we can also trim..
46981 if (node.previousSibling &&
46982 node.previousSibling.nodeType == 1 &&
46983 node.previousSibling.nodeName == 'BR' )
46985 text = this.indentstr + text.replace(/^\s+/g,'');
46987 if (text.match(/\n/)) {
46988 text = text.replace(
46989 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46991 // remoeve the last whitespace / line break.
46992 text = text.replace(/\n\s+$/,'');
46994 // repace long lines
46998 this.html[this.html.length] = text;
47001 // see if previous element was a inline element.
47002 var indentstr = this.indentstr;
47004 text = text.replace(/\s+/g," "); // all whitespace into single white space.
47006 // should trim left?
47007 if (node.previousSibling &&
47008 node.previousSibling.nodeType == 1 &&
47009 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
47015 text = text.replace(/^\s+/,''); // trim left
47018 // should trim right?
47019 if (node.nextSibling &&
47020 node.nextSibling.nodeType == 1 &&
47021 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
47026 text = text.replace(/\s+$/,''); // trim right
47033 if (text.length < 1) {
47036 if (!text.match(/\n/)) {
47037 this.html.push(indentstr + text);
47041 text = this.indentstr + text.replace(
47042 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
47044 // remoeve the last whitespace / line break.
47045 text = text.replace(/\s+$/,'');
47047 this.html.push(text);
47049 // split and indent..
47054 * Writes a cdata node such as <![CDATA[data]]>.
47057 * @param {String} text String to write out inside the cdata.
47059 cdata: function(text) {
47060 this.html.push('<![CDATA[', text, ']]>');
47063 * Writes a comment node such as <!-- Comment -->.
47066 * @param {String} text String to write out inside the comment.
47068 comment: function(text) {
47069 this.html.push('<!--', text, '-->');
47072 * Writes a PI node such as <?xml attr="value" ?>.
47075 * @param {String} name Name of the pi.
47076 * @param {String} text String to write out inside the pi.
47078 pi: function(name, text) {
47079 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
47080 this.indent != '' && this.html.push('\n');
47083 * Writes a doctype node such as <!DOCTYPE data>.
47086 * @param {String} text String to write out inside the doctype.
47088 doctype: function(text) {
47089 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
47092 * Resets the internal buffer if one wants to reuse the writer.
47096 reset: function() {
47097 this.html.length = 0;
47106 * Returns the contents that got serialized.
47108 * @method getContent
47109 * @return {String} HTML contents that got written down.
47111 getContent: function() {
47112 return this.html.join('').replace(/\n$/, '');
47115 pushState : function(cfg)
47117 this.state.push(cfg);
47118 Roo.apply(this, cfg);
47121 popState : function()
47123 if (this.state.length < 1) {
47124 return; // nothing to push
47131 if (this.state.length > 0) {
47132 cfg = this.state[this.state.length-1];
47134 Roo.apply(this, cfg);
47137 addLine: function()
47139 if (this.html.length < 1) {
47144 var value = this.html[this.html.length - 1];
47145 if (value.length > 0 && '\n' !== value) {
47146 this.html.push('\n');
47151 //'pre script noscript style textarea video audio iframe object code'
47152 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
47156 Roo.htmleditor.TidyWriter.inline_elements = [
47157 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
47158 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
47160 Roo.htmleditor.TidyWriter.shortend_elements = [
47161 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
47162 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
47165 Roo.htmleditor.TidyWriter.whitespace_elements = [
47166 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
47168 * This is based loosely on tinymce
47169 * @class Roo.htmleditor.TidyEntities
47171 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47173 * Not 100% sure this is actually used or needed.
47176 Roo.htmleditor.TidyEntities = {
47179 * initialize data..
47181 init : function (){
47183 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
47188 buildEntitiesLookup: function(items, radix) {
47189 var i, chr, entity, lookup = {};
47193 items = typeof(items) == 'string' ? items.split(',') : items;
47194 radix = radix || 10;
47195 // Build entities lookup table
47196 for (i = 0; i < items.length; i += 2) {
47197 chr = String.fromCharCode(parseInt(items[i], radix));
47198 // Only add non base entities
47199 if (!this.baseEntities[chr]) {
47200 entity = '&' + items[i + 1] + ';';
47201 lookup[chr] = entity;
47202 lookup[entity] = chr;
47241 // Needs to be escaped since the YUI compressor would otherwise break the code
47248 // Reverse lookup table for raw entities
47249 reverseEntities : {
47257 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47258 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47259 rawCharsRegExp : /[<>&\"\']/g,
47260 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
47261 namedEntities : false,
47262 namedEntitiesData : [
47763 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47765 * @method encodeRaw
47766 * @param {String} text Text to encode.
47767 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47768 * @return {String} Entity encoded text.
47770 encodeRaw: function(text, attr)
47773 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47774 return t.baseEntities[chr] || chr;
47778 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47779 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47780 * and is exposed as the DOMUtils.encode function.
47782 * @method encodeAllRaw
47783 * @param {String} text Text to encode.
47784 * @return {String} Entity encoded text.
47786 encodeAllRaw: function(text) {
47788 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47789 return t.baseEntities[chr] || chr;
47793 * Encodes the specified string using numeric entities. The core entities will be
47794 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47796 * @method encodeNumeric
47797 * @param {String} text Text to encode.
47798 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47799 * @return {String} Entity encoded text.
47801 encodeNumeric: function(text, attr) {
47803 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47804 // Multi byte sequence convert it to a single entity
47805 if (chr.length > 1) {
47806 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47808 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47812 * Encodes the specified string using named entities. The core entities will be encoded
47813 * as named ones but all non lower ascii characters will be encoded into named entities.
47815 * @method encodeNamed
47816 * @param {String} text Text to encode.
47817 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47818 * @param {Object} entities Optional parameter with entities to use.
47819 * @return {String} Entity encoded text.
47821 encodeNamed: function(text, attr, entities) {
47823 entities = entities || this.namedEntities;
47824 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47825 return t.baseEntities[chr] || entities[chr] || chr;
47829 * Returns an encode function based on the name(s) and it's optional entities.
47831 * @method getEncodeFunc
47832 * @param {String} name Comma separated list of encoders for example named,numeric.
47833 * @param {String} entities Optional parameter with entities to use instead of the built in set.
47834 * @return {function} Encode function to be used.
47836 getEncodeFunc: function(name, entities) {
47837 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47839 function encodeNamedAndNumeric(text, attr) {
47840 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47841 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47845 function encodeCustomNamed(text, attr) {
47846 return t.encodeNamed(text, attr, entities);
47848 // Replace + with , to be compatible with previous TinyMCE versions
47849 name = this.makeMap(name.replace(/\+/g, ','));
47850 // Named and numeric encoder
47851 if (name.named && name.numeric) {
47852 return this.encodeNamedAndNumeric;
47858 return encodeCustomNamed;
47860 return this.encodeNamed;
47863 if (name.numeric) {
47864 return this.encodeNumeric;
47867 return this.encodeRaw;
47870 * Decodes the specified string, this will replace entities with raw UTF characters.
47873 * @param {String} text Text to entity decode.
47874 * @return {String} Entity decoded string.
47876 decode: function(text)
47879 return text.replace(this.entityRegExp, function(all, numeric) {
47881 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47882 // Support upper UTF
47883 if (numeric > 65535) {
47885 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47887 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47889 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47892 nativeDecode : function (text) {
47895 makeMap : function (items, delim, map) {
47897 items = items || [];
47898 delim = delim || ',';
47899 if (typeof items == "string") {
47900 items = items.split(delim);
47905 map[items[i]] = {};
47913 Roo.htmleditor.TidyEntities.init();
47915 * @class Roo.htmleditor.KeyEnter
47916 * Handle Enter press..
47917 * @cfg {Roo.HtmlEditorCore} core the editor.
47919 * Create a new Filter.
47920 * @param {Object} config Configuration options
47927 Roo.htmleditor.KeyEnter = function(cfg) {
47928 Roo.apply(this, cfg);
47929 // this does not actually call walk as it's really just a abstract class
47931 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47934 //Roo.htmleditor.KeyEnter.i = 0;
47937 Roo.htmleditor.KeyEnter.prototype = {
47941 keypress : function(e)
47943 if (e.charCode != 13 && e.charCode != 10) {
47944 Roo.log([e.charCode,e]);
47947 e.preventDefault();
47948 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47949 var doc = this.core.doc;
47953 var sel = this.core.getSelection();
47954 var range = sel.getRangeAt(0);
47955 var n = range.commonAncestorContainer;
47956 var pc = range.closest([ 'ol', 'ul']);
47957 var pli = range.closest('li');
47958 if (!pc || e.ctrlKey) {
47959 // on it list, or ctrl pressed.
47961 sel.insertNode('br', 'after');
47963 // only do this if we have ctrl key..
47964 var br = doc.createElement('br');
47965 br.className = 'clear';
47966 br.setAttribute('style', 'clear: both');
47967 sel.insertNode(br, 'after');
47971 this.core.undoManager.addEvent();
47972 this.core.fireEditorEvent(e);
47976 // deal with <li> insetion
47977 if (pli.innerText.trim() == '' &&
47978 pli.previousSibling &&
47979 pli.previousSibling.nodeName == 'LI' &&
47980 pli.previousSibling.innerText.trim() == '') {
47981 pli.parentNode.removeChild(pli.previousSibling);
47982 sel.cursorAfter(pc);
47983 this.core.undoManager.addEvent();
47984 this.core.fireEditorEvent(e);
47988 var li = doc.createElement('LI');
47989 li.innerHTML = ' ';
47990 if (!pli || !pli.firstSibling) {
47991 pc.appendChild(li);
47993 pli.parentNode.insertBefore(li, pli.firstSibling);
47995 sel.cursorText (li.firstChild);
47997 this.core.undoManager.addEvent();
47998 this.core.fireEditorEvent(e);
48010 * @class Roo.htmleditor.Block
48011 * Base class for html editor blocks - do not use it directly .. extend it..
48012 * @cfg {DomElement} node The node to apply stuff to.
48013 * @cfg {String} friendly_name the name that appears in the context bar about this block
48014 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
48017 * Create a new Filter.
48018 * @param {Object} config Configuration options
48021 Roo.htmleditor.Block = function(cfg)
48023 // do nothing .. should not be called really.
48026 * factory method to get the block from an element (using cache if necessary)
48028 * @param {HtmlElement} the dom element
48030 Roo.htmleditor.Block.factory = function(node)
48032 var cc = Roo.htmleditor.Block.cache;
48033 var id = Roo.get(node).id;
48034 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
48035 Roo.htmleditor.Block.cache[id].readElement(node);
48036 return Roo.htmleditor.Block.cache[id];
48038 var db = node.getAttribute('data-block');
48040 db = node.nodeName.toLowerCase().toUpperCaseFirst();
48042 var cls = Roo.htmleditor['Block' + db];
48043 if (typeof(cls) == 'undefined') {
48044 //Roo.log(node.getAttribute('data-block'));
48045 Roo.log("OOps missing block : " + 'Block' + db);
48048 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
48049 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
48053 * initalize all Elements from content that are 'blockable'
48055 * @param the body element
48057 Roo.htmleditor.Block.initAll = function(body, type)
48059 if (typeof(type) == 'undefined') {
48060 var ia = Roo.htmleditor.Block.initAll;
48066 Roo.each(Roo.get(body).query(type), function(e) {
48067 Roo.htmleditor.Block.factory(e);
48070 // question goes here... do we need to clear out this cache sometimes?
48071 // or show we make it relivant to the htmleditor.
48072 Roo.htmleditor.Block.cache = {};
48074 Roo.htmleditor.Block.prototype = {
48078 // used by context menu
48079 friendly_name : 'Based Block',
48081 // text for button to delete this element
48082 deleteTitle : false,
48086 * Update a node with values from this object
48087 * @param {DomElement} node
48089 updateElement : function(node)
48091 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
48094 * convert to plain HTML for calling insertAtCursor..
48096 toHTML : function()
48098 return Roo.DomHelper.markup(this.toObject());
48101 * used by readEleemnt to extract data from a node
48102 * may need improving as it's pretty basic
48104 * @param {DomElement} node
48105 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
48106 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
48107 * @param {String} style the style property - eg. text-align
48109 getVal : function(node, tag, attr, style)
48112 if (tag !== true && n.tagName != tag.toUpperCase()) {
48113 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
48114 // but kiss for now.
48115 n = node.getElementsByTagName(tag).item(0);
48120 if (attr === false) {
48123 if (attr == 'html') {
48124 return n.innerHTML;
48126 if (attr == 'style') {
48127 return n.style[style];
48130 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
48134 * create a DomHelper friendly object - for use with
48135 * Roo.DomHelper.markup / overwrite / etc..
48138 toObject : function()
48143 * Read a node that has a 'data-block' property - and extract the values from it.
48144 * @param {DomElement} node - the node
48146 readElement : function(node)
48157 * @class Roo.htmleditor.BlockFigure
48158 * Block that has an image and a figcaption
48159 * @cfg {String} image_src the url for the image
48160 * @cfg {String} align (left|right) alignment for the block default left
48161 * @cfg {String} caption the text to appear below (and in the alt tag)
48162 * @cfg {String} caption_display (block|none) display or not the caption
48163 * @cfg {String|number} image_width the width of the image number or %?
48164 * @cfg {String|number} image_height the height of the image number or %?
48167 * Create a new Filter.
48168 * @param {Object} config Configuration options
48171 Roo.htmleditor.BlockFigure = function(cfg)
48174 this.readElement(cfg.node);
48175 this.updateElement(cfg.node);
48177 Roo.apply(this, cfg);
48179 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
48186 caption_display : 'block',
48192 // margin: '2%', not used
48194 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
48197 // used by context menu
48198 friendly_name : 'Image with caption',
48199 deleteTitle : "Delete Image and Caption",
48201 contextMenu : function(toolbar)
48204 var block = function() {
48205 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48209 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48211 var syncValue = toolbar.editorcore.syncValue;
48217 xtype : 'TextItem',
48219 xns : rooui.Toolbar //Boostrap?
48223 text: 'Change Image URL',
48226 click: function (btn, state)
48230 Roo.MessageBox.show({
48231 title : "Image Source URL",
48232 msg : "Enter the url for the image",
48233 buttons: Roo.MessageBox.OKCANCEL,
48234 fn: function(btn, val){
48241 toolbar.editorcore.onEditorEvent();
48245 //multiline: multiline,
48247 value : b.image_src
48251 xns : rooui.Toolbar
48256 text: 'Change Link URL',
48259 click: function (btn, state)
48263 Roo.MessageBox.show({
48264 title : "Link URL",
48265 msg : "Enter the url for the link - leave blank to have no link",
48266 buttons: Roo.MessageBox.OKCANCEL,
48267 fn: function(btn, val){
48274 toolbar.editorcore.onEditorEvent();
48278 //multiline: multiline,
48284 xns : rooui.Toolbar
48288 text: 'Show Video URL',
48291 click: function (btn, state)
48293 Roo.MessageBox.alert("Video URL",
48294 block().video_url == '' ? 'This image is not linked ot a video' :
48295 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
48298 xns : rooui.Toolbar
48303 xtype : 'TextItem',
48305 xns : rooui.Toolbar //Boostrap?
48308 xtype : 'ComboBox',
48309 allowBlank : false,
48310 displayField : 'val',
48313 triggerAction : 'all',
48315 valueField : 'val',
48319 select : function (combo, r, index)
48321 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48323 b.width = r.get('val');
48326 toolbar.editorcore.onEditorEvent();
48331 xtype : 'SimpleStore',
48344 xtype : 'TextItem',
48346 xns : rooui.Toolbar //Boostrap?
48349 xtype : 'ComboBox',
48350 allowBlank : false,
48351 displayField : 'val',
48354 triggerAction : 'all',
48356 valueField : 'val',
48360 select : function (combo, r, index)
48362 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48364 b.align = r.get('val');
48367 toolbar.editorcore.onEditorEvent();
48372 xtype : 'SimpleStore',
48386 text: 'Hide Caption',
48387 name : 'caption_display',
48389 enableToggle : true,
48390 setValue : function(v) {
48391 // this trigger toggle.
48393 this.setText(v ? "Hide Caption" : "Show Caption");
48394 this.setPressed(v != 'block');
48397 toggle: function (btn, state)
48400 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48401 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48404 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48405 toolbar.editorcore.onEditorEvent();
48408 xns : rooui.Toolbar
48414 * create a DomHelper friendly object - for use with
48415 * Roo.DomHelper.markup / overwrite / etc..
48417 toObject : function()
48419 var d = document.createElement('div');
48420 d.innerHTML = this.caption;
48422 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
48424 var iw = this.align == 'center' ? this.width : '100%';
48427 contenteditable : 'false',
48428 src : this.image_src,
48429 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48432 maxWidth : iw + ' !important', // this is not getting rendered?
48438 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48440 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
48445 if (this.href.length > 0) {
48449 contenteditable : 'true',
48457 if (this.video_url.length > 0) {
48462 allowfullscreen : true,
48463 width : 420, // these are for video tricks - that we replace the outer
48465 src : this.video_url,
48471 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48472 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48477 'data-block' : 'Figure',
48478 'data-width' : this.width,
48479 contenteditable : 'false',
48483 float : this.align ,
48484 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48485 width : this.align == 'center' ? '100%' : this.width,
48487 padding: this.align == 'center' ? '0' : '0 10px' ,
48488 textAlign : this.align // seems to work for email..
48493 align : this.align,
48499 'data-display' : this.caption_display,
48501 textAlign : 'left',
48503 lineHeight : '24px',
48504 display : this.caption_display,
48505 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
48507 width: this.align == 'center' ? this.width : '100%'
48511 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
48516 marginTop : '16px',
48522 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
48524 contenteditable : true,
48540 readElement : function(node)
48542 // this should not really come from the link...
48543 this.video_url = this.getVal(node, 'div', 'src');
48544 this.cls = this.getVal(node, 'div', 'class');
48545 this.href = this.getVal(node, 'a', 'href');
48548 this.image_src = this.getVal(node, 'img', 'src');
48550 this.align = this.getVal(node, 'figure', 'align');
48551 var figcaption = this.getVal(node, 'figcaption', false);
48552 if (figcaption !== '') {
48553 this.caption = this.getVal(figcaption, 'i', 'html');
48557 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48558 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48559 this.width = this.getVal(node, true, 'data-width');
48560 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48563 removeNode : function()
48580 * @class Roo.htmleditor.BlockTable
48581 * Block that manages a table
48584 * Create a new Filter.
48585 * @param {Object} config Configuration options
48588 Roo.htmleditor.BlockTable = function(cfg)
48591 this.readElement(cfg.node);
48592 this.updateElement(cfg.node);
48594 Roo.apply(this, cfg);
48597 for(var r = 0; r < this.no_row; r++) {
48599 for(var c = 0; c < this.no_col; c++) {
48600 this.rows[r][c] = this.emptyCell();
48607 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48616 // used by context menu
48617 friendly_name : 'Table',
48618 deleteTitle : 'Delete Table',
48619 // context menu is drawn once..
48621 contextMenu : function(toolbar)
48624 var block = function() {
48625 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48629 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48631 var syncValue = toolbar.editorcore.syncValue;
48637 xtype : 'TextItem',
48639 xns : rooui.Toolbar //Boostrap?
48642 xtype : 'ComboBox',
48643 allowBlank : false,
48644 displayField : 'val',
48647 triggerAction : 'all',
48649 valueField : 'val',
48653 select : function (combo, r, index)
48655 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48657 b.width = r.get('val');
48660 toolbar.editorcore.onEditorEvent();
48665 xtype : 'SimpleStore',
48677 xtype : 'TextItem',
48678 text : "Columns: ",
48679 xns : rooui.Toolbar //Boostrap?
48686 click : function (_self, e)
48688 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48689 block().removeColumn();
48691 toolbar.editorcore.onEditorEvent();
48694 xns : rooui.Toolbar
48700 click : function (_self, e)
48702 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48703 block().addColumn();
48705 toolbar.editorcore.onEditorEvent();
48708 xns : rooui.Toolbar
48712 xtype : 'TextItem',
48714 xns : rooui.Toolbar //Boostrap?
48721 click : function (_self, e)
48723 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48724 block().removeRow();
48726 toolbar.editorcore.onEditorEvent();
48729 xns : rooui.Toolbar
48735 click : function (_self, e)
48739 toolbar.editorcore.onEditorEvent();
48742 xns : rooui.Toolbar
48747 text: 'Reset Column Widths',
48750 click : function (_self, e)
48752 block().resetWidths();
48754 toolbar.editorcore.onEditorEvent();
48757 xns : rooui.Toolbar
48768 * create a DomHelper friendly object - for use with
48769 * Roo.DomHelper.markup / overwrite / etc..
48770 * ?? should it be called with option to hide all editing features?
48772 toObject : function()
48777 contenteditable : 'false', // this stops cell selection from picking the table.
48778 'data-block' : 'Table',
48781 border : 'solid 1px #000', // ??? hard coded?
48782 'border-collapse' : 'collapse'
48785 { tag : 'tbody' , cn : [] }
48789 // do we have a head = not really
48791 Roo.each(this.rows, function( row ) {
48796 border : 'solid 1px #000',
48802 ret.cn[0].cn.push(tr);
48803 // does the row have any properties? ?? height?
48805 Roo.each(row, function( cell ) {
48809 contenteditable : 'true',
48810 'data-block' : 'Td',
48814 if (cell.colspan > 1) {
48815 td.colspan = cell.colspan ;
48816 nc += cell.colspan;
48820 if (cell.rowspan > 1) {
48821 td.rowspan = cell.rowspan ;
48830 ncols = Math.max(nc, ncols);
48834 // add the header row..
48843 readElement : function(node)
48845 node = node ? node : this.node ;
48846 this.width = this.getVal(node, true, 'style', 'width') || '100%';
48850 var trs = Array.from(node.rows);
48851 trs.forEach(function(tr) {
48853 this.rows.push(row);
48857 Array.from(tr.cells).forEach(function(td) {
48860 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48861 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48862 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48863 html : td.innerHTML
48865 no_column += add.colspan;
48872 this.no_col = Math.max(this.no_col, no_column);
48879 normalizeRows: function()
48883 this.rows.forEach(function(row) {
48886 row = this.normalizeRow(row);
48888 row.forEach(function(c) {
48889 while (typeof(ret[rid][cid]) != 'undefined') {
48892 if (typeof(ret[rid]) == 'undefined') {
48898 if (c.rowspan < 2) {
48902 for(var i = 1 ;i < c.rowspan; i++) {
48903 if (typeof(ret[rid+i]) == 'undefined') {
48906 ret[rid+i][cid] = c;
48914 normalizeRow: function(row)
48917 row.forEach(function(c) {
48918 if (c.colspan < 2) {
48922 for(var i =0 ;i < c.colspan; i++) {
48930 deleteColumn : function(sel)
48932 if (!sel || sel.type != 'col') {
48935 if (this.no_col < 2) {
48939 this.rows.forEach(function(row) {
48940 var cols = this.normalizeRow(row);
48941 var col = cols[sel.col];
48942 if (col.colspan > 1) {
48952 removeColumn : function()
48954 this.deleteColumn({
48956 col : this.no_col-1
48958 this.updateElement();
48962 addColumn : function()
48965 this.rows.forEach(function(row) {
48966 row.push(this.emptyCell());
48969 this.updateElement();
48972 deleteRow : function(sel)
48974 if (!sel || sel.type != 'row') {
48978 if (this.no_row < 2) {
48982 var rows = this.normalizeRows();
48985 rows[sel.row].forEach(function(col) {
48986 if (col.rowspan > 1) {
48989 col.remove = 1; // flage it as removed.
48994 this.rows.forEach(function(row) {
48996 row.forEach(function(c) {
48997 if (typeof(c.remove) == 'undefined') {
49002 if (newrow.length > 0) {
49006 this.rows = newrows;
49011 this.updateElement();
49014 removeRow : function()
49018 row : this.no_row-1
49024 addRow : function()
49028 for (var i = 0; i < this.no_col; i++ ) {
49030 row.push(this.emptyCell());
49033 this.rows.push(row);
49034 this.updateElement();
49038 // the default cell object... at present...
49039 emptyCell : function() {
49040 return (new Roo.htmleditor.BlockTd({})).toObject();
49045 removeNode : function()
49052 resetWidths : function()
49054 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
49055 var nn = Roo.htmleditor.Block.factory(n);
49057 nn.updateElement(n);
49070 * since selections really work on the table cell, then editing really should work from there
49072 * The original plan was to support merging etc... - but that may not be needed yet..
49074 * So this simple version will support:
49076 * adjust the width +/-
49077 * reset the width...
49086 * @class Roo.htmleditor.BlockTable
49087 * Block that manages a table
49090 * Create a new Filter.
49091 * @param {Object} config Configuration options
49094 Roo.htmleditor.BlockTd = function(cfg)
49097 this.readElement(cfg.node);
49098 this.updateElement(cfg.node);
49100 Roo.apply(this, cfg);
49105 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
49110 textAlign : 'left',
49117 // used by context menu
49118 friendly_name : 'Table Cell',
49119 deleteTitle : false, // use our customer delete
49121 // context menu is drawn once..
49123 contextMenu : function(toolbar)
49126 var cell = function() {
49127 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
49130 var table = function() {
49131 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
49135 var saveSel = function()
49137 lr = toolbar.editorcore.getSelection().getRangeAt(0);
49139 var restoreSel = function()
49143 toolbar.editorcore.focus();
49144 var cr = toolbar.editorcore.getSelection();
49145 cr.removeAllRanges();
49147 toolbar.editorcore.onEditorEvent();
49148 }).defer(10, this);
49154 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
49156 var syncValue = toolbar.editorcore.syncValue;
49163 text : 'Edit Table',
49165 click : function() {
49166 var t = toolbar.tb.selectedNode.closest('table');
49167 toolbar.editorcore.selectNode(t);
49168 toolbar.editorcore.onEditorEvent();
49177 xtype : 'TextItem',
49178 text : "Column Width: ",
49179 xns : rooui.Toolbar
49186 click : function (_self, e)
49188 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49189 cell().shrinkColumn();
49191 toolbar.editorcore.onEditorEvent();
49194 xns : rooui.Toolbar
49200 click : function (_self, e)
49202 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49203 cell().growColumn();
49205 toolbar.editorcore.onEditorEvent();
49208 xns : rooui.Toolbar
49212 xtype : 'TextItem',
49213 text : "Vertical Align: ",
49214 xns : rooui.Toolbar //Boostrap?
49217 xtype : 'ComboBox',
49218 allowBlank : false,
49219 displayField : 'val',
49222 triggerAction : 'all',
49224 valueField : 'val',
49228 select : function (combo, r, index)
49230 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49232 b.valign = r.get('val');
49235 toolbar.editorcore.onEditorEvent();
49240 xtype : 'SimpleStore',
49244 ['bottom'] // there are afew more...
49252 xtype : 'TextItem',
49253 text : "Merge Cells: ",
49254 xns : rooui.Toolbar
49263 click : function (_self, e)
49265 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49266 cell().mergeRight();
49267 //block().growColumn();
49269 toolbar.editorcore.onEditorEvent();
49272 xns : rooui.Toolbar
49279 click : function (_self, e)
49281 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49282 cell().mergeBelow();
49283 //block().growColumn();
49285 toolbar.editorcore.onEditorEvent();
49288 xns : rooui.Toolbar
49291 xtype : 'TextItem',
49293 xns : rooui.Toolbar
49301 click : function (_self, e)
49303 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49306 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49307 toolbar.editorcore.onEditorEvent();
49311 xns : rooui.Toolbar
49315 xns : rooui.Toolbar
49324 xns : rooui.Toolbar,
49333 click : function (_self, e)
49337 cell().deleteColumn();
49339 toolbar.editorcore.selectNode(t.node);
49340 toolbar.editorcore.onEditorEvent();
49349 click : function (_self, e)
49352 cell().deleteRow();
49355 toolbar.editorcore.selectNode(t.node);
49356 toolbar.editorcore.onEditorEvent();
49363 xtype : 'Separator',
49370 click : function (_self, e)
49373 var nn = t.node.nextSibling || t.node.previousSibling;
49374 t.node.parentNode.removeChild(t.node);
49376 toolbar.editorcore.selectNode(nn, true);
49378 toolbar.editorcore.onEditorEvent();
49388 // align... << fixme
49396 * create a DomHelper friendly object - for use with
49397 * Roo.DomHelper.markup / overwrite / etc..
49398 * ?? should it be called with option to hide all editing features?
49401 * create a DomHelper friendly object - for use with
49402 * Roo.DomHelper.markup / overwrite / etc..
49403 * ?? should it be called with option to hide all editing features?
49405 toObject : function()
49409 contenteditable : 'true', // this stops cell selection from picking the table.
49410 'data-block' : 'Td',
49411 valign : this.valign,
49413 'text-align' : this.textAlign,
49414 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49415 'border-collapse' : 'collapse',
49416 padding : '6px', // 8 for desktop / 4 for mobile
49417 'vertical-align': this.valign
49421 if (this.width != '') {
49422 ret.width = this.width;
49423 ret.style.width = this.width;
49427 if (this.colspan > 1) {
49428 ret.colspan = this.colspan ;
49430 if (this.rowspan > 1) {
49431 ret.rowspan = this.rowspan ;
49440 readElement : function(node)
49442 node = node ? node : this.node ;
49443 this.width = node.style.width;
49444 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49445 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49446 this.html = node.innerHTML;
49447 if (node.style.textAlign != '') {
49448 this.textAlign = node.style.textAlign;
49454 // the default cell object... at present...
49455 emptyCell : function() {
49459 textAlign : 'left',
49460 html : " " // is this going to be editable now?
49465 removeNode : function()
49467 return this.node.closest('table');
49475 toTableArray : function()
49478 var tab = this.node.closest('tr').closest('table');
49479 Array.from(tab.rows).forEach(function(r, ri){
49483 this.colWidths = [];
49484 var all_auto = true;
49485 Array.from(tab.rows).forEach(function(r, ri){
49488 Array.from(r.cells).forEach(function(ce, ci){
49493 colspan : ce.colSpan,
49494 rowspan : ce.rowSpan
49496 if (ce.isEqualNode(this.node)) {
49499 // if we have been filled up by a row?
49500 if (typeof(ret[rn][cn]) != 'undefined') {
49501 while(typeof(ret[rn][cn]) != 'undefined') {
49507 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
49508 this.colWidths[cn] = ce.style.width;
49509 if (this.colWidths[cn] != '') {
49515 if (c.colspan < 2 && c.rowspan < 2 ) {
49520 for(var j = 0; j < c.rowspan; j++) {
49521 if (typeof(ret[rn+j]) == 'undefined') {
49522 continue; // we have a problem..
49525 for(var i = 0; i < c.colspan; i++) {
49526 ret[rn+j][cn+i] = c;
49535 // initalize widths.?
49536 // either all widths or no widths..
49538 this.colWidths[0] = false; // no widths flag.
49549 mergeRight: function()
49552 // get the contents of the next cell along..
49553 var tr = this.node.closest('tr');
49554 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49555 if (i >= tr.childNodes.length - 1) {
49556 return; // no cells on right to merge with.
49558 var table = this.toTableArray();
49560 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49561 return; // nothing right?
49563 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49564 // right cell - must be same rowspan and on the same row.
49565 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49566 return; // right hand side is not same rowspan.
49571 this.node.innerHTML += ' ' + rc.cell.innerHTML;
49572 tr.removeChild(rc.cell);
49573 this.colspan += rc.colspan;
49574 this.node.setAttribute('colspan', this.colspan);
49576 var table = this.toTableArray();
49577 this.normalizeWidths(table);
49578 this.updateWidths(table);
49582 mergeBelow : function()
49584 var table = this.toTableArray();
49585 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49586 return; // no row below
49588 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49589 return; // nothing right?
49591 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49593 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49594 return; // right hand side is not same rowspan.
49596 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
49597 rc.cell.parentNode.removeChild(rc.cell);
49598 this.rowspan += rc.rowspan;
49599 this.node.setAttribute('rowspan', this.rowspan);
49604 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49607 var table = this.toTableArray();
49608 var cd = this.cellData;
49612 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49615 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49616 if (r == cd.row && c == cd.col) {
49617 this.node.removeAttribute('rowspan');
49618 this.node.removeAttribute('colspan');
49621 var ntd = this.node.cloneNode(); // which col/row should be 0..
49622 ntd.removeAttribute('id');
49623 ntd.style.width = this.colWidths[c];
49624 ntd.innerHTML = '';
49625 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
49629 this.redrawAllCells(table);
49635 redrawAllCells: function(table)
49639 var tab = this.node.closest('tr').closest('table');
49640 var ctr = tab.rows[0].parentNode;
49641 Array.from(tab.rows).forEach(function(r, ri){
49643 Array.from(r.cells).forEach(function(ce, ci){
49644 ce.parentNode.removeChild(ce);
49646 r.parentNode.removeChild(r);
49648 for(var r = 0 ; r < table.length; r++) {
49649 var re = tab.rows[r];
49651 var re = tab.ownerDocument.createElement('tr');
49652 ctr.appendChild(re);
49653 for(var c = 0 ; c < table[r].length; c++) {
49654 if (table[r][c].cell === false) {
49658 re.appendChild(table[r][c].cell);
49660 table[r][c].cell = false;
49665 updateWidths : function(table)
49667 for(var r = 0 ; r < table.length; r++) {
49669 for(var c = 0 ; c < table[r].length; c++) {
49670 if (table[r][c].cell === false) {
49674 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49675 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49676 el.width = Math.floor(this.colWidths[c]) +'%';
49677 el.updateElement(el.node);
49679 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
49680 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49682 for(var i = 0; i < table[r][c].colspan; i ++) {
49683 width += Math.floor(this.colWidths[c + i]);
49685 el.width = width +'%';
49686 el.updateElement(el.node);
49688 table[r][c].cell = false; // done
49692 normalizeWidths : function(table)
49694 if (this.colWidths[0] === false) {
49695 var nw = 100.0 / this.colWidths.length;
49696 this.colWidths.forEach(function(w,i) {
49697 this.colWidths[i] = nw;
49702 var t = 0, missing = [];
49704 this.colWidths.forEach(function(w,i) {
49706 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49707 var add = this.colWidths[i];
49716 var nc = this.colWidths.length;
49717 if (missing.length) {
49718 var mult = (nc - missing.length) / (1.0 * nc);
49720 var ew = (100 -t) / (1.0 * missing.length);
49721 this.colWidths.forEach(function(w,i) {
49723 this.colWidths[i] = w * mult;
49727 this.colWidths[i] = ew;
49729 // have to make up numbers..
49732 // now we should have all the widths..
49737 shrinkColumn : function()
49739 var table = this.toTableArray();
49740 this.normalizeWidths(table);
49741 var col = this.cellData.col;
49742 var nw = this.colWidths[col] * 0.8;
49746 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49747 this.colWidths.forEach(function(w,i) {
49749 this.colWidths[i] = nw;
49752 this.colWidths[i] += otherAdd
49754 this.updateWidths(table);
49757 growColumn : function()
49759 var table = this.toTableArray();
49760 this.normalizeWidths(table);
49761 var col = this.cellData.col;
49762 var nw = this.colWidths[col] * 1.2;
49766 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
49767 this.colWidths.forEach(function(w,i) {
49769 this.colWidths[i] = nw;
49772 this.colWidths[i] -= otherSub
49774 this.updateWidths(table);
49777 deleteRow : function()
49779 // delete this rows 'tr'
49780 // if any of the cells in this row have a rowspan > 1 && row!= this row..
49781 // then reduce the rowspan.
49782 var table = this.toTableArray();
49783 // this.cellData.row;
49784 for (var i =0;i< table[this.cellData.row].length ; i++) {
49785 var c = table[this.cellData.row][i];
49786 if (c.row != this.cellData.row) {
49789 c.cell.setAttribute('rowspan', c.rowspan);
49792 if (c.rowspan > 1) {
49794 c.cell.setAttribute('rowspan', c.rowspan);
49797 table.splice(this.cellData.row,1);
49798 this.redrawAllCells(table);
49801 deleteColumn : function()
49803 var table = this.toTableArray();
49805 for (var i =0;i< table.length ; i++) {
49806 var c = table[i][this.cellData.col];
49807 if (c.col != this.cellData.col) {
49808 table[i][this.cellData.col].colspan--;
49809 } else if (c.colspan > 1) {
49811 c.cell.setAttribute('colspan', c.colspan);
49813 table[i].splice(this.cellData.col,1);
49816 this.redrawAllCells(table);
49824 //<script type="text/javascript">
49827 * Based Ext JS Library 1.1.1
49828 * Copyright(c) 2006-2007, Ext JS, LLC.
49834 * @class Roo.HtmlEditorCore
49835 * @extends Roo.Component
49836 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49838 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49841 Roo.HtmlEditorCore = function(config){
49844 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49849 * @event initialize
49850 * Fires when the editor is fully initialized (including the iframe)
49851 * @param {Roo.HtmlEditorCore} this
49856 * Fires when the editor is first receives the focus. Any insertion must wait
49857 * until after this event.
49858 * @param {Roo.HtmlEditorCore} this
49862 * @event beforesync
49863 * Fires before the textarea is updated with content from the editor iframe. Return false
49864 * to cancel the sync.
49865 * @param {Roo.HtmlEditorCore} this
49866 * @param {String} html
49870 * @event beforepush
49871 * Fires before the iframe editor is updated with content from the textarea. Return false
49872 * to cancel the push.
49873 * @param {Roo.HtmlEditorCore} this
49874 * @param {String} html
49879 * Fires when the textarea is updated with content from the editor iframe.
49880 * @param {Roo.HtmlEditorCore} this
49881 * @param {String} html
49886 * Fires when the iframe editor is updated with content from the textarea.
49887 * @param {Roo.HtmlEditorCore} this
49888 * @param {String} html
49893 * @event editorevent
49894 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49895 * @param {Roo.HtmlEditorCore} this
49902 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49904 // defaults : white / black...
49905 this.applyBlacklists();
49912 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
49916 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
49922 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
49927 * @cfg {Number} height (in pixels)
49931 * @cfg {Number} width (in pixels)
49935 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49936 * if you are doing an email editor, this probably needs disabling, it's designed
49941 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49943 enableBlocks : true,
49945 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49948 stylesheets: false,
49950 * @cfg {String} language default en - language of text (usefull for rtl languages)
49956 * @cfg {boolean} allowComments - default false - allow comments in HTML source
49957 * - by default they are stripped - if you are editing email you may need this.
49959 allowComments: false,
49963 // private properties
49964 validationEvent : false,
49966 initialized : false,
49968 sourceEditMode : false,
49969 onFocus : Roo.emptyFn,
49971 hideMode:'offsets',
49975 // blacklist + whitelisted elements..
49982 undoManager : false,
49984 * Protected method that will not generally be called directly. It
49985 * is called when the editor initializes the iframe with HTML contents. Override this method if you
49986 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
49988 getDocMarkup : function(){
49992 // inherit styels from page...??
49993 if (this.stylesheets === false) {
49995 Roo.get(document.head).select('style').each(function(node) {
49996 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49999 Roo.get(document.head).select('link').each(function(node) {
50000 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50003 } else if (!this.stylesheets.length) {
50005 st = '<style type="text/css">' +
50006 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50009 for (var i in this.stylesheets) {
50010 if (typeof(this.stylesheets[i]) != 'string') {
50013 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
50018 st += '<style type="text/css">' +
50019 'IMG { cursor: pointer } ' +
50022 st += '<meta name="google" content="notranslate">';
50024 var cls = 'notranslate roo-htmleditor-body';
50026 if(this.bodyCls.length){
50027 cls += ' ' + this.bodyCls;
50030 return '<html class="notranslate" translate="no"><head>' + st +
50031 //<style type="text/css">' +
50032 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50034 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
50038 onRender : function(ct, position)
50041 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
50042 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
50045 this.el.dom.style.border = '0 none';
50046 this.el.dom.setAttribute('tabIndex', -1);
50047 this.el.addClass('x-hidden hide');
50051 if(Roo.isIE){ // fix IE 1px bogus margin
50052 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
50056 this.frameId = Roo.id();
50060 var iframe = this.owner.wrap.createChild({
50062 cls: 'form-control', // bootstrap..
50064 name: this.frameId,
50065 frameBorder : 'no',
50066 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
50071 this.iframe = iframe.dom;
50073 this.assignDocWin();
50075 this.doc.designMode = 'on';
50078 this.doc.write(this.getDocMarkup());
50082 var task = { // must defer to wait for browser to be ready
50084 //console.log("run task?" + this.doc.readyState);
50085 this.assignDocWin();
50086 if(this.doc.body || this.doc.readyState == 'complete'){
50088 this.doc.designMode="on";
50093 Roo.TaskMgr.stop(task);
50094 this.initEditor.defer(10, this);
50101 Roo.TaskMgr.start(task);
50106 onResize : function(w, h)
50108 Roo.log('resize: ' +w + ',' + h );
50109 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
50113 if(typeof w == 'number'){
50115 this.iframe.style.width = w + 'px';
50117 if(typeof h == 'number'){
50119 this.iframe.style.height = h + 'px';
50121 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
50128 * Toggles the editor between standard and source edit mode.
50129 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
50131 toggleSourceEdit : function(sourceEditMode){
50133 this.sourceEditMode = sourceEditMode === true;
50135 if(this.sourceEditMode){
50137 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
50140 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
50141 //this.iframe.className = '';
50144 //this.setSize(this.owner.wrap.getSize());
50145 //this.fireEvent('editmodechange', this, this.sourceEditMode);
50152 * Protected method that will not generally be called directly. If you need/want
50153 * custom HTML cleanup, this is the method you should override.
50154 * @param {String} html The HTML to be cleaned
50155 * return {String} The cleaned HTML
50157 cleanHtml : function(html)
50159 html = String(html);
50160 if(html.length > 5){
50161 if(Roo.isSafari){ // strip safari nonsense
50162 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
50165 if(html == ' '){
50172 * HTML Editor -> Textarea
50173 * Protected method that will not generally be called directly. Syncs the contents
50174 * of the editor iframe with the textarea.
50176 syncValue : function()
50178 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50179 if(this.initialized){
50181 if (this.undoManager) {
50182 this.undoManager.addEvent();
50186 var bd = (this.doc.body || this.doc.documentElement);
50189 var sel = this.win.getSelection();
50191 var div = document.createElement('div');
50192 div.innerHTML = bd.innerHTML;
50193 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50194 if (gtx.length > 0) {
50195 var rm = gtx.item(0).parentNode;
50196 rm.parentNode.removeChild(rm);
50200 if (this.enableBlocks) {
50201 new Roo.htmleditor.FilterBlock({ node : div });
50204 var html = div.innerHTML;
50207 if (this.autoClean) {
50209 new Roo.htmleditor.FilterAttributes({
50211 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
50212 attrib_clean : ['href', 'src' ]
50215 var tidy = new Roo.htmleditor.TidySerializer({
50218 html = tidy.serialize(div);
50224 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50225 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50227 html = '<div style="'+m[0]+'">' + html + '</div>';
50230 html = this.cleanHtml(html);
50231 // fix up the special chars.. normaly like back quotes in word...
50232 // however we do not want to do this with chinese..
50233 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50235 var cc = match.charCodeAt();
50237 // Get the character value, handling surrogate pairs
50238 if (match.length == 2) {
50239 // It's a surrogate pair, calculate the Unicode code point
50240 var high = match.charCodeAt(0) - 0xD800;
50241 var low = match.charCodeAt(1) - 0xDC00;
50242 cc = (high * 0x400) + low + 0x10000;
50244 (cc >= 0x4E00 && cc < 0xA000 ) ||
50245 (cc >= 0x3400 && cc < 0x4E00 ) ||
50246 (cc >= 0xf900 && cc < 0xfb00 )
50251 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50252 return "&#" + cc + ";";
50259 if(this.owner.fireEvent('beforesync', this, html) !== false){
50260 this.el.dom.value = html;
50261 this.owner.fireEvent('sync', this, html);
50267 * TEXTAREA -> EDITABLE
50268 * Protected method that will not generally be called directly. Pushes the value of the textarea
50269 * into the iframe editor.
50271 pushValue : function()
50273 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50274 if(this.initialized){
50275 var v = this.el.dom.value.trim();
50278 if(this.owner.fireEvent('beforepush', this, v) !== false){
50279 var d = (this.doc.body || this.doc.documentElement);
50282 this.el.dom.value = d.innerHTML;
50283 this.owner.fireEvent('push', this, v);
50285 if (this.autoClean) {
50286 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50287 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50289 if (this.enableBlocks) {
50290 Roo.htmleditor.Block.initAll(this.doc.body);
50293 this.updateLanguage();
50295 var lc = this.doc.body.lastChild;
50296 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50297 // add an extra line at the end.
50298 this.doc.body.appendChild(this.doc.createElement('br'));
50306 deferFocus : function(){
50307 this.focus.defer(10, this);
50311 focus : function(){
50312 if(this.win && !this.sourceEditMode){
50319 assignDocWin: function()
50321 var iframe = this.iframe;
50324 this.doc = iframe.contentWindow.document;
50325 this.win = iframe.contentWindow;
50327 // if (!Roo.get(this.frameId)) {
50330 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50331 // this.win = Roo.get(this.frameId).dom.contentWindow;
50333 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50337 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50338 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50343 initEditor : function(){
50344 //console.log("INIT EDITOR");
50345 this.assignDocWin();
50349 this.doc.designMode="on";
50351 this.doc.write(this.getDocMarkup());
50354 var dbody = (this.doc.body || this.doc.documentElement);
50355 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50356 // this copies styles from the containing element into thsi one..
50357 // not sure why we need all of this..
50358 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50360 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50361 //ss['background-attachment'] = 'fixed'; // w3c
50362 dbody.bgProperties = 'fixed'; // ie
50363 dbody.setAttribute("translate", "no");
50365 //Roo.DomHelper.applyStyles(dbody, ss);
50366 Roo.EventManager.on(this.doc, {
50368 'mouseup': this.onEditorEvent,
50369 'dblclick': this.onEditorEvent,
50370 'click': this.onEditorEvent,
50371 'keyup': this.onEditorEvent,
50376 Roo.EventManager.on(this.doc, {
50377 'paste': this.onPasteEvent,
50381 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50384 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50385 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50387 this.initialized = true;
50390 // initialize special key events - enter
50391 new Roo.htmleditor.KeyEnter({core : this});
50395 this.owner.fireEvent('initialize', this);
50398 // this is to prevent a href clicks resulting in a redirect?
50400 onPasteEvent : function(e,v)
50402 // I think we better assume paste is going to be a dirty load of rubish from word..
50404 // even pasting into a 'email version' of this widget will have to clean up that mess.
50405 var cd = (e.browserEvent.clipboardData || window.clipboardData);
50407 // check what type of paste - if it's an image, then handle it differently.
50408 if (cd.files && cd.files.length > 0) {
50410 var urlAPI = (window.createObjectURL && window) ||
50411 (window.URL && URL.revokeObjectURL && URL) ||
50412 (window.webkitURL && webkitURL);
50414 var url = urlAPI.createObjectURL( cd.files[0]);
50415 this.insertAtCursor('<img src=" + url + ">');
50418 if (cd.types.indexOf('text/html') < 0 ) {
50422 var html = cd.getData('text/html'); // clipboard event
50423 if (cd.types.indexOf('text/rtf') > -1) {
50424 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50425 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50430 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50431 .map(function(g) { return g.toDataURL(); })
50432 .filter(function(g) { return g != 'about:blank'; });
50435 html = this.cleanWordChars(html);
50437 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50440 var sn = this.getParentElement();
50441 // check if d contains a table, and prevent nesting??
50442 //Roo.log(d.getElementsByTagName('table'));
50444 //Roo.log(sn.closest('table'));
50445 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50446 e.preventDefault();
50447 this.insertAtCursor("You can not nest tables");
50448 //Roo.log("prevent?"); // fixme -
50454 if (images.length > 0) {
50455 // replace all v:imagedata - with img.
50456 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
50457 Roo.each(ar, function(node) {
50458 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
50459 node.parentNode.removeChild(node);
50463 Roo.each(d.getElementsByTagName('img'), function(img, i) {
50464 img.setAttribute('src', images[i]);
50467 if (this.autoClean) {
50468 new Roo.htmleditor.FilterWord({ node : d });
50470 new Roo.htmleditor.FilterStyleToTag({ node : d });
50471 new Roo.htmleditor.FilterAttributes({
50473 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
50474 attrib_clean : ['href', 'src' ]
50476 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50477 // should be fonts..
50478 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
50479 new Roo.htmleditor.FilterParagraph({ node : d });
50480 new Roo.htmleditor.FilterSpan({ node : d });
50481 new Roo.htmleditor.FilterLongBr({ node : d });
50482 new Roo.htmleditor.FilterComment({ node : d });
50486 if (this.enableBlocks) {
50488 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50489 if (img.closest('figure')) { // assume!! that it's aready
50492 var fig = new Roo.htmleditor.BlockFigure({
50493 image_src : img.src
50495 fig.updateElement(img); // replace it..
50501 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
50502 if (this.enableBlocks) {
50503 Roo.htmleditor.Block.initAll(this.doc.body);
50507 e.preventDefault();
50509 // default behaveiour should be our local cleanup paste? (optional?)
50510 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50511 //this.owner.fireEvent('paste', e, v);
50514 onDestroy : function(){
50520 //for (var i =0; i < this.toolbars.length;i++) {
50521 // // fixme - ask toolbars for heights?
50522 // this.toolbars[i].onDestroy();
50525 //this.wrap.dom.innerHTML = '';
50526 //this.wrap.remove();
50531 onFirstFocus : function(){
50533 this.assignDocWin();
50534 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50536 this.activated = true;
50539 if(Roo.isGecko){ // prevent silly gecko errors
50541 var s = this.win.getSelection();
50542 if(!s.focusNode || s.focusNode.nodeType != 3){
50543 var r = s.getRangeAt(0);
50544 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50549 this.execCmd('useCSS', true);
50550 this.execCmd('styleWithCSS', false);
50553 this.owner.fireEvent('activate', this);
50557 adjustFont: function(btn){
50558 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50559 //if(Roo.isSafari){ // safari
50562 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50563 if(Roo.isSafari){ // safari
50564 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50565 v = (v < 10) ? 10 : v;
50566 v = (v > 48) ? 48 : v;
50567 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50572 v = Math.max(1, v+adjust);
50574 this.execCmd('FontSize', v );
50577 onEditorEvent : function(e)
50581 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50582 return; // we do not handle this.. (undo manager does..)
50584 // in theory this detects if the last element is not a br, then we try and do that.
50585 // its so clicking in space at bottom triggers adding a br and moving the cursor.
50587 e.target.nodeName == 'BODY' &&
50588 e.type == "mouseup" &&
50589 this.doc.body.lastChild
50591 var lc = this.doc.body.lastChild;
50592 // gtx-trans is google translate plugin adding crap.
50593 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50594 lc = lc.previousSibling;
50596 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50597 // if last element is <BR> - then dont do anything.
50599 var ns = this.doc.createElement('br');
50600 this.doc.body.appendChild(ns);
50601 range = this.doc.createRange();
50602 range.setStartAfter(ns);
50603 range.collapse(true);
50604 var sel = this.win.getSelection();
50605 sel.removeAllRanges();
50606 sel.addRange(range);
50612 this.fireEditorEvent(e);
50613 // this.updateToolbar();
50614 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50617 fireEditorEvent: function(e)
50619 this.owner.fireEvent('editorevent', this, e);
50622 insertTag : function(tg)
50624 // could be a bit smarter... -> wrap the current selected tRoo..
50625 if (tg.toLowerCase() == 'span' ||
50626 tg.toLowerCase() == 'code' ||
50627 tg.toLowerCase() == 'sup' ||
50628 tg.toLowerCase() == 'sub'
50631 range = this.createRange(this.getSelection());
50632 var wrappingNode = this.doc.createElement(tg.toLowerCase());
50633 wrappingNode.appendChild(range.extractContents());
50634 range.insertNode(wrappingNode);
50641 this.execCmd("formatblock", tg);
50642 this.undoManager.addEvent();
50645 insertText : function(txt)
50649 var range = this.createRange();
50650 range.deleteContents();
50651 //alert(Sender.getAttribute('label'));
50653 range.insertNode(this.doc.createTextNode(txt));
50654 this.undoManager.addEvent();
50660 * Executes a Midas editor command on the editor document and performs necessary focus and
50661 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50662 * @param {String} cmd The Midas command
50663 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50665 relayCmd : function(cmd, value)
50669 case 'justifyleft':
50670 case 'justifyright':
50671 case 'justifycenter':
50672 // if we are in a cell, then we will adjust the
50673 var n = this.getParentElement();
50674 var td = n.closest('td');
50676 var bl = Roo.htmleditor.Block.factory(td);
50677 bl.textAlign = cmd.replace('justify','');
50678 bl.updateElement();
50679 this.owner.fireEvent('editorevent', this);
50682 this.execCmd('styleWithCSS', true); //
50686 // if there is no selection, then we insert, and set the curson inside it..
50687 this.execCmd('styleWithCSS', false);
50697 this.execCmd(cmd, value);
50698 this.owner.fireEvent('editorevent', this);
50699 //this.updateToolbar();
50700 this.owner.deferFocus();
50704 * Executes a Midas editor command directly on the editor document.
50705 * For visual commands, you should use {@link #relayCmd} instead.
50706 * <b>This should only be called after the editor is initialized.</b>
50707 * @param {String} cmd The Midas command
50708 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50710 execCmd : function(cmd, value){
50711 this.doc.execCommand(cmd, false, value === undefined ? null : value);
50718 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50720 * @param {String} text | dom node..
50722 insertAtCursor : function(text)
50725 if(!this.activated){
50729 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50733 // from jquery ui (MIT licenced)
50735 var win = this.win;
50737 if (win.getSelection && win.getSelection().getRangeAt) {
50739 // delete the existing?
50741 this.createRange(this.getSelection()).deleteContents();
50742 range = win.getSelection().getRangeAt(0);
50743 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50744 range.insertNode(node);
50745 range = range.cloneRange();
50746 range.collapse(false);
50748 win.getSelection().removeAllRanges();
50749 win.getSelection().addRange(range);
50753 } else if (win.document.selection && win.document.selection.createRange) {
50754 // no firefox support
50755 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50756 win.document.selection.createRange().pasteHTML(txt);
50759 // no firefox support
50760 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50761 this.execCmd('InsertHTML', txt);
50769 mozKeyPress : function(e){
50771 var c = e.getCharCode(), cmd;
50774 c = String.fromCharCode(c).toLowerCase();
50788 // this.cleanUpPaste.defer(100, this);
50794 this.relayCmd(cmd);
50795 //this.win.focus();
50796 //this.execCmd(cmd);
50797 //this.deferFocus();
50798 e.preventDefault();
50806 fixKeys : function(){ // load time branching for fastest keydown performance
50810 return function(e){
50811 var k = e.getKey(), r;
50814 r = this.doc.selection.createRange();
50817 r.pasteHTML('    ');
50822 /// this is handled by Roo.htmleditor.KeyEnter
50825 r = this.doc.selection.createRange();
50827 var target = r.parentElement();
50828 if(!target || target.tagName.toLowerCase() != 'li'){
50830 r.pasteHTML('<br/>');
50837 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50838 // this.cleanUpPaste.defer(100, this);
50844 }else if(Roo.isOpera){
50845 return function(e){
50846 var k = e.getKey();
50850 this.execCmd('InsertHTML','    ');
50854 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50855 // this.cleanUpPaste.defer(100, this);
50860 }else if(Roo.isSafari){
50861 return function(e){
50862 var k = e.getKey();
50866 this.execCmd('InsertText','\t');
50870 this.mozKeyPress(e);
50872 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50873 // this.cleanUpPaste.defer(100, this);
50881 getAllAncestors: function()
50883 var p = this.getSelectedNode();
50886 a.push(p); // push blank onto stack..
50887 p = this.getParentElement();
50891 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50895 a.push(this.doc.body);
50899 lastSelNode : false,
50902 getSelection : function()
50904 this.assignDocWin();
50905 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50908 * Select a dom node
50909 * @param {DomElement} node the node to select
50911 selectNode : function(node, collapse)
50913 var nodeRange = node.ownerDocument.createRange();
50915 nodeRange.selectNode(node);
50917 nodeRange.selectNodeContents(node);
50919 if (collapse === true) {
50920 nodeRange.collapse(true);
50923 var s = this.win.getSelection();
50924 s.removeAllRanges();
50925 s.addRange(nodeRange);
50928 getSelectedNode: function()
50930 // this may only work on Gecko!!!
50932 // should we cache this!!!!
50936 var range = this.createRange(this.getSelection()).cloneRange();
50939 var parent = range.parentElement();
50941 var testRange = range.duplicate();
50942 testRange.moveToElementText(parent);
50943 if (testRange.inRange(range)) {
50946 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
50949 parent = parent.parentElement;
50954 // is ancestor a text element.
50955 var ac = range.commonAncestorContainer;
50956 if (ac.nodeType == 3) {
50957 ac = ac.parentNode;
50960 var ar = ac.childNodes;
50963 var other_nodes = [];
50964 var has_other_nodes = false;
50965 for (var i=0;i<ar.length;i++) {
50966 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
50969 // fullly contained node.
50971 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
50976 // probably selected..
50977 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
50978 other_nodes.push(ar[i]);
50982 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
50987 has_other_nodes = true;
50989 if (!nodes.length && other_nodes.length) {
50990 nodes= other_nodes;
50992 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
51000 createRange: function(sel)
51002 // this has strange effects when using with
51003 // top toolbar - not sure if it's a great idea.
51004 //this.editor.contentWindow.focus();
51005 if (typeof sel != "undefined") {
51007 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
51009 return this.doc.createRange();
51012 return this.doc.createRange();
51015 getParentElement: function()
51018 this.assignDocWin();
51019 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
51021 var range = this.createRange(sel);
51024 var p = range.commonAncestorContainer;
51025 while (p.nodeType == 3) { // text node
51036 * Range intersection.. the hard stuff...
51040 * [ -- selected range --- ]
51044 * if end is before start or hits it. fail.
51045 * if start is after end or hits it fail.
51047 * if either hits (but other is outside. - then it's not
51053 // @see http://www.thismuchiknow.co.uk/?p=64.
51054 rangeIntersectsNode : function(range, node)
51056 var nodeRange = node.ownerDocument.createRange();
51058 nodeRange.selectNode(node);
51060 nodeRange.selectNodeContents(node);
51063 var rangeStartRange = range.cloneRange();
51064 rangeStartRange.collapse(true);
51066 var rangeEndRange = range.cloneRange();
51067 rangeEndRange.collapse(false);
51069 var nodeStartRange = nodeRange.cloneRange();
51070 nodeStartRange.collapse(true);
51072 var nodeEndRange = nodeRange.cloneRange();
51073 nodeEndRange.collapse(false);
51075 return rangeStartRange.compareBoundaryPoints(
51076 Range.START_TO_START, nodeEndRange) == -1 &&
51077 rangeEndRange.compareBoundaryPoints(
51078 Range.START_TO_START, nodeStartRange) == 1;
51082 rangeCompareNode : function(range, node)
51084 var nodeRange = node.ownerDocument.createRange();
51086 nodeRange.selectNode(node);
51088 nodeRange.selectNodeContents(node);
51092 range.collapse(true);
51094 nodeRange.collapse(true);
51096 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
51097 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
51099 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
51101 var nodeIsBefore = ss == 1;
51102 var nodeIsAfter = ee == -1;
51104 if (nodeIsBefore && nodeIsAfter) {
51107 if (!nodeIsBefore && nodeIsAfter) {
51108 return 1; //right trailed.
51111 if (nodeIsBefore && !nodeIsAfter) {
51112 return 2; // left trailed.
51118 cleanWordChars : function(input) {// change the chars to hex code
51121 [ 8211, "–" ],
51122 [ 8212, "—" ],
51130 var output = input;
51131 Roo.each(swapCodes, function(sw) {
51132 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
51134 output = output.replace(swapper, sw[1]);
51144 cleanUpChild : function (node)
51147 new Roo.htmleditor.FilterComment({node : node});
51148 new Roo.htmleditor.FilterAttributes({
51150 attrib_black : this.ablack,
51151 attrib_clean : this.aclean,
51152 style_white : this.cwhite,
51153 style_black : this.cblack
51155 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
51156 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
51162 * Clean up MS wordisms...
51163 * @deprecated - use filter directly
51165 cleanWord : function(node)
51167 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
51168 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
51175 * @deprecated - use filters
51177 cleanTableWidths : function(node)
51179 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
51186 applyBlacklists : function()
51188 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
51189 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
51191 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
51192 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
51193 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
51197 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51198 if (b.indexOf(tag) > -1) {
51201 this.white.push(tag);
51205 Roo.each(w, function(tag) {
51206 if (b.indexOf(tag) > -1) {
51209 if (this.white.indexOf(tag) > -1) {
51212 this.white.push(tag);
51217 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51218 if (w.indexOf(tag) > -1) {
51221 this.black.push(tag);
51225 Roo.each(b, function(tag) {
51226 if (w.indexOf(tag) > -1) {
51229 if (this.black.indexOf(tag) > -1) {
51232 this.black.push(tag);
51237 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
51238 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
51242 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51243 if (b.indexOf(tag) > -1) {
51246 this.cwhite.push(tag);
51250 Roo.each(w, function(tag) {
51251 if (b.indexOf(tag) > -1) {
51254 if (this.cwhite.indexOf(tag) > -1) {
51257 this.cwhite.push(tag);
51262 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51263 if (w.indexOf(tag) > -1) {
51266 this.cblack.push(tag);
51270 Roo.each(b, function(tag) {
51271 if (w.indexOf(tag) > -1) {
51274 if (this.cblack.indexOf(tag) > -1) {
51277 this.cblack.push(tag);
51282 setStylesheets : function(stylesheets)
51284 if(typeof(stylesheets) == 'string'){
51285 Roo.get(this.iframe.contentDocument.head).createChild({
51287 rel : 'stylesheet',
51296 Roo.each(stylesheets, function(s) {
51301 Roo.get(_this.iframe.contentDocument.head).createChild({
51303 rel : 'stylesheet',
51313 updateLanguage : function()
51315 if (!this.iframe || !this.iframe.contentDocument) {
51318 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51322 removeStylesheets : function()
51326 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51331 setStyle : function(style)
51333 Roo.get(this.iframe.contentDocument.head).createChild({
51342 // hide stuff that is not compatible
51356 * @event specialkey
51360 * @cfg {String} fieldClass @hide
51363 * @cfg {String} focusClass @hide
51366 * @cfg {String} autoCreate @hide
51369 * @cfg {String} inputType @hide
51372 * @cfg {String} invalidClass @hide
51375 * @cfg {String} invalidText @hide
51378 * @cfg {String} msgFx @hide
51381 * @cfg {String} validateOnBlur @hide
51385 Roo.HtmlEditorCore.white = [
51386 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51388 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
51389 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
51390 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
51391 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
51392 'TABLE', 'UL', 'XMP',
51394 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
51397 'DIR', 'MENU', 'OL', 'UL', 'DL',
51403 Roo.HtmlEditorCore.black = [
51404 // 'embed', 'object', // enable - backend responsiblity to clean thiese
51406 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
51407 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
51408 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
51409 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
51410 //'FONT' // CLEAN LATER..
51411 'COLGROUP', 'COL' // messy tables.
51415 Roo.HtmlEditorCore.clean = [ // ?? needed???
51416 'SCRIPT', 'STYLE', 'TITLE', 'XML'
51418 Roo.HtmlEditorCore.tag_remove = [
51423 Roo.HtmlEditorCore.ablack = [
51427 Roo.HtmlEditorCore.aclean = [
51428 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
51432 Roo.HtmlEditorCore.pwhite= [
51433 'http', 'https', 'mailto'
51436 // white listed style attributes.
51437 Roo.HtmlEditorCore.cwhite= [
51438 // 'text-align', /// default is to allow most things..
51444 // black listed style attributes.
51445 Roo.HtmlEditorCore.cblack= [
51446 // 'font-size' -- this can be set by the project
51452 //<script type="text/javascript">
51455 * Ext JS Library 1.1.1
51456 * Copyright(c) 2006-2007, Ext JS, LLC.
51462 Roo.form.HtmlEditor = function(config){
51466 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51468 if (!this.toolbars) {
51469 this.toolbars = [];
51471 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51477 * @class Roo.form.HtmlEditor
51478 * @extends Roo.form.Field
51479 * Provides a lightweight HTML Editor component.
51481 * This has been tested on Fireforx / Chrome.. IE may not be so great..
51483 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51484 * supported by this editor.</b><br/><br/>
51485 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51486 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51488 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51490 * @cfg {Boolean} clearUp
51494 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51499 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
51504 * @cfg {Number} height (in pixels)
51508 * @cfg {Number} width (in pixels)
51513 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
51516 stylesheets: false,
51520 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51525 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51531 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51536 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51541 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51543 allowComments: false,
51545 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51547 enableBlocks : true,
51550 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51551 * if you are doing an email editor, this probably needs disabling, it's designed
51555 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51559 * @cfg {String} language default en - language of text (usefull for rtl languages)
51568 // private properties
51569 validationEvent : false,
51571 initialized : false,
51574 onFocus : Roo.emptyFn,
51576 hideMode:'offsets',
51578 actionMode : 'container', // defaults to hiding it...
51580 defaultAutoCreate : { // modified by initCompnoent..
51582 style:"width:500px;height:300px;",
51583 autocomplete: "new-password"
51587 initComponent : function(){
51590 * @event initialize
51591 * Fires when the editor is fully initialized (including the iframe)
51592 * @param {HtmlEditor} this
51597 * Fires when the editor is first receives the focus. Any insertion must wait
51598 * until after this event.
51599 * @param {HtmlEditor} this
51603 * @event beforesync
51604 * Fires before the textarea is updated with content from the editor iframe. Return false
51605 * to cancel the sync.
51606 * @param {HtmlEditor} this
51607 * @param {String} html
51611 * @event beforepush
51612 * Fires before the iframe editor is updated with content from the textarea. Return false
51613 * to cancel the push.
51614 * @param {HtmlEditor} this
51615 * @param {String} html
51620 * Fires when the textarea is updated with content from the editor iframe.
51621 * @param {HtmlEditor} this
51622 * @param {String} html
51627 * Fires when the iframe editor is updated with content from the textarea.
51628 * @param {HtmlEditor} this
51629 * @param {String} html
51633 * @event editmodechange
51634 * Fires when the editor switches edit modes
51635 * @param {HtmlEditor} this
51636 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51638 editmodechange: true,
51640 * @event editorevent
51641 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51642 * @param {HtmlEditor} this
51646 * @event firstfocus
51647 * Fires when on first focus - needed by toolbars..
51648 * @param {HtmlEditor} this
51653 * Auto save the htmlEditor value as a file into Events
51654 * @param {HtmlEditor} this
51658 * @event savedpreview
51659 * preview the saved version of htmlEditor
51660 * @param {HtmlEditor} this
51662 savedpreview: true,
51665 * @event stylesheetsclick
51666 * Fires when press the Sytlesheets button
51667 * @param {Roo.HtmlEditorCore} this
51669 stylesheetsclick: true,
51672 * Fires when press user pastes into the editor
51673 * @param {Roo.HtmlEditorCore} this
51677 this.defaultAutoCreate = {
51679 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51680 autocomplete: "new-password"
51685 * Protected method that will not generally be called directly. It
51686 * is called when the editor creates its toolbar. Override this method if you need to
51687 * add custom toolbar buttons.
51688 * @param {HtmlEditor} editor
51690 createToolbar : function(editor){
51691 Roo.log("create toolbars");
51692 if (!editor.toolbars || !editor.toolbars.length) {
51693 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51696 for (var i =0 ; i < editor.toolbars.length;i++) {
51697 editor.toolbars[i] = Roo.factory(
51698 typeof(editor.toolbars[i]) == 'string' ?
51699 { xtype: editor.toolbars[i]} : editor.toolbars[i],
51700 Roo.form.HtmlEditor);
51701 editor.toolbars[i].init(editor);
51707 * get the Context selected node
51708 * @returns {DomElement|boolean} selected node if active or false if none
51711 getSelectedNode : function()
51713 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51716 return this.toolbars[1].tb.selectedNode;
51720 onRender : function(ct, position)
51723 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51725 this.wrap = this.el.wrap({
51726 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51729 this.editorcore.onRender(ct, position);
51731 if (this.resizable) {
51732 this.resizeEl = new Roo.Resizable(this.wrap, {
51736 minHeight : this.height,
51737 height: this.height,
51738 handles : this.resizable,
51741 resize : function(r, w, h) {
51742 _t.onResize(w,h); // -something
51748 this.createToolbar(this);
51752 this.setSize(this.wrap.getSize());
51754 if (this.resizeEl) {
51755 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51756 // should trigger onReize..
51759 this.keyNav = new Roo.KeyNav(this.el, {
51761 "tab" : function(e){
51762 e.preventDefault();
51764 var value = this.getValue();
51766 var start = this.el.dom.selectionStart;
51767 var end = this.el.dom.selectionEnd;
51771 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51772 this.el.dom.setSelectionRange(end + 1, end + 1);
51776 var f = value.substring(0, start).split("\t");
51778 if(f.pop().length != 0){
51782 this.setValue(f.join("\t") + value.substring(end));
51783 this.el.dom.setSelectionRange(start - 1, start - 1);
51787 "home" : function(e){
51788 e.preventDefault();
51790 var curr = this.el.dom.selectionStart;
51791 var lines = this.getValue().split("\n");
51798 this.el.dom.setSelectionRange(0, 0);
51804 for (var i = 0; i < lines.length;i++) {
51805 pos += lines[i].length;
51815 pos -= lines[i].length;
51821 this.el.dom.setSelectionRange(pos, pos);
51825 this.el.dom.selectionStart = pos;
51826 this.el.dom.selectionEnd = curr;
51829 "end" : function(e){
51830 e.preventDefault();
51832 var curr = this.el.dom.selectionStart;
51833 var lines = this.getValue().split("\n");
51840 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51846 for (var i = 0; i < lines.length;i++) {
51848 pos += lines[i].length;
51862 this.el.dom.setSelectionRange(pos, pos);
51866 this.el.dom.selectionStart = curr;
51867 this.el.dom.selectionEnd = pos;
51872 doRelay : function(foo, bar, hname){
51873 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51879 // if(this.autosave && this.w){
51880 // this.autoSaveFn = setInterval(this.autosave, 1000);
51885 onResize : function(w, h)
51887 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51892 if(typeof w == 'number'){
51893 var aw = w - this.wrap.getFrameWidth('lr');
51894 this.el.setWidth(this.adjustWidth('textarea', aw));
51897 if(typeof h == 'number'){
51899 for (var i =0; i < this.toolbars.length;i++) {
51900 // fixme - ask toolbars for heights?
51901 tbh += this.toolbars[i].tb.el.getHeight();
51902 if (this.toolbars[i].footer) {
51903 tbh += this.toolbars[i].footer.el.getHeight();
51910 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51911 ah -= 5; // knock a few pixes off for look..
51913 this.el.setHeight(this.adjustWidth('textarea', ah));
51917 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51918 this.editorcore.onResize(ew,eh);
51923 * Toggles the editor between standard and source edit mode.
51924 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51926 toggleSourceEdit : function(sourceEditMode)
51928 this.editorcore.toggleSourceEdit(sourceEditMode);
51930 if(this.editorcore.sourceEditMode){
51931 Roo.log('editor - showing textarea');
51934 // Roo.log(this.syncValue());
51935 this.editorcore.syncValue();
51936 this.el.removeClass('x-hidden');
51937 this.el.dom.removeAttribute('tabIndex');
51939 this.el.dom.scrollTop = 0;
51942 for (var i = 0; i < this.toolbars.length; i++) {
51943 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51944 this.toolbars[i].tb.hide();
51945 this.toolbars[i].footer.hide();
51950 Roo.log('editor - hiding textarea');
51952 // Roo.log(this.pushValue());
51953 this.editorcore.pushValue();
51955 this.el.addClass('x-hidden');
51956 this.el.dom.setAttribute('tabIndex', -1);
51958 for (var i = 0; i < this.toolbars.length; i++) {
51959 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51960 this.toolbars[i].tb.show();
51961 this.toolbars[i].footer.show();
51965 //this.deferFocus();
51968 this.setSize(this.wrap.getSize());
51969 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
51971 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
51974 // private (for BoxComponent)
51975 adjustSize : Roo.BoxComponent.prototype.adjustSize,
51977 // private (for BoxComponent)
51978 getResizeEl : function(){
51982 // private (for BoxComponent)
51983 getPositionEl : function(){
51988 initEvents : function(){
51989 this.originalValue = this.getValue();
51993 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51996 markInvalid : Roo.emptyFn,
51998 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52001 clearInvalid : Roo.emptyFn,
52003 setValue : function(v){
52004 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
52005 this.editorcore.pushValue();
52009 * update the language in the body - really done by core
52010 * @param {String} language - eg. en / ar / zh-CN etc..
52012 updateLanguage : function(lang)
52014 this.language = lang;
52015 this.editorcore.language = lang;
52016 this.editorcore.updateLanguage();
52020 deferFocus : function(){
52021 this.focus.defer(10, this);
52025 focus : function(){
52026 this.editorcore.focus();
52032 onDestroy : function(){
52038 for (var i =0; i < this.toolbars.length;i++) {
52039 // fixme - ask toolbars for heights?
52040 this.toolbars[i].onDestroy();
52043 this.wrap.dom.innerHTML = '';
52044 this.wrap.remove();
52049 onFirstFocus : function(){
52050 //Roo.log("onFirstFocus");
52051 this.editorcore.onFirstFocus();
52052 for (var i =0; i < this.toolbars.length;i++) {
52053 this.toolbars[i].onFirstFocus();
52059 syncValue : function()
52061 this.editorcore.syncValue();
52064 pushValue : function()
52066 this.editorcore.pushValue();
52069 setStylesheets : function(stylesheets)
52071 this.editorcore.setStylesheets(stylesheets);
52074 removeStylesheets : function()
52076 this.editorcore.removeStylesheets();
52080 // hide stuff that is not compatible
52094 * @event specialkey
52098 * @cfg {String} fieldClass @hide
52101 * @cfg {String} focusClass @hide
52104 * @cfg {String} autoCreate @hide
52107 * @cfg {String} inputType @hide
52110 * @cfg {String} invalidClass @hide
52113 * @cfg {String} invalidText @hide
52116 * @cfg {String} msgFx @hide
52119 * @cfg {String} validateOnBlur @hide
52125 * Ext JS Library 1.1.1
52126 * Copyright(c) 2006-2007, Ext JS, LLC.
52132 * @class Roo.form.HtmlEditor.ToolbarStandard
52137 new Roo.form.HtmlEditor({
52140 new Roo.form.HtmlEditorToolbar1({
52141 disable : { fonts: 1 , format: 1, ..., ... , ...],
52147 * @cfg {Object} disable List of elements to disable..
52148 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
52152 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
52155 Roo.form.HtmlEditor.ToolbarStandard = function(config)
52158 Roo.apply(this, config);
52160 // default disabled, based on 'good practice'..
52161 this.disable = this.disable || {};
52162 Roo.applyIf(this.disable, {
52165 specialElements : true
52169 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52170 // dont call parent... till later.
52173 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
52180 editorcore : false,
52182 * @cfg {Object} disable List of toolbar elements to disable
52189 * @cfg {String} createLinkText The default text for the create link prompt
52191 createLinkText : 'Please enter the URL for the link:',
52193 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52195 defaultLinkValue : 'http:/'+'/',
52199 * @cfg {Array} fontFamilies An array of available font families
52217 // "á" , ?? a acute?
52222 "°" // , // degrees
52224 // "é" , // e ecute
52225 // "ú" , // u ecute?
52228 specialElements : [
52230 text: "Insert Table",
52233 ihtml : '<table><tr><td>Cell</td></tr></table>'
52237 text: "Insert Image",
52240 ihtml : '<img src="about:blank"/>'
52249 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
52250 "input:submit", "input:button", "select", "textarea", "label" ],
52253 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
52255 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52264 * @cfg {String} defaultFont default font to use.
52266 defaultFont: 'tahoma',
52268 fontSelect : false,
52271 formatCombo : false,
52273 init : function(editor)
52275 this.editor = editor;
52276 this.editorcore = editor.editorcore ? editor.editorcore : editor;
52277 var editorcore = this.editorcore;
52281 var fid = editorcore.frameId;
52283 function btn(id, toggle, handler){
52284 var xid = fid + '-'+ id ;
52288 cls : 'x-btn-icon x-edit-'+id,
52289 enableToggle:toggle !== false,
52290 scope: _t, // was editor...
52291 handler:handler||_t.relayBtnCmd,
52292 clickEvent:'mousedown',
52293 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52300 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52302 // stop form submits
52303 tb.el.on('click', function(e){
52304 e.preventDefault(); // what does this do?
52307 if(!this.disable.font) { // && !Roo.isSafari){
52308 /* why no safari for fonts
52309 editor.fontSelect = tb.el.createChild({
52312 cls:'x-font-select',
52313 html: this.createFontOptions()
52316 editor.fontSelect.on('change', function(){
52317 var font = editor.fontSelect.dom.value;
52318 editor.relayCmd('fontname', font);
52319 editor.deferFocus();
52323 editor.fontSelect.dom,
52329 if(!this.disable.formats){
52330 this.formatCombo = new Roo.form.ComboBox({
52331 store: new Roo.data.SimpleStore({
52334 data : this.formats // from states.js
52338 //autoCreate : {tag: "div", size: "20"},
52339 displayField:'tag',
52343 triggerAction: 'all',
52344 emptyText:'Add tag',
52345 selectOnFocus:true,
52348 'select': function(c, r, i) {
52349 editorcore.insertTag(r.get('tag'));
52355 tb.addField(this.formatCombo);
52359 if(!this.disable.format){
52364 btn('strikethrough')
52367 if(!this.disable.fontSize){
52372 btn('increasefontsize', false, editorcore.adjustFont),
52373 btn('decreasefontsize', false, editorcore.adjustFont)
52378 if(!this.disable.colors){
52381 id:editorcore.frameId +'-forecolor',
52382 cls:'x-btn-icon x-edit-forecolor',
52383 clickEvent:'mousedown',
52384 tooltip: this.buttonTips['forecolor'] || undefined,
52386 menu : new Roo.menu.ColorMenu({
52387 allowReselect: true,
52388 focus: Roo.emptyFn,
52391 selectHandler: function(cp, color){
52392 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52393 editor.deferFocus();
52396 clickEvent:'mousedown'
52399 id:editorcore.frameId +'backcolor',
52400 cls:'x-btn-icon x-edit-backcolor',
52401 clickEvent:'mousedown',
52402 tooltip: this.buttonTips['backcolor'] || undefined,
52404 menu : new Roo.menu.ColorMenu({
52405 focus: Roo.emptyFn,
52408 allowReselect: true,
52409 selectHandler: function(cp, color){
52411 editorcore.execCmd('useCSS', false);
52412 editorcore.execCmd('hilitecolor', color);
52413 editorcore.execCmd('useCSS', true);
52414 editor.deferFocus();
52416 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
52417 Roo.isSafari || Roo.isIE ? '#'+color : color);
52418 editor.deferFocus();
52422 clickEvent:'mousedown'
52427 // now add all the items...
52430 if(!this.disable.alignments){
52433 btn('justifyleft'),
52434 btn('justifycenter'),
52435 btn('justifyright')
52439 //if(!Roo.isSafari){
52440 if(!this.disable.links){
52443 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
52447 if(!this.disable.lists){
52450 btn('insertorderedlist'),
52451 btn('insertunorderedlist')
52454 if(!this.disable.sourceEdit){
52457 btn('sourceedit', true, function(btn){
52458 this.toggleSourceEdit(btn.pressed);
52465 // special menu.. - needs to be tidied up..
52466 if (!this.disable.special) {
52469 cls: 'x-edit-none',
52475 for (var i =0; i < this.specialChars.length; i++) {
52476 smenu.menu.items.push({
52478 html: this.specialChars[i],
52479 handler: function(a,b) {
52480 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52481 //editor.insertAtCursor(a.html);
52495 if (!this.disable.cleanStyles) {
52497 cls: 'x-btn-icon x-btn-clear',
52503 for (var i =0; i < this.cleanStyles.length; i++) {
52504 cmenu.menu.items.push({
52505 actiontype : this.cleanStyles[i],
52506 html: 'Remove ' + this.cleanStyles[i],
52507 handler: function(a,b) {
52510 var c = Roo.get(editorcore.doc.body);
52511 c.select('[style]').each(function(s) {
52512 s.dom.style.removeProperty(a.actiontype);
52514 editorcore.syncValue();
52519 cmenu.menu.items.push({
52520 actiontype : 'tablewidths',
52521 html: 'Remove Table Widths',
52522 handler: function(a,b) {
52523 editorcore.cleanTableWidths();
52524 editorcore.syncValue();
52528 cmenu.menu.items.push({
52529 actiontype : 'word',
52530 html: 'Remove MS Word Formating',
52531 handler: function(a,b) {
52532 editorcore.cleanWord();
52533 editorcore.syncValue();
52538 cmenu.menu.items.push({
52539 actiontype : 'all',
52540 html: 'Remove All Styles',
52541 handler: function(a,b) {
52543 var c = Roo.get(editorcore.doc.body);
52544 c.select('[style]').each(function(s) {
52545 s.dom.removeAttribute('style');
52547 editorcore.syncValue();
52552 cmenu.menu.items.push({
52553 actiontype : 'all',
52554 html: 'Remove All CSS Classes',
52555 handler: function(a,b) {
52557 var c = Roo.get(editorcore.doc.body);
52558 c.select('[class]').each(function(s) {
52559 s.dom.removeAttribute('class');
52561 editorcore.cleanWord();
52562 editorcore.syncValue();
52567 cmenu.menu.items.push({
52568 actiontype : 'tidy',
52569 html: 'Tidy HTML Source',
52570 handler: function(a,b) {
52571 new Roo.htmleditor.Tidy(editorcore.doc.body);
52572 editorcore.syncValue();
52581 if (!this.disable.specialElements) {
52584 cls: 'x-edit-none',
52589 for (var i =0; i < this.specialElements.length; i++) {
52590 semenu.menu.items.push(
52592 handler: function(a,b) {
52593 editor.insertAtCursor(this.ihtml);
52595 }, this.specialElements[i])
52607 for(var i =0; i< this.btns.length;i++) {
52608 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52609 b.cls = 'x-edit-none';
52611 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52612 b.cls += ' x-init-enable';
52615 b.scope = editorcore;
52623 // disable everything...
52625 this.tb.items.each(function(item){
52628 item.id != editorcore.frameId+ '-sourceedit' &&
52629 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52635 this.rendered = true;
52637 // the all the btns;
52638 editor.on('editorevent', this.updateToolbar, this);
52639 // other toolbars need to implement this..
52640 //editor.on('editmodechange', this.updateToolbar, this);
52644 relayBtnCmd : function(btn) {
52645 this.editorcore.relayCmd(btn.cmd);
52647 // private used internally
52648 createLink : function(){
52649 //Roo.log("create link?");
52650 var ec = this.editorcore;
52651 var ar = ec.getAllAncestors();
52653 for(var i = 0;i< ar.length;i++) {
52654 if (ar[i] && ar[i].nodeName == 'A') {
52662 Roo.MessageBox.show({
52663 title : "Add / Edit Link URL",
52664 msg : "Enter the url for the link",
52665 buttons: Roo.MessageBox.OKCANCEL,
52666 fn: function(btn, url){
52670 if(url && url != 'http:/'+'/'){
52672 n.setAttribute('href', url);
52674 ec.relayCmd('createlink', url);
52680 //multiline: multiline,
52682 value : n ? n.getAttribute('href') : ''
52686 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52692 * Protected method that will not generally be called directly. It triggers
52693 * a toolbar update by reading the markup state of the current selection in the editor.
52695 updateToolbar: function(){
52697 if(!this.editorcore.activated){
52698 this.editor.onFirstFocus();
52702 var btns = this.tb.items.map,
52703 doc = this.editorcore.doc,
52704 frameId = this.editorcore.frameId;
52706 if(!this.disable.font && !Roo.isSafari){
52708 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52709 if(name != this.fontSelect.dom.value){
52710 this.fontSelect.dom.value = name;
52714 if(!this.disable.format){
52715 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52716 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52717 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52718 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52720 if(!this.disable.alignments){
52721 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52722 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52723 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52725 if(!Roo.isSafari && !this.disable.lists){
52726 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52727 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52730 var ans = this.editorcore.getAllAncestors();
52731 if (this.formatCombo) {
52734 var store = this.formatCombo.store;
52735 this.formatCombo.setValue("");
52736 for (var i =0; i < ans.length;i++) {
52737 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52739 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52747 // hides menus... - so this cant be on a menu...
52748 Roo.menu.MenuMgr.hideAll();
52750 //this.editorsyncValue();
52754 createFontOptions : function(){
52755 var buf = [], fs = this.fontFamilies, ff, lc;
52759 for(var i = 0, len = fs.length; i< len; i++){
52761 lc = ff.toLowerCase();
52763 '<option value="',lc,'" style="font-family:',ff,';"',
52764 (this.defaultFont == lc ? ' selected="true">' : '>'),
52769 return buf.join('');
52772 toggleSourceEdit : function(sourceEditMode){
52774 Roo.log("toolbar toogle");
52775 if(sourceEditMode === undefined){
52776 sourceEditMode = !this.sourceEditMode;
52778 this.sourceEditMode = sourceEditMode === true;
52779 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52780 // just toggle the button?
52781 if(btn.pressed !== this.sourceEditMode){
52782 btn.toggle(this.sourceEditMode);
52786 if(sourceEditMode){
52787 Roo.log("disabling buttons");
52788 this.tb.items.each(function(item){
52789 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52795 Roo.log("enabling buttons");
52796 if(this.editorcore.initialized){
52797 this.tb.items.each(function(item){
52800 // initialize 'blocks'
52801 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52802 Roo.htmleditor.Block.factory(e).updateElement(e);
52808 Roo.log("calling toggole on editor");
52809 // tell the editor that it's been pressed..
52810 this.editor.toggleSourceEdit(sourceEditMode);
52814 * Object collection of toolbar tooltips for the buttons in the editor. The key
52815 * is the command id associated with that button and the value is a valid QuickTips object.
52820 title: 'Bold (Ctrl+B)',
52821 text: 'Make the selected text bold.',
52822 cls: 'x-html-editor-tip'
52825 title: 'Italic (Ctrl+I)',
52826 text: 'Make the selected text italic.',
52827 cls: 'x-html-editor-tip'
52835 title: 'Bold (Ctrl+B)',
52836 text: 'Make the selected text bold.',
52837 cls: 'x-html-editor-tip'
52840 title: 'Italic (Ctrl+I)',
52841 text: 'Make the selected text italic.',
52842 cls: 'x-html-editor-tip'
52845 title: 'Underline (Ctrl+U)',
52846 text: 'Underline the selected text.',
52847 cls: 'x-html-editor-tip'
52850 title: 'Strikethrough',
52851 text: 'Strikethrough the selected text.',
52852 cls: 'x-html-editor-tip'
52854 increasefontsize : {
52855 title: 'Grow Text',
52856 text: 'Increase the font size.',
52857 cls: 'x-html-editor-tip'
52859 decreasefontsize : {
52860 title: 'Shrink Text',
52861 text: 'Decrease the font size.',
52862 cls: 'x-html-editor-tip'
52865 title: 'Text Highlight Color',
52866 text: 'Change the background color of the selected text.',
52867 cls: 'x-html-editor-tip'
52870 title: 'Font Color',
52871 text: 'Change the color of the selected text.',
52872 cls: 'x-html-editor-tip'
52875 title: 'Align Text Left',
52876 text: 'Align text to the left.',
52877 cls: 'x-html-editor-tip'
52880 title: 'Center Text',
52881 text: 'Center text in the editor.',
52882 cls: 'x-html-editor-tip'
52885 title: 'Align Text Right',
52886 text: 'Align text to the right.',
52887 cls: 'x-html-editor-tip'
52889 insertunorderedlist : {
52890 title: 'Bullet List',
52891 text: 'Start a bulleted list.',
52892 cls: 'x-html-editor-tip'
52894 insertorderedlist : {
52895 title: 'Numbered List',
52896 text: 'Start a numbered list.',
52897 cls: 'x-html-editor-tip'
52900 title: 'Hyperlink',
52901 text: 'Make the selected text a hyperlink.',
52902 cls: 'x-html-editor-tip'
52905 title: 'Source Edit',
52906 text: 'Switch to source editing mode.',
52907 cls: 'x-html-editor-tip'
52911 onDestroy : function(){
52914 this.tb.items.each(function(item){
52916 item.menu.removeAll();
52918 item.menu.el.destroy();
52926 onFirstFocus: function() {
52927 this.tb.items.each(function(item){
52936 // <script type="text/javascript">
52939 * Ext JS Library 1.1.1
52940 * Copyright(c) 2006-2007, Ext JS, LLC.
52947 * @class Roo.form.HtmlEditor.ToolbarContext
52952 new Roo.form.HtmlEditor({
52955 { xtype: 'ToolbarStandard', styles : {} }
52956 { xtype: 'ToolbarContext', disable : {} }
52962 * @config : {Object} disable List of elements to disable.. (not done yet.)
52963 * @config : {Object} styles Map of styles available.
52967 Roo.form.HtmlEditor.ToolbarContext = function(config)
52970 Roo.apply(this, config);
52971 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52972 // dont call parent... till later.
52973 this.styles = this.styles || {};
52978 Roo.form.HtmlEditor.ToolbarContext.types = {
52993 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53019 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53090 name : 'selectoptions',
53096 // should we really allow this??
53097 // should this just be
53114 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
53115 Roo.form.HtmlEditor.ToolbarContext.stores = false;
53117 Roo.form.HtmlEditor.ToolbarContext.options = {
53119 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
53120 [ 'Courier New', 'Courier New'],
53121 [ 'Tahoma', 'Tahoma'],
53122 [ 'Times New Roman,serif', 'Times'],
53123 [ 'Verdana','Verdana' ]
53127 // fixme - these need to be configurable..
53130 //Roo.form.HtmlEditor.ToolbarContext.types
53133 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
53140 editorcore : false,
53142 * @cfg {Object} disable List of toolbar elements to disable
53147 * @cfg {Object} styles List of styles
53148 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
53150 * These must be defined in the page, so they get rendered correctly..
53161 init : function(editor)
53163 this.editor = editor;
53164 this.editorcore = editor.editorcore ? editor.editorcore : editor;
53165 var editorcore = this.editorcore;
53167 var fid = editorcore.frameId;
53169 function btn(id, toggle, handler){
53170 var xid = fid + '-'+ id ;
53174 cls : 'x-btn-icon x-edit-'+id,
53175 enableToggle:toggle !== false,
53176 scope: editorcore, // was editor...
53177 handler:handler||editorcore.relayBtnCmd,
53178 clickEvent:'mousedown',
53179 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53183 // create a new element.
53184 var wdiv = editor.wrap.createChild({
53186 }, editor.wrap.dom.firstChild.nextSibling, true);
53188 // can we do this more than once??
53190 // stop form submits
53193 // disable everything...
53194 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53195 this.toolbars = {};
53196 // block toolbars are built in updateToolbar when needed.
53197 for (var i in ty) {
53199 this.toolbars[i] = this.buildToolbar(ty[i],i);
53201 this.tb = this.toolbars.BODY;
53203 this.buildFooter();
53204 this.footer.show();
53205 editor.on('hide', function( ) { this.footer.hide() }, this);
53206 editor.on('show', function( ) { this.footer.show() }, this);
53209 this.rendered = true;
53211 // the all the btns;
53212 editor.on('editorevent', this.updateToolbar, this);
53213 // other toolbars need to implement this..
53214 //editor.on('editmodechange', this.updateToolbar, this);
53220 * Protected method that will not generally be called directly. It triggers
53221 * a toolbar update by reading the markup state of the current selection in the editor.
53223 * Note you can force an update by calling on('editorevent', scope, false)
53225 updateToolbar: function(editor ,ev, sel)
53229 ev.stopEvent(); // se if we can stop this looping with mutiple events.
53233 // capture mouse up - this is handy for selecting images..
53234 // perhaps should go somewhere else...
53235 if(!this.editorcore.activated){
53236 this.editor.onFirstFocus();
53239 //Roo.log(ev ? ev.target : 'NOTARGET');
53242 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53243 // selectNode - might want to handle IE?
53248 (ev.type == 'mouseup' || ev.type == 'click' ) &&
53249 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53250 // they have click on an image...
53251 // let's see if we can change the selection...
53254 // this triggers looping?
53255 //this.editorcore.selectNode(sel);
53259 // this forces an id..
53260 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53261 e.classList.remove('roo-ed-selection');
53263 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53264 //Roo.get(node).addClass('roo-ed-selection');
53266 //var updateFooter = sel ? false : true;
53269 var ans = this.editorcore.getAllAncestors();
53272 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53275 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
53276 sel = sel ? sel : this.editorcore.doc.body;
53277 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53281 var tn = sel.tagName.toUpperCase();
53282 var lastSel = this.tb.selectedNode;
53283 this.tb.selectedNode = sel;
53284 var left_label = tn;
53286 // ok see if we are editing a block?
53289 // you are not actually selecting the block.
53290 if (sel && sel.hasAttribute('data-block')) {
53292 } else if (sel && sel.closest('[data-block]')) {
53294 db = sel.closest('[data-block]');
53295 //var cepar = sel.closest('[contenteditable=true]');
53296 //if (db && cepar && cepar.tagName != 'BODY') {
53297 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53303 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53304 if (db && this.editorcore.enableBlocks) {
53305 block = Roo.htmleditor.Block.factory(db);
53310 db.classList.length > 0 ? db.className + ' ' : ''
53311 ) + 'roo-ed-selection';
53313 // since we removed it earlier... its not there..
53314 tn = 'BLOCK.' + db.getAttribute('data-block');
53316 //this.editorcore.selectNode(db);
53317 if (typeof(this.toolbars[tn]) == 'undefined') {
53318 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
53320 this.toolbars[tn].selectedNode = db;
53321 left_label = block.friendly_name;
53322 ans = this.editorcore.getAllAncestors();
53330 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53331 return; // no change?
53337 ///console.log("show: " + tn);
53338 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53342 this.tb.items.first().el.innerHTML = left_label + ': ';
53345 // update attributes
53346 if (block && this.tb.fields) {
53348 this.tb.fields.each(function(e) {
53349 e.setValue(block[e.name]);
53353 } else if (this.tb.fields && this.tb.selectedNode) {
53354 this.tb.fields.each( function(e) {
53356 e.setValue(this.tb.selectedNode.style[e.stylename]);
53359 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53361 this.updateToolbarStyles(this.tb.selectedNode);
53366 Roo.menu.MenuMgr.hideAll();
53371 // update the footer
53373 this.updateFooter(ans);
53377 updateToolbarStyles : function(sel)
53379 var hasStyles = false;
53380 for(var i in this.styles) {
53386 if (hasStyles && this.tb.hasStyles) {
53387 var st = this.tb.fields.item(0);
53389 st.store.removeAll();
53390 var cn = sel.className.split(/\s+/);
53393 if (this.styles['*']) {
53395 Roo.each(this.styles['*'], function(v) {
53396 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53399 if (this.styles[tn]) {
53400 Roo.each(this.styles[tn], function(v) {
53401 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
53405 st.store.loadData(avs);
53412 updateFooter : function(ans)
53415 if (ans === false) {
53416 this.footDisp.dom.innerHTML = '';
53420 this.footerEls = ans.reverse();
53421 Roo.each(this.footerEls, function(a,i) {
53422 if (!a) { return; }
53423 html += html.length ? ' > ' : '';
53425 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53430 var sz = this.footDisp.up('td').getSize();
53431 this.footDisp.dom.style.width = (sz.width -10) + 'px';
53432 this.footDisp.dom.style.marginLeft = '5px';
53434 this.footDisp.dom.style.overflow = 'hidden';
53436 this.footDisp.dom.innerHTML = html;
53443 onDestroy : function(){
53446 this.tb.items.each(function(item){
53448 item.menu.removeAll();
53450 item.menu.el.destroy();
53458 onFirstFocus: function() {
53459 // need to do this for all the toolbars..
53460 this.tb.items.each(function(item){
53464 buildToolbar: function(tlist, nm, friendly_name, block)
53466 var editor = this.editor;
53467 var editorcore = this.editorcore;
53468 // create a new element.
53469 var wdiv = editor.wrap.createChild({
53471 }, editor.wrap.dom.firstChild.nextSibling, true);
53474 var tb = new Roo.Toolbar(wdiv);
53475 ///this.tb = tb; // << this sets the active toolbar..
53476 if (tlist === false && block) {
53477 tlist = block.contextMenu(this);
53480 tb.hasStyles = false;
53483 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
53485 var styles = Array.from(this.styles);
53489 if (styles && styles.length) {
53490 tb.hasStyles = true;
53491 // this needs a multi-select checkbox...
53492 tb.addField( new Roo.form.ComboBox({
53493 store: new Roo.data.SimpleStore({
53495 fields: ['val', 'selected'],
53498 name : '-roo-edit-className',
53499 attrname : 'className',
53500 displayField: 'val',
53504 triggerAction: 'all',
53505 emptyText:'Select Style',
53506 selectOnFocus:true,
53509 'select': function(c, r, i) {
53510 // initial support only for on class per el..
53511 tb.selectedNode.className = r ? r.get('val') : '';
53512 editorcore.syncValue();
53519 var tbc = Roo.form.HtmlEditor.ToolbarContext;
53522 for (var i = 0; i < tlist.length; i++) {
53524 // newer versions will use xtype cfg to create menus.
53525 if (typeof(tlist[i].xtype) != 'undefined') {
53527 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53533 var item = tlist[i];
53534 tb.add(item.title + ": ");
53537 //optname == used so you can configure the options available..
53538 var opts = item.opts ? item.opts : false;
53539 if (item.optname) { // use the b
53540 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53545 // opts == pulldown..
53546 tb.addField( new Roo.form.ComboBox({
53547 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53549 fields: ['val', 'display'],
53552 name : '-roo-edit-' + tlist[i].name,
53554 attrname : tlist[i].name,
53555 stylename : item.style ? item.style : false,
53557 displayField: item.displayField ? item.displayField : 'val',
53558 valueField : 'val',
53560 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
53562 triggerAction: 'all',
53563 emptyText:'Select',
53564 selectOnFocus:true,
53565 width: item.width ? item.width : 130,
53567 'select': function(c, r, i) {
53571 tb.selectedNode.style[c.stylename] = r.get('val');
53572 editorcore.syncValue();
53576 tb.selectedNode.removeAttribute(c.attrname);
53577 editorcore.syncValue();
53580 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53581 editorcore.syncValue();
53590 tb.addField( new Roo.form.TextField({
53593 //allowBlank:false,
53599 tb.addField( new Roo.form.TextField({
53600 name: '-roo-edit-' + tlist[i].name,
53601 attrname : tlist[i].name,
53607 'change' : function(f, nv, ov) {
53610 tb.selectedNode.setAttribute(f.attrname, nv);
53611 editorcore.syncValue();
53619 var show_delete = !block || block.deleteTitle !== false;
53621 show_delete = false;
53625 text: 'Stylesheets',
53628 click : function ()
53630 _this.editor.fireEvent('stylesheetsclick', _this.editor);
53639 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53642 click : function ()
53644 var sn = tb.selectedNode;
53646 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53652 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53653 if (sn.hasAttribute('data-block')) {
53654 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
53655 sn.parentNode.removeChild(sn);
53657 } else if (sn && sn.tagName != 'BODY') {
53658 // remove and keep parents.
53659 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53664 var range = editorcore.createRange();
53666 range.setStart(stn,0);
53667 range.setEnd(stn,0);
53668 var selection = editorcore.getSelection();
53669 selection.removeAllRanges();
53670 selection.addRange(range);
53673 //_this.updateToolbar(null, null, pn);
53674 _this.updateToolbar(null, null, null);
53675 _this.updateFooter(false);
53686 tb.el.on('click', function(e){
53687 e.preventDefault(); // what does this do?
53689 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53692 // dont need to disable them... as they will get hidden
53697 buildFooter : function()
53700 var fel = this.editor.wrap.createChild();
53701 this.footer = new Roo.Toolbar(fel);
53702 // toolbar has scrolly on left / right?
53703 var footDisp= new Roo.Toolbar.Fill();
53709 handler : function() {
53710 _t.footDisp.scrollTo('left',0,true)
53714 this.footer.add( footDisp );
53719 handler : function() {
53721 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53725 var fel = Roo.get(footDisp.el);
53726 fel.addClass('x-editor-context');
53727 this.footDispWrap = fel;
53728 this.footDispWrap.overflow = 'hidden';
53730 this.footDisp = fel.createChild();
53731 this.footDispWrap.on('click', this.onContextClick, this)
53735 // when the footer contect changes
53736 onContextClick : function (ev,dom)
53738 ev.preventDefault();
53739 var cn = dom.className;
53741 if (!cn.match(/x-ed-loc-/)) {
53744 var n = cn.split('-').pop();
53745 var ans = this.footerEls;
53748 this.editorcore.selectNode(sel);
53751 this.updateToolbar(null, null, sel);
53768 * Ext JS Library 1.1.1
53769 * Copyright(c) 2006-2007, Ext JS, LLC.
53771 * Originally Released Under LGPL - original licence link has changed is not relivant.
53774 * <script type="text/javascript">
53778 * @class Roo.form.BasicForm
53779 * @extends Roo.util.Observable
53780 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53782 * @param {String/HTMLElement/Roo.Element} el The form element or its id
53783 * @param {Object} config Configuration options
53785 Roo.form.BasicForm = function(el, config){
53786 this.allItems = [];
53787 this.childForms = [];
53788 Roo.apply(this, config);
53790 * The Roo.form.Field items in this form.
53791 * @type MixedCollection
53795 this.items = new Roo.util.MixedCollection(false, function(o){
53796 return o.id || (o.id = Roo.id());
53800 * @event beforeaction
53801 * Fires before any action is performed. Return false to cancel the action.
53802 * @param {Form} this
53803 * @param {Action} action The action to be performed
53805 beforeaction: true,
53807 * @event actionfailed
53808 * Fires when an action fails.
53809 * @param {Form} this
53810 * @param {Action} action The action that failed
53812 actionfailed : true,
53814 * @event actioncomplete
53815 * Fires when an action is completed.
53816 * @param {Form} this
53817 * @param {Action} action The action that completed
53819 actioncomplete : true
53824 Roo.form.BasicForm.superclass.constructor.call(this);
53826 Roo.form.BasicForm.popover.apply();
53829 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53831 * @cfg {String} method
53832 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53835 * @cfg {DataReader} reader
53836 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53837 * This is optional as there is built-in support for processing JSON.
53840 * @cfg {DataReader} errorReader
53841 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53842 * This is completely optional as there is built-in support for processing JSON.
53845 * @cfg {String} url
53846 * The URL to use for form actions if one isn't supplied in the action options.
53849 * @cfg {Boolean} fileUpload
53850 * Set to true if this form is a file upload.
53854 * @cfg {Object} baseParams
53855 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53860 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53865 activeAction : null,
53868 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53869 * or setValues() data instead of when the form was first created.
53871 trackResetOnLoad : false,
53875 * childForms - used for multi-tab forms
53878 childForms : false,
53881 * allItems - full list of fields.
53887 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53888 * element by passing it or its id or mask the form itself by passing in true.
53891 waitMsgTarget : false,
53896 disableMask : false,
53899 * @cfg {Boolean} errorMask (true|false) default false
53904 * @cfg {Number} maskOffset Default 100
53909 initEl : function(el){
53910 this.el = Roo.get(el);
53911 this.id = this.el.id || Roo.id();
53912 this.el.on('submit', this.onSubmit, this);
53913 this.el.addClass('x-form');
53917 onSubmit : function(e){
53922 * Returns true if client-side validation on the form is successful.
53925 isValid : function(){
53927 var target = false;
53928 this.items.each(function(f){
53935 if(!target && f.el.isVisible(true)){
53940 if(this.errorMask && !valid){
53941 Roo.form.BasicForm.popover.mask(this, target);
53947 * Returns array of invalid form fields.
53951 invalidFields : function()
53954 this.items.each(function(f){
53967 * DEPRICATED Returns true if any fields in this form have changed since their original load.
53970 isDirty : function(){
53972 this.items.each(function(f){
53982 * Returns true if any fields in this form have changed since their original load. (New version)
53986 hasChanged : function()
53989 this.items.each(function(f){
53990 if(f.hasChanged()){
53999 * Resets all hasChanged to 'false' -
54000 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
54001 * So hasChanged storage is only to be used for this purpose
54004 resetHasChanged : function()
54006 this.items.each(function(f){
54007 f.resetHasChanged();
54014 * Performs a predefined action (submit or load) or custom actions you define on this form.
54015 * @param {String} actionName The name of the action type
54016 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
54017 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
54018 * accept other config options):
54020 Property Type Description
54021 ---------------- --------------- ----------------------------------------------------------------------------------
54022 url String The url for the action (defaults to the form's url)
54023 method String The form method to use (defaults to the form's method, or POST if not defined)
54024 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
54025 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
54026 validate the form on the client (defaults to false)
54028 * @return {BasicForm} this
54030 doAction : function(action, options){
54031 if(typeof action == 'string'){
54032 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
54034 if(this.fireEvent('beforeaction', this, action) !== false){
54035 this.beforeAction(action);
54036 action.run.defer(100, action);
54042 * Shortcut to do a submit action.
54043 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54044 * @return {BasicForm} this
54046 submit : function(options){
54047 this.doAction('submit', options);
54052 * Shortcut to do a load action.
54053 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54054 * @return {BasicForm} this
54056 load : function(options){
54057 this.doAction('load', options);
54062 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
54063 * @param {Record} record The record to edit
54064 * @return {BasicForm} this
54066 updateRecord : function(record){
54067 record.beginEdit();
54068 var fs = record.fields;
54069 fs.each(function(f){
54070 var field = this.findField(f.name);
54072 record.set(f.name, field.getValue());
54080 * Loads an Roo.data.Record into this form.
54081 * @param {Record} record The record to load
54082 * @return {BasicForm} this
54084 loadRecord : function(record){
54085 this.setValues(record.data);
54090 beforeAction : function(action){
54091 var o = action.options;
54093 if(!this.disableMask) {
54094 if(this.waitMsgTarget === true){
54095 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
54096 }else if(this.waitMsgTarget){
54097 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
54098 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
54100 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
54108 afterAction : function(action, success){
54109 this.activeAction = null;
54110 var o = action.options;
54112 if(!this.disableMask) {
54113 if(this.waitMsgTarget === true){
54115 }else if(this.waitMsgTarget){
54116 this.waitMsgTarget.unmask();
54118 Roo.MessageBox.updateProgress(1);
54119 Roo.MessageBox.hide();
54127 Roo.callback(o.success, o.scope, [this, action]);
54128 this.fireEvent('actioncomplete', this, action);
54132 // failure condition..
54133 // we have a scenario where updates need confirming.
54134 // eg. if a locking scenario exists..
54135 // we look for { errors : { needs_confirm : true }} in the response.
54137 (typeof(action.result) != 'undefined') &&
54138 (typeof(action.result.errors) != 'undefined') &&
54139 (typeof(action.result.errors.needs_confirm) != 'undefined')
54142 Roo.MessageBox.confirm(
54143 "Change requires confirmation",
54144 action.result.errorMsg,
54149 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
54159 Roo.callback(o.failure, o.scope, [this, action]);
54160 // show an error message if no failed handler is set..
54161 if (!this.hasListener('actionfailed')) {
54162 Roo.MessageBox.alert("Error",
54163 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
54164 action.result.errorMsg :
54165 "Saving Failed, please check your entries or try again"
54169 this.fireEvent('actionfailed', this, action);
54175 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
54176 * @param {String} id The value to search for
54179 findField : function(id){
54180 var field = this.items.get(id);
54182 this.items.each(function(f){
54183 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
54189 return field || null;
54193 * Add a secondary form to this one,
54194 * Used to provide tabbed forms. One form is primary, with hidden values
54195 * which mirror the elements from the other forms.
54197 * @param {Roo.form.Form} form to add.
54200 addForm : function(form)
54203 if (this.childForms.indexOf(form) > -1) {
54207 this.childForms.push(form);
54209 Roo.each(form.allItems, function (fe) {
54211 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54212 if (this.findField(n)) { // already added..
54215 var add = new Roo.form.Hidden({
54218 add.render(this.el);
54225 * Mark fields in this form invalid in bulk.
54226 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54227 * @return {BasicForm} this
54229 markInvalid : function(errors){
54230 if(errors instanceof Array){
54231 for(var i = 0, len = errors.length; i < len; i++){
54232 var fieldError = errors[i];
54233 var f = this.findField(fieldError.id);
54235 f.markInvalid(fieldError.msg);
54241 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54242 field.markInvalid(errors[id]);
54246 Roo.each(this.childForms || [], function (f) {
54247 f.markInvalid(errors);
54254 * Set values for fields in this form in bulk.
54255 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54256 * @return {BasicForm} this
54258 setValues : function(values){
54259 if(values instanceof Array){ // array of objects
54260 for(var i = 0, len = values.length; i < len; i++){
54262 var f = this.findField(v.id);
54264 f.setValue(v.value);
54265 if(this.trackResetOnLoad){
54266 f.originalValue = f.getValue();
54270 }else{ // object hash
54273 if(typeof values[id] != 'function' && (field = this.findField(id))){
54275 if (field.setFromData &&
54276 field.valueField &&
54277 field.displayField &&
54278 // combos' with local stores can
54279 // be queried via setValue()
54280 // to set their value..
54281 (field.store && !field.store.isLocal)
54285 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54286 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54287 field.setFromData(sd);
54290 field.setValue(values[id]);
54294 if(this.trackResetOnLoad){
54295 field.originalValue = field.getValue();
54300 this.resetHasChanged();
54303 Roo.each(this.childForms || [], function (f) {
54304 f.setValues(values);
54305 f.resetHasChanged();
54312 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54313 * they are returned as an array.
54314 * @param {Boolean} asString
54317 getValues : function(asString)
54319 if (this.childForms) {
54320 // copy values from the child forms
54321 Roo.each(this.childForms, function (f) {
54322 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54327 if (typeof(FormData) != 'undefined' && asString !== true) {
54328 // this relies on a 'recent' version of chrome apparently...
54330 var fd = (new FormData(this.el.dom)).entries();
54332 var ent = fd.next();
54333 while (!ent.done) {
54334 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54345 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54346 if(asString === true){
54349 return Roo.urlDecode(fs);
54353 * Returns the fields in this form as an object with key/value pairs.
54354 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54355 * Normally this will not return readOnly data
54356 * @param {Boolean} with_readonly return readonly field data.
54359 getFieldValues : function(with_readonly)
54361 if (this.childForms) {
54362 // copy values from the child forms
54363 // should this call getFieldValues - probably not as we do not currently copy
54364 // hidden fields when we generate..
54365 Roo.each(this.childForms, function (f) {
54366 this.setValues(f.getFieldValues());
54371 this.items.each(function(f){
54373 if (f.readOnly && with_readonly !== true) {
54374 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54375 // if a subform contains a copy of them.
54376 // if you have subforms with the same editable data, you will need to copy the data back
54380 if (!f.getName()) {
54383 var v = f.getValue();
54384 if (f.inputType =='radio') {
54385 if (typeof(ret[f.getName()]) == 'undefined') {
54386 ret[f.getName()] = ''; // empty..
54389 if (!f.el.dom.checked) {
54393 v = f.el.dom.value;
54397 // not sure if this supported any more..
54398 if ((typeof(v) == 'object') && f.getRawValue) {
54399 v = f.getRawValue() ; // dates..
54401 // combo boxes where name != hiddenName...
54402 if (f.name != f.getName()) {
54403 ret[f.name] = f.getRawValue();
54405 ret[f.getName()] = v;
54412 * Clears all invalid messages in this form.
54413 * @return {BasicForm} this
54415 clearInvalid : function(){
54416 this.items.each(function(f){
54420 Roo.each(this.childForms || [], function (f) {
54429 * Resets this form.
54430 * @return {BasicForm} this
54432 reset : function(){
54433 this.items.each(function(f){
54437 Roo.each(this.childForms || [], function (f) {
54440 this.resetHasChanged();
54446 * Add Roo.form components to this form.
54447 * @param {Field} field1
54448 * @param {Field} field2 (optional)
54449 * @param {Field} etc (optional)
54450 * @return {BasicForm} this
54453 this.items.addAll(Array.prototype.slice.call(arguments, 0));
54459 * Removes a field from the items collection (does NOT remove its markup).
54460 * @param {Field} field
54461 * @return {BasicForm} this
54463 remove : function(field){
54464 this.items.remove(field);
54469 * Looks at the fields in this form, checks them for an id attribute,
54470 * and calls applyTo on the existing dom element with that id.
54471 * @return {BasicForm} this
54473 render : function(){
54474 this.items.each(function(f){
54475 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54483 * Calls {@link Ext#apply} for all fields in this form with the passed object.
54484 * @param {Object} values
54485 * @return {BasicForm} this
54487 applyToFields : function(o){
54488 this.items.each(function(f){
54495 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54496 * @param {Object} values
54497 * @return {BasicForm} this
54499 applyIfToFields : function(o){
54500 this.items.each(function(f){
54508 Roo.BasicForm = Roo.form.BasicForm;
54510 Roo.apply(Roo.form.BasicForm, {
54524 intervalID : false,
54530 if(this.isApplied){
54535 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54536 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54537 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54538 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54541 this.maskEl.top.enableDisplayMode("block");
54542 this.maskEl.left.enableDisplayMode("block");
54543 this.maskEl.bottom.enableDisplayMode("block");
54544 this.maskEl.right.enableDisplayMode("block");
54546 Roo.get(document.body).on('click', function(){
54550 Roo.get(document.body).on('touchstart', function(){
54554 this.isApplied = true
54557 mask : function(form, target)
54561 this.target = target;
54563 if(!this.form.errorMask || !target.el){
54567 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54569 var ot = this.target.el.calcOffsetsTo(scrollable);
54571 var scrollTo = ot[1] - this.form.maskOffset;
54573 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54575 scrollable.scrollTo('top', scrollTo);
54577 var el = this.target.wrap || this.target.el;
54579 var box = el.getBox();
54581 this.maskEl.top.setStyle('position', 'absolute');
54582 this.maskEl.top.setStyle('z-index', 10000);
54583 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54584 this.maskEl.top.setLeft(0);
54585 this.maskEl.top.setTop(0);
54586 this.maskEl.top.show();
54588 this.maskEl.left.setStyle('position', 'absolute');
54589 this.maskEl.left.setStyle('z-index', 10000);
54590 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54591 this.maskEl.left.setLeft(0);
54592 this.maskEl.left.setTop(box.y - this.padding);
54593 this.maskEl.left.show();
54595 this.maskEl.bottom.setStyle('position', 'absolute');
54596 this.maskEl.bottom.setStyle('z-index', 10000);
54597 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54598 this.maskEl.bottom.setLeft(0);
54599 this.maskEl.bottom.setTop(box.bottom + this.padding);
54600 this.maskEl.bottom.show();
54602 this.maskEl.right.setStyle('position', 'absolute');
54603 this.maskEl.right.setStyle('z-index', 10000);
54604 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54605 this.maskEl.right.setLeft(box.right + this.padding);
54606 this.maskEl.right.setTop(box.y - this.padding);
54607 this.maskEl.right.show();
54609 this.intervalID = window.setInterval(function() {
54610 Roo.form.BasicForm.popover.unmask();
54613 window.onwheel = function(){ return false;};
54615 (function(){ this.isMasked = true; }).defer(500, this);
54619 unmask : function()
54621 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54625 this.maskEl.top.setStyle('position', 'absolute');
54626 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54627 this.maskEl.top.hide();
54629 this.maskEl.left.setStyle('position', 'absolute');
54630 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54631 this.maskEl.left.hide();
54633 this.maskEl.bottom.setStyle('position', 'absolute');
54634 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54635 this.maskEl.bottom.hide();
54637 this.maskEl.right.setStyle('position', 'absolute');
54638 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54639 this.maskEl.right.hide();
54641 window.onwheel = function(){ return true;};
54643 if(this.intervalID){
54644 window.clearInterval(this.intervalID);
54645 this.intervalID = false;
54648 this.isMasked = false;
54656 * Ext JS Library 1.1.1
54657 * Copyright(c) 2006-2007, Ext JS, LLC.
54659 * Originally Released Under LGPL - original licence link has changed is not relivant.
54662 * <script type="text/javascript">
54666 * @class Roo.form.Form
54667 * @extends Roo.form.BasicForm
54668 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54669 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54671 * @param {Object} config Configuration options
54673 Roo.form.Form = function(config){
54675 if (config.items) {
54676 xitems = config.items;
54677 delete config.items;
54681 Roo.form.Form.superclass.constructor.call(this, null, config);
54682 this.url = this.url || this.action;
54684 this.root = new Roo.form.Layout(Roo.applyIf({
54688 this.active = this.root;
54690 * Array of all the buttons that have been added to this form via {@link addButton}
54694 this.allItems = [];
54697 * @event clientvalidation
54698 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54699 * @param {Form} this
54700 * @param {Boolean} valid true if the form has passed client-side validation
54702 clientvalidation: true,
54705 * Fires when the form is rendered
54706 * @param {Roo.form.Form} form
54711 if (this.progressUrl) {
54712 // push a hidden field onto the list of fields..
54716 name : 'UPLOAD_IDENTIFIER'
54721 Roo.each(xitems, this.addxtype, this);
54725 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54727 * @cfg {Roo.Button} buttons[] buttons at bottom of form
54731 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54734 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54737 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54739 buttonAlign:'center',
54742 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54747 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54748 * This property cascades to child containers if not set.
54753 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54754 * fires a looping event with that state. This is required to bind buttons to the valid
54755 * state using the config value formBind:true on the button.
54757 monitorValid : false,
54760 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54765 * @cfg {String} progressUrl - Url to return progress data
54768 progressUrl : false,
54770 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54771 * sending a formdata with extra parameters - eg uploaded elements.
54777 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54778 * fields are added and the column is closed. If no fields are passed the column remains open
54779 * until end() is called.
54780 * @param {Object} config The config to pass to the column
54781 * @param {Field} field1 (optional)
54782 * @param {Field} field2 (optional)
54783 * @param {Field} etc (optional)
54784 * @return Column The column container object
54786 column : function(c){
54787 var col = new Roo.form.Column(c);
54789 if(arguments.length > 1){ // duplicate code required because of Opera
54790 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54797 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54798 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54799 * until end() is called.
54800 * @param {Object} config The config to pass to the fieldset
54801 * @param {Field} field1 (optional)
54802 * @param {Field} field2 (optional)
54803 * @param {Field} etc (optional)
54804 * @return FieldSet The fieldset container object
54806 fieldset : function(c){
54807 var fs = new Roo.form.FieldSet(c);
54809 if(arguments.length > 1){ // duplicate code required because of Opera
54810 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54817 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54818 * fields are added and the container is closed. If no fields are passed the container remains open
54819 * until end() is called.
54820 * @param {Object} config The config to pass to the Layout
54821 * @param {Field} field1 (optional)
54822 * @param {Field} field2 (optional)
54823 * @param {Field} etc (optional)
54824 * @return Layout The container object
54826 container : function(c){
54827 var l = new Roo.form.Layout(c);
54829 if(arguments.length > 1){ // duplicate code required because of Opera
54830 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54837 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54838 * @param {Object} container A Roo.form.Layout or subclass of Layout
54839 * @return {Form} this
54841 start : function(c){
54842 // cascade label info
54843 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54844 this.active.stack.push(c);
54845 c.ownerCt = this.active;
54851 * Closes the current open container
54852 * @return {Form} this
54855 if(this.active == this.root){
54858 this.active = this.active.ownerCt;
54863 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
54864 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54865 * as the label of the field.
54866 * @param {Field} field1
54867 * @param {Field} field2 (optional)
54868 * @param {Field} etc. (optional)
54869 * @return {Form} this
54872 this.active.stack.push.apply(this.active.stack, arguments);
54873 this.allItems.push.apply(this.allItems,arguments);
54875 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54876 if(a[i].isFormField){
54881 Roo.form.Form.superclass.add.apply(this, r);
54891 * Find any element that has been added to a form, using it's ID or name
54892 * This can include framesets, columns etc. along with regular fields..
54893 * @param {String} id - id or name to find.
54895 * @return {Element} e - or false if nothing found.
54897 findbyId : function(id)
54903 Roo.each(this.allItems, function(f){
54904 if (f.id == id || f.name == id ){
54915 * Render this form into the passed container. This should only be called once!
54916 * @param {String/HTMLElement/Element} container The element this component should be rendered into
54917 * @return {Form} this
54919 render : function(ct)
54925 var o = this.autoCreate || {
54927 method : this.method || 'POST',
54928 id : this.id || Roo.id()
54930 this.initEl(ct.createChild(o));
54932 this.root.render(this.el);
54936 this.items.each(function(f){
54937 f.render('x-form-el-'+f.id);
54940 if(this.buttons.length > 0){
54941 // tables are required to maintain order and for correct IE layout
54942 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
54943 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
54944 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
54946 var tr = tb.getElementsByTagName('tr')[0];
54947 for(var i = 0, len = this.buttons.length; i < len; i++) {
54948 var b = this.buttons[i];
54949 var td = document.createElement('td');
54950 td.className = 'x-form-btn-td';
54951 b.render(tr.appendChild(td));
54954 if(this.monitorValid){ // initialize after render
54955 this.startMonitoring();
54957 this.fireEvent('rendered', this);
54962 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
54963 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
54964 * object or a valid Roo.DomHelper element config
54965 * @param {Function} handler The function called when the button is clicked
54966 * @param {Object} scope (optional) The scope of the handler function
54967 * @return {Roo.Button}
54969 addButton : function(config, handler, scope){
54973 minWidth: this.minButtonWidth,
54976 if(typeof config == "string"){
54979 Roo.apply(bc, config);
54981 var btn = new Roo.Button(null, bc);
54982 this.buttons.push(btn);
54987 * Adds a series of form elements (using the xtype property as the factory method.
54988 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
54989 * @param {Object} config
54992 addxtype : function()
54994 var ar = Array.prototype.slice.call(arguments, 0);
54996 for(var i = 0; i < ar.length; i++) {
54998 continue; // skip -- if this happends something invalid got sent, we
54999 // should ignore it, as basically that interface element will not show up
55000 // and that should be pretty obvious!!
55003 if (Roo.form[ar[i].xtype]) {
55005 var fe = Roo.factory(ar[i], Roo.form);
55011 fe.store.form = this;
55016 this.allItems.push(fe);
55017 if (fe.items && fe.addxtype) {
55018 fe.addxtype.apply(fe, fe.items);
55028 // console.log('adding ' + ar[i].xtype);
55030 if (ar[i].xtype == 'Button') {
55031 //console.log('adding button');
55032 //console.log(ar[i]);
55033 this.addButton(ar[i]);
55034 this.allItems.push(fe);
55038 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
55039 alert('end is not supported on xtype any more, use items');
55041 // //console.log('adding end');
55049 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
55050 * option "monitorValid"
55052 startMonitoring : function(){
55055 Roo.TaskMgr.start({
55056 run : this.bindHandler,
55057 interval : this.monitorPoll || 200,
55064 * Stops monitoring of the valid state of this form
55066 stopMonitoring : function(){
55067 this.bound = false;
55071 bindHandler : function(){
55073 return false; // stops binding
55076 this.items.each(function(f){
55077 if(!f.isValid(true)){
55082 for(var i = 0, len = this.buttons.length; i < len; i++){
55083 var btn = this.buttons[i];
55084 if(btn.formBind === true && btn.disabled === valid){
55085 btn.setDisabled(!valid);
55088 this.fireEvent('clientvalidation', this, valid);
55102 Roo.Form = Roo.form.Form;
55105 * Ext JS Library 1.1.1
55106 * Copyright(c) 2006-2007, Ext JS, LLC.
55108 * Originally Released Under LGPL - original licence link has changed is not relivant.
55111 * <script type="text/javascript">
55114 // as we use this in bootstrap.
55115 Roo.namespace('Roo.form');
55117 * @class Roo.form.Action
55118 * Internal Class used to handle form actions
55120 * @param {Roo.form.BasicForm} el The form element or its id
55121 * @param {Object} config Configuration options
55126 // define the action interface
55127 Roo.form.Action = function(form, options){
55129 this.options = options || {};
55132 * Client Validation Failed
55135 Roo.form.Action.CLIENT_INVALID = 'client';
55137 * Server Validation Failed
55140 Roo.form.Action.SERVER_INVALID = 'server';
55142 * Connect to Server Failed
55145 Roo.form.Action.CONNECT_FAILURE = 'connect';
55147 * Reading Data from Server Failed
55150 Roo.form.Action.LOAD_FAILURE = 'load';
55152 Roo.form.Action.prototype = {
55154 failureType : undefined,
55155 response : undefined,
55156 result : undefined,
55158 // interface method
55159 run : function(options){
55163 // interface method
55164 success : function(response){
55168 // interface method
55169 handleResponse : function(response){
55173 // default connection failure
55174 failure : function(response){
55176 this.response = response;
55177 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55178 this.form.afterAction(this, false);
55181 processResponse : function(response){
55182 this.response = response;
55183 if(!response.responseText){
55186 this.result = this.handleResponse(response);
55187 return this.result;
55190 // utility functions used internally
55191 getUrl : function(appendParams){
55192 var url = this.options.url || this.form.url || this.form.el.dom.action;
55194 var p = this.getParams();
55196 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55202 getMethod : function(){
55203 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55206 getParams : function(){
55207 var bp = this.form.baseParams;
55208 var p = this.options.params;
55210 if(typeof p == "object"){
55211 p = Roo.urlEncode(Roo.applyIf(p, bp));
55212 }else if(typeof p == 'string' && bp){
55213 p += '&' + Roo.urlEncode(bp);
55216 p = Roo.urlEncode(bp);
55221 createCallback : function(){
55223 success: this.success,
55224 failure: this.failure,
55226 timeout: (this.form.timeout*1000),
55227 upload: this.form.fileUpload ? this.success : undefined
55232 Roo.form.Action.Submit = function(form, options){
55233 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55236 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55239 haveProgress : false,
55240 uploadComplete : false,
55242 // uploadProgress indicator.
55243 uploadProgress : function()
55245 if (!this.form.progressUrl) {
55249 if (!this.haveProgress) {
55250 Roo.MessageBox.progress("Uploading", "Uploading");
55252 if (this.uploadComplete) {
55253 Roo.MessageBox.hide();
55257 this.haveProgress = true;
55259 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55261 var c = new Roo.data.Connection();
55263 url : this.form.progressUrl,
55268 success : function(req){
55269 //console.log(data);
55273 rdata = Roo.decode(req.responseText)
55275 Roo.log("Invalid data from server..");
55279 if (!rdata || !rdata.success) {
55281 Roo.MessageBox.alert(Roo.encode(rdata));
55284 var data = rdata.data;
55286 if (this.uploadComplete) {
55287 Roo.MessageBox.hide();
55292 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55293 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55296 this.uploadProgress.defer(2000,this);
55299 failure: function(data) {
55300 Roo.log('progress url failed ');
55311 // run get Values on the form, so it syncs any secondary forms.
55312 this.form.getValues();
55314 var o = this.options;
55315 var method = this.getMethod();
55316 var isPost = method == 'POST';
55317 if(o.clientValidation === false || this.form.isValid()){
55319 if (this.form.progressUrl) {
55320 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55321 (new Date() * 1) + '' + Math.random());
55326 Roo.Ajax.request(Roo.apply(this.createCallback(), {
55327 form:this.form.el.dom,
55328 url:this.getUrl(!isPost),
55330 params:isPost ? this.getParams() : null,
55331 isUpload: this.form.fileUpload,
55332 formData : this.form.formData
55335 this.uploadProgress();
55337 }else if (o.clientValidation !== false){ // client validation failed
55338 this.failureType = Roo.form.Action.CLIENT_INVALID;
55339 this.form.afterAction(this, false);
55343 success : function(response)
55345 this.uploadComplete= true;
55346 if (this.haveProgress) {
55347 Roo.MessageBox.hide();
55351 var result = this.processResponse(response);
55352 if(result === true || result.success){
55353 this.form.afterAction(this, true);
55357 this.form.markInvalid(result.errors);
55358 this.failureType = Roo.form.Action.SERVER_INVALID;
55360 this.form.afterAction(this, false);
55362 failure : function(response)
55364 this.uploadComplete= true;
55365 if (this.haveProgress) {
55366 Roo.MessageBox.hide();
55369 this.response = response;
55370 this.failureType = Roo.form.Action.CONNECT_FAILURE;
55371 this.form.afterAction(this, false);
55374 handleResponse : function(response){
55375 if(this.form.errorReader){
55376 var rs = this.form.errorReader.read(response);
55379 for(var i = 0, len = rs.records.length; i < len; i++) {
55380 var r = rs.records[i];
55381 errors[i] = r.data;
55384 if(errors.length < 1){
55388 success : rs.success,
55394 ret = Roo.decode(response.responseText);
55398 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55408 Roo.form.Action.Load = function(form, options){
55409 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55410 this.reader = this.form.reader;
55413 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55418 Roo.Ajax.request(Roo.apply(
55419 this.createCallback(), {
55420 method:this.getMethod(),
55421 url:this.getUrl(false),
55422 params:this.getParams()
55426 success : function(response){
55428 var result = this.processResponse(response);
55429 if(result === true || !result.success || !result.data){
55430 this.failureType = Roo.form.Action.LOAD_FAILURE;
55431 this.form.afterAction(this, false);
55434 this.form.clearInvalid();
55435 this.form.setValues(result.data);
55436 this.form.afterAction(this, true);
55439 handleResponse : function(response){
55440 if(this.form.reader){
55441 var rs = this.form.reader.read(response);
55442 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55444 success : rs.success,
55448 return Roo.decode(response.responseText);
55452 Roo.form.Action.ACTION_TYPES = {
55453 'load' : Roo.form.Action.Load,
55454 'submit' : Roo.form.Action.Submit
55457 * Ext JS Library 1.1.1
55458 * Copyright(c) 2006-2007, Ext JS, LLC.
55460 * Originally Released Under LGPL - original licence link has changed is not relivant.
55463 * <script type="text/javascript">
55467 * @class Roo.form.Layout
55468 * @extends Roo.Component
55469 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55470 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55472 * @param {Object} config Configuration options
55474 Roo.form.Layout = function(config){
55476 if (config.items) {
55477 xitems = config.items;
55478 delete config.items;
55480 Roo.form.Layout.superclass.constructor.call(this, config);
55482 Roo.each(xitems, this.addxtype, this);
55486 Roo.extend(Roo.form.Layout, Roo.Component, {
55488 * @cfg {String/Object} autoCreate
55489 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55492 * @cfg {String/Object/Function} style
55493 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55494 * a function which returns such a specification.
55497 * @cfg {String} labelAlign (left|top|right)
55498 * Valid values are "left," "top" and "right" (defaults to "left")
55501 * @cfg {Number} labelWidth
55502 * Fixed width in pixels of all field labels (defaults to undefined)
55505 * @cfg {Boolean} clear
55506 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55510 * @cfg {String} labelSeparator
55511 * The separator to use after field labels (defaults to ':')
55513 labelSeparator : ':',
55515 * @cfg {Boolean} hideLabels
55516 * True to suppress the display of field labels in this layout (defaults to false)
55518 hideLabels : false,
55521 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55526 onRender : function(ct, position){
55527 if(this.el){ // from markup
55528 this.el = Roo.get(this.el);
55529 }else { // generate
55530 var cfg = this.getAutoCreate();
55531 this.el = ct.createChild(cfg, position);
55534 this.el.applyStyles(this.style);
55536 if(this.labelAlign){
55537 this.el.addClass('x-form-label-'+this.labelAlign);
55539 if(this.hideLabels){
55540 this.labelStyle = "display:none";
55541 this.elementStyle = "padding-left:0;";
55543 if(typeof this.labelWidth == 'number'){
55544 this.labelStyle = "width:"+this.labelWidth+"px;";
55545 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55547 if(this.labelAlign == 'top'){
55548 this.labelStyle = "width:auto;";
55549 this.elementStyle = "padding-left:0;";
55552 var stack = this.stack;
55553 var slen = stack.length;
55555 if(!this.fieldTpl){
55556 var t = new Roo.Template(
55557 '<div class="x-form-item {5}">',
55558 '<label for="{0}" style="{2}">{1}{4}</label>',
55559 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55561 '</div><div class="x-form-clear-left"></div>'
55563 t.disableFormats = true;
55565 Roo.form.Layout.prototype.fieldTpl = t;
55567 for(var i = 0; i < slen; i++) {
55568 if(stack[i].isFormField){
55569 this.renderField(stack[i]);
55571 this.renderComponent(stack[i]);
55576 this.el.createChild({cls:'x-form-clear'});
55581 renderField : function(f){
55582 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55585 f.labelStyle||this.labelStyle||'', //2
55586 this.elementStyle||'', //3
55587 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55588 f.itemCls||this.itemCls||'' //5
55589 ], true).getPrevSibling());
55593 renderComponent : function(c){
55594 c.render(c.isLayout ? this.el : this.el.createChild());
55597 * Adds a object form elements (using the xtype property as the factory method.)
55598 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
55599 * @param {Object} config
55601 addxtype : function(o)
55603 // create the lement.
55604 o.form = this.form;
55605 var fe = Roo.factory(o, Roo.form);
55606 this.form.allItems.push(fe);
55607 this.stack.push(fe);
55609 if (fe.isFormField) {
55610 this.form.items.add(fe);
55619 * @class Roo.form.Column
55620 * @extends Roo.form.Layout
55621 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55622 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55624 * @param {Object} config Configuration options
55626 Roo.form.Column = function(config){
55627 Roo.form.Column.superclass.constructor.call(this, config);
55630 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55632 * @cfg {Number/String} width
55633 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55636 * @cfg {String/Object} autoCreate
55637 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55641 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55644 onRender : function(ct, position){
55645 Roo.form.Column.superclass.onRender.call(this, ct, position);
55647 this.el.setWidth(this.width);
55653 * @class Roo.form.Row
55654 * @extends Roo.form.Layout
55655 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55656 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55658 * @param {Object} config Configuration options
55662 Roo.form.Row = function(config){
55663 Roo.form.Row.superclass.constructor.call(this, config);
55666 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55668 * @cfg {Number/String} width
55669 * The fixed width of the column in pixels or CSS value (defaults to "auto")
55672 * @cfg {Number/String} height
55673 * The fixed height of the column in pixels or CSS value (defaults to "auto")
55675 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55679 onRender : function(ct, position){
55680 //console.log('row render');
55682 var t = new Roo.Template(
55683 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55684 '<label for="{0}" style="{2}">{1}{4}</label>',
55685 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55689 t.disableFormats = true;
55691 Roo.form.Layout.prototype.rowTpl = t;
55693 this.fieldTpl = this.rowTpl;
55695 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55696 var labelWidth = 100;
55698 if ((this.labelAlign != 'top')) {
55699 if (typeof this.labelWidth == 'number') {
55700 labelWidth = this.labelWidth
55702 this.padWidth = 20 + labelWidth;
55706 Roo.form.Column.superclass.onRender.call(this, ct, position);
55708 this.el.setWidth(this.width);
55711 this.el.setHeight(this.height);
55716 renderField : function(f){
55717 f.fieldEl = this.fieldTpl.append(this.el, [
55718 f.id, f.fieldLabel,
55719 f.labelStyle||this.labelStyle||'',
55720 this.elementStyle||'',
55721 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55722 f.itemCls||this.itemCls||'',
55723 f.width ? f.width + this.padWidth : 160 + this.padWidth
55730 * @class Roo.form.FieldSet
55731 * @extends Roo.form.Layout
55732 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55733 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55735 * @param {Object} config Configuration options
55737 Roo.form.FieldSet = function(config){
55738 Roo.form.FieldSet.superclass.constructor.call(this, config);
55741 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55743 * @cfg {String} legend
55744 * The text to display as the legend for the FieldSet (defaults to '')
55747 * @cfg {String/Object} autoCreate
55748 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55752 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55755 onRender : function(ct, position){
55756 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55758 this.setLegend(this.legend);
55763 setLegend : function(text){
55765 this.el.child('legend').update(text);
55770 * Ext JS Library 1.1.1
55771 * Copyright(c) 2006-2007, Ext JS, LLC.
55773 * Originally Released Under LGPL - original licence link has changed is not relivant.
55776 * <script type="text/javascript">
55779 * @class Roo.form.VTypes
55780 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55783 Roo.form.VTypes = function(){
55784 // closure these in so they are only created once.
55785 var alpha = /^[a-zA-Z_]+$/;
55786 var alphanum = /^[a-zA-Z0-9_]+$/;
55787 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55788 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55790 // All these messages and functions are configurable
55793 * The function used to validate email addresses
55794 * @param {String} value The email address
55796 'email' : function(v){
55797 return email.test(v);
55800 * The error text to display when the email validation function returns false
55803 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
55805 * The keystroke filter mask to be applied on email input
55808 'emailMask' : /[a-z0-9_\.\-@]/i,
55811 * The function used to validate URLs
55812 * @param {String} value The URL
55814 'url' : function(v){
55815 return url.test(v);
55818 * The error text to display when the url validation function returns false
55821 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55824 * The function used to validate alpha values
55825 * @param {String} value The value
55827 'alpha' : function(v){
55828 return alpha.test(v);
55831 * The error text to display when the alpha validation function returns false
55834 'alphaText' : 'This field should only contain letters and _',
55836 * The keystroke filter mask to be applied on alpha input
55839 'alphaMask' : /[a-z_]/i,
55842 * The function used to validate alphanumeric values
55843 * @param {String} value The value
55845 'alphanum' : function(v){
55846 return alphanum.test(v);
55849 * The error text to display when the alphanumeric validation function returns false
55852 'alphanumText' : 'This field should only contain letters, numbers and _',
55854 * The keystroke filter mask to be applied on alphanumeric input
55857 'alphanumMask' : /[a-z0-9_]/i
55859 }();//<script type="text/javascript">
55862 * @class Roo.form.FCKeditor
55863 * @extends Roo.form.TextArea
55864 * Wrapper around the FCKEditor http://www.fckeditor.net
55866 * Creates a new FCKeditor
55867 * @param {Object} config Configuration options
55869 Roo.form.FCKeditor = function(config){
55870 Roo.form.FCKeditor.superclass.constructor.call(this, config);
55873 * @event editorinit
55874 * Fired when the editor is initialized - you can add extra handlers here..
55875 * @param {FCKeditor} this
55876 * @param {Object} the FCK object.
55883 Roo.form.FCKeditor.editors = { };
55884 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55886 //defaultAutoCreate : {
55887 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
55891 * @cfg {Object} fck options - see fck manual for details.
55896 * @cfg {Object} fck toolbar set (Basic or Default)
55898 toolbarSet : 'Basic',
55900 * @cfg {Object} fck BasePath
55902 basePath : '/fckeditor/',
55910 onRender : function(ct, position)
55913 this.defaultAutoCreate = {
55915 style:"width:300px;height:60px;",
55916 autocomplete: "new-password"
55919 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55922 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55923 if(this.preventScrollbars){
55924 this.el.setStyle("overflow", "hidden");
55926 this.el.setHeight(this.growMin);
55929 //console.log('onrender' + this.getId() );
55930 Roo.form.FCKeditor.editors[this.getId()] = this;
55933 this.replaceTextarea() ;
55937 getEditor : function() {
55938 return this.fckEditor;
55941 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
55942 * @param {Mixed} value The value to set
55946 setValue : function(value)
55948 //console.log('setValue: ' + value);
55950 if(typeof(value) == 'undefined') { // not sure why this is happending...
55953 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55955 //if(!this.el || !this.getEditor()) {
55956 // this.value = value;
55957 //this.setValue.defer(100,this,[value]);
55961 if(!this.getEditor()) {
55965 this.getEditor().SetData(value);
55972 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
55973 * @return {Mixed} value The field value
55975 getValue : function()
55978 if (this.frame && this.frame.dom.style.display == 'none') {
55979 return Roo.form.FCKeditor.superclass.getValue.call(this);
55982 if(!this.el || !this.getEditor()) {
55984 // this.getValue.defer(100,this);
55989 var value=this.getEditor().GetData();
55990 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55991 return Roo.form.FCKeditor.superclass.getValue.call(this);
55997 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
55998 * @return {Mixed} value The field value
56000 getRawValue : function()
56002 if (this.frame && this.frame.dom.style.display == 'none') {
56003 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56006 if(!this.el || !this.getEditor()) {
56007 //this.getRawValue.defer(100,this);
56014 var value=this.getEditor().GetData();
56015 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
56016 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56020 setSize : function(w,h) {
56024 //if (this.frame && this.frame.dom.style.display == 'none') {
56025 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56028 //if(!this.el || !this.getEditor()) {
56029 // this.setSize.defer(100,this, [w,h]);
56035 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56037 this.frame.dom.setAttribute('width', w);
56038 this.frame.dom.setAttribute('height', h);
56039 this.frame.setSize(w,h);
56043 toggleSourceEdit : function(value) {
56047 this.el.dom.style.display = value ? '' : 'none';
56048 this.frame.dom.style.display = value ? 'none' : '';
56053 focus: function(tag)
56055 if (this.frame.dom.style.display == 'none') {
56056 return Roo.form.FCKeditor.superclass.focus.call(this);
56058 if(!this.el || !this.getEditor()) {
56059 this.focus.defer(100,this, [tag]);
56066 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
56067 this.getEditor().Focus();
56069 if (!this.getEditor().Selection.GetSelection()) {
56070 this.focus.defer(100,this, [tag]);
56075 var r = this.getEditor().EditorDocument.createRange();
56076 r.setStart(tgs[0],0);
56077 r.setEnd(tgs[0],0);
56078 this.getEditor().Selection.GetSelection().removeAllRanges();
56079 this.getEditor().Selection.GetSelection().addRange(r);
56080 this.getEditor().Focus();
56087 replaceTextarea : function()
56089 if ( document.getElementById( this.getId() + '___Frame' ) ) {
56092 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
56094 // We must check the elements firstly using the Id and then the name.
56095 var oTextarea = document.getElementById( this.getId() );
56097 var colElementsByName = document.getElementsByName( this.getId() ) ;
56099 oTextarea.style.display = 'none' ;
56101 if ( oTextarea.tabIndex ) {
56102 this.TabIndex = oTextarea.tabIndex ;
56105 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
56106 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
56107 this.frame = Roo.get(this.getId() + '___Frame')
56110 _getConfigHtml : function()
56114 for ( var o in this.fckconfig ) {
56115 sConfig += sConfig.length > 0 ? '&' : '';
56116 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
56119 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
56123 _getIFrameHtml : function()
56125 var sFile = 'fckeditor.html' ;
56126 /* no idea what this is about..
56129 if ( (/fcksource=true/i).test( window.top.location.search ) )
56130 sFile = 'fckeditor.original.html' ;
56135 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
56136 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
56139 var html = '<iframe id="' + this.getId() +
56140 '___Frame" src="' + sLink +
56141 '" width="' + this.width +
56142 '" height="' + this.height + '"' +
56143 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
56144 ' frameborder="0" scrolling="no"></iframe>' ;
56149 _insertHtmlBefore : function( html, element )
56151 if ( element.insertAdjacentHTML ) {
56153 element.insertAdjacentHTML( 'beforeBegin', html ) ;
56155 var oRange = document.createRange() ;
56156 oRange.setStartBefore( element ) ;
56157 var oFragment = oRange.createContextualFragment( html );
56158 element.parentNode.insertBefore( oFragment, element ) ;
56171 //Roo.reg('fckeditor', Roo.form.FCKeditor);
56173 function FCKeditor_OnComplete(editorInstance){
56174 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
56175 f.fckEditor = editorInstance;
56176 //console.log("loaded");
56177 f.fireEvent('editorinit', f, editorInstance);
56197 //<script type="text/javascript">
56199 * @class Roo.form.GridField
56200 * @extends Roo.form.Field
56201 * Embed a grid (or editable grid into a form)
56204 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56206 * xgrid.store = Roo.data.Store
56207 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56208 * xgrid.store.reader = Roo.data.JsonReader
56212 * Creates a new GridField
56213 * @param {Object} config Configuration options
56215 Roo.form.GridField = function(config){
56216 Roo.form.GridField.superclass.constructor.call(this, config);
56220 Roo.extend(Roo.form.GridField, Roo.form.Field, {
56222 * @cfg {Number} width - used to restrict width of grid..
56226 * @cfg {Number} height - used to restrict height of grid..
56230 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56236 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56237 * {tag: "input", type: "checkbox", autocomplete: "off"})
56239 // defaultAutoCreate : { tag: 'div' },
56240 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56242 * @cfg {String} addTitle Text to include for adding a title.
56246 onResize : function(){
56247 Roo.form.Field.superclass.onResize.apply(this, arguments);
56250 initEvents : function(){
56251 // Roo.form.Checkbox.superclass.initEvents.call(this);
56252 // has no events...
56257 getResizeEl : function(){
56261 getPositionEl : function(){
56266 onRender : function(ct, position){
56268 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56269 var style = this.style;
56272 Roo.form.GridField.superclass.onRender.call(this, ct, position);
56273 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56274 this.viewEl = this.wrap.createChild({ tag: 'div' });
56276 this.viewEl.applyStyles(style);
56279 this.viewEl.setWidth(this.width);
56282 this.viewEl.setHeight(this.height);
56284 //if(this.inputValue !== undefined){
56285 //this.setValue(this.value);
56288 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56291 this.grid.render();
56292 this.grid.getDataSource().on('remove', this.refreshValue, this);
56293 this.grid.getDataSource().on('update', this.refreshValue, this);
56294 this.grid.on('afteredit', this.refreshValue, this);
56300 * Sets the value of the item.
56301 * @param {String} either an object or a string..
56303 setValue : function(v){
56305 v = v || []; // empty set..
56306 // this does not seem smart - it really only affects memoryproxy grids..
56307 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56308 var ds = this.grid.getDataSource();
56309 // assumes a json reader..
56311 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
56312 ds.loadData( data);
56314 // clear selection so it does not get stale.
56315 if (this.grid.sm) {
56316 this.grid.sm.clearSelections();
56319 Roo.form.GridField.superclass.setValue.call(this, v);
56320 this.refreshValue();
56321 // should load data in the grid really....
56325 refreshValue: function() {
56327 this.grid.getDataSource().each(function(r) {
56330 this.el.dom.value = Roo.encode(val);
56338 * Ext JS Library 1.1.1
56339 * Copyright(c) 2006-2007, Ext JS, LLC.
56341 * Originally Released Under LGPL - original licence link has changed is not relivant.
56344 * <script type="text/javascript">
56347 * @class Roo.form.DisplayField
56348 * @extends Roo.form.Field
56349 * A generic Field to display non-editable data.
56350 * @cfg {Boolean} closable (true|false) default false
56352 * Creates a new Display Field item.
56353 * @param {Object} config Configuration options
56355 Roo.form.DisplayField = function(config){
56356 Roo.form.DisplayField.superclass.constructor.call(this, config);
56361 * Fires after the click the close btn
56362 * @param {Roo.form.DisplayField} this
56368 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
56369 inputType: 'hidden',
56375 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56377 focusClass : undefined,
56379 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56381 fieldClass: 'x-form-field',
56384 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56386 valueRenderer: undefined,
56390 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56391 * {tag: "input", type: "checkbox", autocomplete: "off"})
56394 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56398 onResize : function(){
56399 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56403 initEvents : function(){
56404 // Roo.form.Checkbox.superclass.initEvents.call(this);
56405 // has no events...
56408 this.closeEl.on('click', this.onClose, this);
56414 getResizeEl : function(){
56418 getPositionEl : function(){
56423 onRender : function(ct, position){
56425 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56426 //if(this.inputValue !== undefined){
56427 this.wrap = this.el.wrap();
56429 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56432 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56435 if (this.bodyStyle) {
56436 this.viewEl.applyStyles(this.bodyStyle);
56438 //this.viewEl.setStyle('padding', '2px');
56440 this.setValue(this.value);
56445 initValue : Roo.emptyFn,
56450 onClick : function(){
56455 * Sets the checked state of the checkbox.
56456 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56458 setValue : function(v){
56460 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
56461 // this might be called before we have a dom element..
56462 if (!this.viewEl) {
56465 this.viewEl.dom.innerHTML = html;
56466 Roo.form.DisplayField.superclass.setValue.call(this, v);
56470 onClose : function(e)
56472 e.preventDefault();
56474 this.fireEvent('close', this);
56483 * @class Roo.form.DayPicker
56484 * @extends Roo.form.Field
56485 * A Day picker show [M] [T] [W] ....
56487 * Creates a new Day Picker
56488 * @param {Object} config Configuration options
56490 Roo.form.DayPicker= function(config){
56491 Roo.form.DayPicker.superclass.constructor.call(this, config);
56495 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
56497 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56499 focusClass : undefined,
56501 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56503 fieldClass: "x-form-field",
56506 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56507 * {tag: "input", type: "checkbox", autocomplete: "off"})
56509 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56512 actionMode : 'viewEl',
56516 inputType : 'hidden',
56519 inputElement: false, // real input element?
56520 basedOn: false, // ????
56522 isFormField: true, // not sure where this is needed!!!!
56524 onResize : function(){
56525 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56526 if(!this.boxLabel){
56527 this.el.alignTo(this.wrap, 'c-c');
56531 initEvents : function(){
56532 Roo.form.Checkbox.superclass.initEvents.call(this);
56533 this.el.on("click", this.onClick, this);
56534 this.el.on("change", this.onClick, this);
56538 getResizeEl : function(){
56542 getPositionEl : function(){
56548 onRender : function(ct, position){
56549 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56551 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56553 var r1 = '<table><tr>';
56554 var r2 = '<tr class="x-form-daypick-icons">';
56555 for (var i=0; i < 7; i++) {
56556 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56557 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
56560 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56561 viewEl.select('img').on('click', this.onClick, this);
56562 this.viewEl = viewEl;
56565 // this will not work on Chrome!!!
56566 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
56567 this.el.on('propertychange', this.setFromHidden, this); //ie
56575 initValue : Roo.emptyFn,
56578 * Returns the checked state of the checkbox.
56579 * @return {Boolean} True if checked, else false
56581 getValue : function(){
56582 return this.el.dom.value;
56587 onClick : function(e){
56588 //this.setChecked(!this.checked);
56589 Roo.get(e.target).toggleClass('x-menu-item-checked');
56590 this.refreshValue();
56591 //if(this.el.dom.checked != this.checked){
56592 // this.setValue(this.el.dom.checked);
56597 refreshValue : function()
56600 this.viewEl.select('img',true).each(function(e,i,n) {
56601 val += e.is(".x-menu-item-checked") ? String(n) : '';
56603 this.setValue(val, true);
56607 * Sets the checked state of the checkbox.
56608 * On is always based on a string comparison between inputValue and the param.
56609 * @param {Boolean/String} value - the value to set
56610 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56612 setValue : function(v,suppressEvent){
56613 if (!this.el.dom) {
56616 var old = this.el.dom.value ;
56617 this.el.dom.value = v;
56618 if (suppressEvent) {
56622 // update display..
56623 this.viewEl.select('img',true).each(function(e,i,n) {
56625 var on = e.is(".x-menu-item-checked");
56626 var newv = v.indexOf(String(n)) > -1;
56628 e.toggleClass('x-menu-item-checked');
56634 this.fireEvent('change', this, v, old);
56639 // handle setting of hidden value by some other method!!?!?
56640 setFromHidden: function()
56645 //console.log("SET FROM HIDDEN");
56646 //alert('setFrom hidden');
56647 this.setValue(this.el.dom.value);
56650 onDestroy : function()
56653 Roo.get(this.viewEl).remove();
56656 Roo.form.DayPicker.superclass.onDestroy.call(this);
56660 * RooJS Library 1.1.1
56661 * Copyright(c) 2008-2011 Alan Knowles
56668 * @class Roo.form.ComboCheck
56669 * @extends Roo.form.ComboBox
56670 * A combobox for multiple select items.
56672 * FIXME - could do with a reset button..
56675 * Create a new ComboCheck
56676 * @param {Object} config Configuration options
56678 Roo.form.ComboCheck = function(config){
56679 Roo.form.ComboCheck.superclass.constructor.call(this, config);
56680 // should verify some data...
56682 // hiddenName = required..
56683 // displayField = required
56684 // valudField == required
56685 var req= [ 'hiddenName', 'displayField', 'valueField' ];
56687 Roo.each(req, function(e) {
56688 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56689 throw "Roo.form.ComboCheck : missing value for: " + e;
56696 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56701 selectedClass: 'x-menu-item-checked',
56704 onRender : function(ct, position){
56710 var cls = 'x-combo-list';
56713 this.tpl = new Roo.Template({
56714 html : '<div class="'+cls+'-item x-menu-check-item">' +
56715 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
56716 '<span>{' + this.displayField + '}</span>' +
56723 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56724 this.view.singleSelect = false;
56725 this.view.multiSelect = true;
56726 this.view.toggleSelect = true;
56727 this.pageTb.add(new Roo.Toolbar.Fill(), {
56730 handler: function()
56737 onViewOver : function(e, t){
56743 onViewClick : function(doFocus,index){
56747 select: function () {
56748 //Roo.log("SELECT CALLED");
56751 selectByValue : function(xv, scrollIntoView){
56752 var ar = this.getValueArray();
56755 Roo.each(ar, function(v) {
56756 if(v === undefined || v === null){
56759 var r = this.findRecord(this.valueField, v);
56761 sels.push(this.store.indexOf(r))
56765 this.view.select(sels);
56771 onSelect : function(record, index){
56772 // Roo.log("onselect Called");
56773 // this is only called by the clear button now..
56774 this.view.clearSelections();
56775 this.setValue('[]');
56776 if (this.value != this.valueBefore) {
56777 this.fireEvent('change', this, this.value, this.valueBefore);
56778 this.valueBefore = this.value;
56781 getValueArray : function()
56786 //Roo.log(this.value);
56787 if (typeof(this.value) == 'undefined') {
56790 var ar = Roo.decode(this.value);
56791 return ar instanceof Array ? ar : []; //?? valid?
56794 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
56799 expand : function ()
56802 Roo.form.ComboCheck.superclass.expand.call(this);
56803 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56804 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56809 collapse : function(){
56810 Roo.form.ComboCheck.superclass.collapse.call(this);
56811 var sl = this.view.getSelectedIndexes();
56812 var st = this.store;
56816 Roo.each(sl, function(i) {
56818 nv.push(r.get(this.valueField));
56820 this.setValue(Roo.encode(nv));
56821 if (this.value != this.valueBefore) {
56823 this.fireEvent('change', this, this.value, this.valueBefore);
56824 this.valueBefore = this.value;
56829 setValue : function(v){
56833 var vals = this.getValueArray();
56835 Roo.each(vals, function(k) {
56836 var r = this.findRecord(this.valueField, k);
56838 tv.push(r.data[this.displayField]);
56839 }else if(this.valueNotFoundText !== undefined){
56840 tv.push( this.valueNotFoundText );
56845 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56846 this.hiddenField.value = v;
56852 * Ext JS Library 1.1.1
56853 * Copyright(c) 2006-2007, Ext JS, LLC.
56855 * Originally Released Under LGPL - original licence link has changed is not relivant.
56858 * <script type="text/javascript">
56862 * @class Roo.form.Signature
56863 * @extends Roo.form.Field
56867 * @param {Object} config Configuration options
56870 Roo.form.Signature = function(config){
56871 Roo.form.Signature.superclass.constructor.call(this, config);
56873 this.addEvents({// not in used??
56876 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56877 * @param {Roo.form.Signature} combo This combo box
56882 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56883 * @param {Roo.form.ComboBox} combo This combo box
56884 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56890 Roo.extend(Roo.form.Signature, Roo.form.Field, {
56892 * @cfg {Object} labels Label to use when rendering a form.
56896 * confirm : "Confirm"
56901 confirm : "Confirm"
56904 * @cfg {Number} width The signature panel width (defaults to 300)
56908 * @cfg {Number} height The signature panel height (defaults to 100)
56912 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56914 allowBlank : false,
56917 // {Object} signPanel The signature SVG panel element (defaults to {})
56919 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56920 isMouseDown : false,
56921 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56922 isConfirmed : false,
56923 // {String} signatureTmp SVG mapping string (defaults to empty string)
56927 defaultAutoCreate : { // modified by initCompnoent..
56933 onRender : function(ct, position){
56935 Roo.form.Signature.superclass.onRender.call(this, ct, position);
56937 this.wrap = this.el.wrap({
56938 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
56941 this.createToolbar(this);
56942 this.signPanel = this.wrap.createChild({
56944 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
56948 this.svgID = Roo.id();
56949 this.svgEl = this.signPanel.createChild({
56950 xmlns : 'http://www.w3.org/2000/svg',
56952 id : this.svgID + "-svg",
56954 height: this.height,
56955 viewBox: '0 0 '+this.width+' '+this.height,
56959 id: this.svgID + "-svg-r",
56961 height: this.height,
56966 id: this.svgID + "-svg-l",
56968 y1: (this.height*0.8), // start set the line in 80% of height
56969 x2: this.width, // end
56970 y2: (this.height*0.8), // end set the line in 80% of height
56972 'stroke-width': "1",
56973 'stroke-dasharray': "3",
56974 'shape-rendering': "crispEdges",
56975 'pointer-events': "none"
56979 id: this.svgID + "-svg-p",
56981 'stroke-width': "3",
56983 'pointer-events': 'none'
56988 this.svgBox = this.svgEl.dom.getScreenCTM();
56990 createSVG : function(){
56991 var svg = this.signPanel;
56992 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
56995 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
56996 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
56997 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
56998 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
56999 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
57000 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
57001 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
57004 isTouchEvent : function(e){
57005 return e.type.match(/^touch/);
57007 getCoords : function (e) {
57008 var pt = this.svgEl.dom.createSVGPoint();
57011 if (this.isTouchEvent(e)) {
57012 pt.x = e.targetTouches[0].clientX;
57013 pt.y = e.targetTouches[0].clientY;
57015 var a = this.svgEl.dom.getScreenCTM();
57016 var b = a.inverse();
57017 var mx = pt.matrixTransform(b);
57018 return mx.x + ',' + mx.y;
57020 //mouse event headler
57021 down : function (e) {
57022 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
57023 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
57025 this.isMouseDown = true;
57027 e.preventDefault();
57029 move : function (e) {
57030 if (this.isMouseDown) {
57031 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
57032 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
57035 e.preventDefault();
57037 up : function (e) {
57038 this.isMouseDown = false;
57039 var sp = this.signatureTmp.split(' ');
57042 if(!sp[sp.length-2].match(/^L/)){
57046 this.signatureTmp = sp.join(" ");
57049 if(this.getValue() != this.signatureTmp){
57050 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57051 this.isConfirmed = false;
57053 e.preventDefault();
57057 * Protected method that will not generally be called directly. It
57058 * is called when the editor creates its toolbar. Override this method if you need to
57059 * add custom toolbar buttons.
57060 * @param {HtmlEditor} editor
57062 createToolbar : function(editor){
57063 function btn(id, toggle, handler){
57064 var xid = fid + '-'+ id ;
57068 cls : 'x-btn-icon x-edit-'+id,
57069 enableToggle:toggle !== false,
57070 scope: editor, // was editor...
57071 handler:handler||editor.relayBtnCmd,
57072 clickEvent:'mousedown',
57073 tooltip: etb.buttonTips[id] || undefined, ///tips ???
57079 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
57083 cls : ' x-signature-btn x-signature-'+id,
57084 scope: editor, // was editor...
57085 handler: this.reset,
57086 clickEvent:'mousedown',
57087 text: this.labels.clear
57094 cls : ' x-signature-btn x-signature-'+id,
57095 scope: editor, // was editor...
57096 handler: this.confirmHandler,
57097 clickEvent:'mousedown',
57098 text: this.labels.confirm
57105 * when user is clicked confirm then show this image.....
57107 * @return {String} Image Data URI
57109 getImageDataURI : function(){
57110 var svg = this.svgEl.dom.parentNode.innerHTML;
57111 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
57116 * @return {Boolean} this.isConfirmed
57118 getConfirmed : function(){
57119 return this.isConfirmed;
57123 * @return {Number} this.width
57125 getWidth : function(){
57130 * @return {Number} this.height
57132 getHeight : function(){
57133 return this.height;
57136 getSignature : function(){
57137 return this.signatureTmp;
57140 reset : function(){
57141 this.signatureTmp = '';
57142 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57143 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
57144 this.isConfirmed = false;
57145 Roo.form.Signature.superclass.reset.call(this);
57147 setSignature : function(s){
57148 this.signatureTmp = s;
57149 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57150 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
57152 this.isConfirmed = false;
57153 Roo.form.Signature.superclass.reset.call(this);
57156 // Roo.log(this.signPanel.dom.contentWindow.up())
57159 setConfirmed : function(){
57163 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
57166 confirmHandler : function(){
57167 if(!this.getSignature()){
57171 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
57172 this.setValue(this.getSignature());
57173 this.isConfirmed = true;
57175 this.fireEvent('confirm', this);
57178 // Subclasses should provide the validation implementation by overriding this
57179 validateValue : function(value){
57180 if(this.allowBlank){
57184 if(this.isConfirmed){
57191 * Ext JS Library 1.1.1
57192 * Copyright(c) 2006-2007, Ext JS, LLC.
57194 * Originally Released Under LGPL - original licence link has changed is not relivant.
57197 * <script type="text/javascript">
57202 * @class Roo.form.ComboBox
57203 * @extends Roo.form.TriggerField
57204 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57206 * Create a new ComboBox.
57207 * @param {Object} config Configuration options
57209 Roo.form.Select = function(config){
57210 Roo.form.Select.superclass.constructor.call(this, config);
57214 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57216 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57219 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57220 * rendering into an Roo.Editor, defaults to false)
57223 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57224 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57227 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57230 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57231 * the dropdown list (defaults to undefined, with no header element)
57235 * @cfg {String/Roo.Template} tpl The template to use to render the output
57239 defaultAutoCreate : {tag: "select" },
57241 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57243 listWidth: undefined,
57245 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57246 * mode = 'remote' or 'text' if mode = 'local')
57248 displayField: undefined,
57250 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57251 * mode = 'remote' or 'value' if mode = 'local').
57252 * Note: use of a valueField requires the user make a selection
57253 * in order for a value to be mapped.
57255 valueField: undefined,
57259 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57260 * field's data value (defaults to the underlying DOM element's name)
57262 hiddenName: undefined,
57264 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57268 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57270 selectedClass: 'x-combo-selected',
57272 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
57273 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57274 * which displays a downward arrow icon).
57276 triggerClass : 'x-form-arrow-trigger',
57278 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57282 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57283 * anchor positions (defaults to 'tl-bl')
57285 listAlign: 'tl-bl?',
57287 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57291 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
57292 * query specified by the allQuery config option (defaults to 'query')
57294 triggerAction: 'query',
57296 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57297 * (defaults to 4, does not apply if editable = false)
57301 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57302 * delay (typeAheadDelay) if it matches a known value (defaults to false)
57306 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57307 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57311 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57312 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
57316 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
57317 * when editable = true (defaults to false)
57319 selectOnFocus:false,
57321 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57323 queryParam: 'query',
57325 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
57326 * when mode = 'remote' (defaults to 'Loading...')
57328 loadingText: 'Loading...',
57330 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57334 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57338 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57339 * traditional select (defaults to true)
57343 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57347 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57351 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57352 * listWidth has a higher value)
57356 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57357 * allow the user to set arbitrary text into the field (defaults to false)
57359 forceSelection:false,
57361 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57362 * if typeAhead = true (defaults to 250)
57364 typeAheadDelay : 250,
57366 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57367 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57369 valueNotFoundText : undefined,
57372 * @cfg {String} defaultValue The value displayed after loading the store.
57377 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57379 blockFocus : false,
57382 * @cfg {Boolean} disableClear Disable showing of clear button.
57384 disableClear : false,
57386 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
57388 alwaysQuery : false,
57394 // element that contains real text value.. (when hidden is used..)
57397 onRender : function(ct, position){
57398 Roo.form.Field.prototype.onRender.call(this, ct, position);
57401 this.store.on('beforeload', this.onBeforeLoad, this);
57402 this.store.on('load', this.onLoad, this);
57403 this.store.on('loadexception', this.onLoadException, this);
57404 this.store.load({});
57412 initEvents : function(){
57413 //Roo.form.ComboBox.superclass.initEvents.call(this);
57417 onDestroy : function(){
57420 this.store.un('beforeload', this.onBeforeLoad, this);
57421 this.store.un('load', this.onLoad, this);
57422 this.store.un('loadexception', this.onLoadException, this);
57424 //Roo.form.ComboBox.superclass.onDestroy.call(this);
57428 fireKey : function(e){
57429 if(e.isNavKeyPress() && !this.list.isVisible()){
57430 this.fireEvent("specialkey", this, e);
57435 onResize: function(w, h){
57443 * Allow or prevent the user from directly editing the field text. If false is passed,
57444 * the user will only be able to select from the items defined in the dropdown list. This method
57445 * is the runtime equivalent of setting the 'editable' config option at config time.
57446 * @param {Boolean} value True to allow the user to directly edit the field text
57448 setEditable : function(value){
57453 onBeforeLoad : function(){
57455 Roo.log("Select before load");
57458 this.innerList.update(this.loadingText ?
57459 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57460 //this.restrictHeight();
57461 this.selectedIndex = -1;
57465 onLoad : function(){
57468 var dom = this.el.dom;
57469 dom.innerHTML = '';
57470 var od = dom.ownerDocument;
57472 if (this.emptyText) {
57473 var op = od.createElement('option');
57474 op.setAttribute('value', '');
57475 op.innerHTML = String.format('{0}', this.emptyText);
57476 dom.appendChild(op);
57478 if(this.store.getCount() > 0){
57480 var vf = this.valueField;
57481 var df = this.displayField;
57482 this.store.data.each(function(r) {
57483 // which colmsn to use... testing - cdoe / title..
57484 var op = od.createElement('option');
57485 op.setAttribute('value', r.data[vf]);
57486 op.innerHTML = String.format('{0}', r.data[df]);
57487 dom.appendChild(op);
57489 if (typeof(this.defaultValue != 'undefined')) {
57490 this.setValue(this.defaultValue);
57495 //this.onEmptyResults();
57500 onLoadException : function()
57502 dom.innerHTML = '';
57504 Roo.log("Select on load exception");
57508 Roo.log(this.store.reader.jsonData);
57509 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57510 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57516 onTypeAhead : function(){
57521 onSelect : function(record, index){
57522 Roo.log('on select?');
57524 if(this.fireEvent('beforeselect', this, record, index) !== false){
57525 this.setFromData(index > -1 ? record.data : false);
57527 this.fireEvent('select', this, record, index);
57532 * Returns the currently selected field value or empty string if no value is set.
57533 * @return {String} value The selected value
57535 getValue : function(){
57536 var dom = this.el.dom;
57537 this.value = dom.options[dom.selectedIndex].value;
57543 * Clears any text/value currently set in the field
57545 clearValue : function(){
57547 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57552 * Sets the specified value into the field. If the value finds a match, the corresponding record text
57553 * will be displayed in the field. If the value does not match the data value of an existing item,
57554 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57555 * Otherwise the field will be blank (although the value will still be set).
57556 * @param {String} value The value to match
57558 setValue : function(v){
57559 var d = this.el.dom;
57560 for (var i =0; i < d.options.length;i++) {
57561 if (v == d.options[i].value) {
57562 d.selectedIndex = i;
57570 * @property {Object} the last set data for the element
57575 * Sets the value of the field based on a object which is related to the record format for the store.
57576 * @param {Object} value the value to set as. or false on reset?
57578 setFromData : function(o){
57579 Roo.log('setfrom data?');
57585 reset : function(){
57589 findRecord : function(prop, value){
57594 if(this.store.getCount() > 0){
57595 this.store.each(function(r){
57596 if(r.data[prop] == value){
57606 getName: function()
57608 // returns hidden if it's set..
57609 if (!this.rendered) {return ''};
57610 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
57618 onEmptyResults : function(){
57619 Roo.log('empty results');
57624 * Returns true if the dropdown list is expanded, else false.
57626 isExpanded : function(){
57631 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57632 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57633 * @param {String} value The data value of the item to select
57634 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57635 * selected item if it is not currently in view (defaults to true)
57636 * @return {Boolean} True if the value matched an item in the list, else false
57638 selectByValue : function(v, scrollIntoView){
57639 Roo.log('select By Value');
57642 if(v !== undefined && v !== null){
57643 var r = this.findRecord(this.valueField || this.displayField, v);
57645 this.select(this.store.indexOf(r), scrollIntoView);
57653 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57654 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57655 * @param {Number} index The zero-based index of the list item to select
57656 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57657 * selected item if it is not currently in view (defaults to true)
57659 select : function(index, scrollIntoView){
57660 Roo.log('select ');
57663 this.selectedIndex = index;
57664 this.view.select(index);
57665 if(scrollIntoView !== false){
57666 var el = this.view.getNode(index);
57668 this.innerList.scrollChildIntoView(el, false);
57676 validateBlur : function(){
57683 initQuery : function(){
57684 this.doQuery(this.getRawValue());
57688 doForce : function(){
57689 if(this.el.dom.value.length > 0){
57690 this.el.dom.value =
57691 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57697 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
57698 * query allowing the query action to be canceled if needed.
57699 * @param {String} query The SQL query to execute
57700 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57701 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
57702 * saved in the current store (defaults to false)
57704 doQuery : function(q, forceAll){
57706 Roo.log('doQuery?');
57707 if(q === undefined || q === null){
57712 forceAll: forceAll,
57716 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57720 forceAll = qe.forceAll;
57721 if(forceAll === true || (q.length >= this.minChars)){
57722 if(this.lastQuery != q || this.alwaysQuery){
57723 this.lastQuery = q;
57724 if(this.mode == 'local'){
57725 this.selectedIndex = -1;
57727 this.store.clearFilter();
57729 this.store.filter(this.displayField, q);
57733 this.store.baseParams[this.queryParam] = q;
57735 params: this.getParams(q)
57740 this.selectedIndex = -1;
57747 getParams : function(q){
57749 //p[this.queryParam] = q;
57752 p.limit = this.pageSize;
57758 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57760 collapse : function(){
57765 collapseIf : function(e){
57770 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57772 expand : function(){
57780 * @cfg {Boolean} grow
57784 * @cfg {Number} growMin
57788 * @cfg {Number} growMax
57796 setWidth : function()
57800 getResizeEl : function(){
57803 });//<script type="text/javasscript">
57807 * @class Roo.DDView
57808 * A DnD enabled version of Roo.View.
57809 * @param {Element/String} container The Element in which to create the View.
57810 * @param {String} tpl The template string used to create the markup for each element of the View
57811 * @param {Object} config The configuration properties. These include all the config options of
57812 * {@link Roo.View} plus some specific to this class.<br>
57814 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57815 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57817 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57818 .x-view-drag-insert-above {
57819 border-top:1px dotted #3366cc;
57821 .x-view-drag-insert-below {
57822 border-bottom:1px dotted #3366cc;
57828 Roo.DDView = function(container, tpl, config) {
57829 Roo.DDView.superclass.constructor.apply(this, arguments);
57830 this.getEl().setStyle("outline", "0px none");
57831 this.getEl().unselectable();
57832 if (this.dragGroup) {
57833 this.setDraggable(this.dragGroup.split(","));
57835 if (this.dropGroup) {
57836 this.setDroppable(this.dropGroup.split(","));
57838 if (this.deletable) {
57839 this.setDeletable();
57841 this.isDirtyFlag = false;
57847 Roo.extend(Roo.DDView, Roo.View, {
57848 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57849 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57850 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57851 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57855 reset: Roo.emptyFn,
57857 clearInvalid: Roo.form.Field.prototype.clearInvalid,
57859 validate: function() {
57863 destroy: function() {
57864 this.purgeListeners();
57865 this.getEl.removeAllListeners();
57866 this.getEl().remove();
57867 if (this.dragZone) {
57868 if (this.dragZone.destroy) {
57869 this.dragZone.destroy();
57872 if (this.dropZone) {
57873 if (this.dropZone.destroy) {
57874 this.dropZone.destroy();
57879 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57880 getName: function() {
57884 /** Loads the View from a JSON string representing the Records to put into the Store. */
57885 setValue: function(v) {
57887 throw "DDView.setValue(). DDView must be constructed with a valid Store";
57890 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57891 this.store.proxy = new Roo.data.MemoryProxy(data);
57895 /** @return {String} a parenthesised list of the ids of the Records in the View. */
57896 getValue: function() {
57898 this.store.each(function(rec) {
57899 result += rec.id + ',';
57901 return result.substr(0, result.length - 1) + ')';
57904 getIds: function() {
57905 var i = 0, result = new Array(this.store.getCount());
57906 this.store.each(function(rec) {
57907 result[i++] = rec.id;
57912 isDirty: function() {
57913 return this.isDirtyFlag;
57917 * Part of the Roo.dd.DropZone interface. If no target node is found, the
57918 * whole Element becomes the target, and this causes the drop gesture to append.
57920 getTargetFromEvent : function(e) {
57921 var target = e.getTarget();
57922 while ((target !== null) && (target.parentNode != this.el.dom)) {
57923 target = target.parentNode;
57926 target = this.el.dom.lastChild || this.el.dom;
57932 * Create the drag data which consists of an object which has the property "ddel" as
57933 * the drag proxy element.
57935 getDragData : function(e) {
57936 var target = this.findItemFromChild(e.getTarget());
57938 this.handleSelection(e);
57939 var selNodes = this.getSelectedNodes();
57942 copy: this.copy || (this.allowCopy && e.ctrlKey),
57946 var selectedIndices = this.getSelectedIndexes();
57947 for (var i = 0; i < selectedIndices.length; i++) {
57948 dragData.records.push(this.store.getAt(selectedIndices[i]));
57950 if (selNodes.length == 1) {
57951 dragData.ddel = target.cloneNode(true); // the div element
57953 var div = document.createElement('div'); // create the multi element drag "ghost"
57954 div.className = 'multi-proxy';
57955 for (var i = 0, len = selNodes.length; i < len; i++) {
57956 div.appendChild(selNodes[i].cloneNode(true));
57958 dragData.ddel = div;
57960 //console.log(dragData)
57961 //console.log(dragData.ddel.innerHTML)
57964 //console.log('nodragData')
57968 /** Specify to which ddGroup items in this DDView may be dragged. */
57969 setDraggable: function(ddGroup) {
57970 if (ddGroup instanceof Array) {
57971 Roo.each(ddGroup, this.setDraggable, this);
57974 if (this.dragZone) {
57975 this.dragZone.addToGroup(ddGroup);
57977 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
57978 containerScroll: true,
57982 // Draggability implies selection. DragZone's mousedown selects the element.
57983 if (!this.multiSelect) { this.singleSelect = true; }
57985 // Wire the DragZone's handlers up to methods in *this*
57986 this.dragZone.getDragData = this.getDragData.createDelegate(this);
57990 /** Specify from which ddGroup this DDView accepts drops. */
57991 setDroppable: function(ddGroup) {
57992 if (ddGroup instanceof Array) {
57993 Roo.each(ddGroup, this.setDroppable, this);
57996 if (this.dropZone) {
57997 this.dropZone.addToGroup(ddGroup);
57999 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
58000 containerScroll: true,
58004 // Wire the DropZone's handlers up to methods in *this*
58005 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
58006 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
58007 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
58008 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
58009 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
58013 /** Decide whether to drop above or below a View node. */
58014 getDropPoint : function(e, n, dd){
58015 if (n == this.el.dom) { return "above"; }
58016 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
58017 var c = t + (b - t) / 2;
58018 var y = Roo.lib.Event.getPageY(e);
58026 onNodeEnter : function(n, dd, e, data){
58030 onNodeOver : function(n, dd, e, data){
58031 var pt = this.getDropPoint(e, n, dd);
58032 // set the insert point style on the target node
58033 var dragElClass = this.dropNotAllowed;
58036 if (pt == "above"){
58037 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
58038 targetElClass = "x-view-drag-insert-above";
58040 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
58041 targetElClass = "x-view-drag-insert-below";
58043 if (this.lastInsertClass != targetElClass){
58044 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
58045 this.lastInsertClass = targetElClass;
58048 return dragElClass;
58051 onNodeOut : function(n, dd, e, data){
58052 this.removeDropIndicators(n);
58055 onNodeDrop : function(n, dd, e, data){
58056 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
58059 var pt = this.getDropPoint(e, n, dd);
58060 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
58061 if (pt == "below") { insertAt++; }
58062 for (var i = 0; i < data.records.length; i++) {
58063 var r = data.records[i];
58064 var dup = this.store.getById(r.id);
58065 if (dup && (dd != this.dragZone)) {
58066 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
58069 this.store.insert(insertAt++, r.copy());
58071 data.source.isDirtyFlag = true;
58073 this.store.insert(insertAt++, r);
58075 this.isDirtyFlag = true;
58078 this.dragZone.cachedTarget = null;
58082 removeDropIndicators : function(n){
58084 Roo.fly(n).removeClass([
58085 "x-view-drag-insert-above",
58086 "x-view-drag-insert-below"]);
58087 this.lastInsertClass = "_noclass";
58092 * Utility method. Add a delete option to the DDView's context menu.
58093 * @param {String} imageUrl The URL of the "delete" icon image.
58095 setDeletable: function(imageUrl) {
58096 if (!this.singleSelect && !this.multiSelect) {
58097 this.singleSelect = true;
58099 var c = this.getContextMenu();
58100 this.contextMenu.on("itemclick", function(item) {
58103 this.remove(this.getSelectedIndexes());
58107 this.contextMenu.add({
58114 /** Return the context menu for this DDView. */
58115 getContextMenu: function() {
58116 if (!this.contextMenu) {
58117 // Create the View's context menu
58118 this.contextMenu = new Roo.menu.Menu({
58119 id: this.id + "-contextmenu"
58121 this.el.on("contextmenu", this.showContextMenu, this);
58123 return this.contextMenu;
58126 disableContextMenu: function() {
58127 if (this.contextMenu) {
58128 this.el.un("contextmenu", this.showContextMenu, this);
58132 showContextMenu: function(e, item) {
58133 item = this.findItemFromChild(e.getTarget());
58136 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
58137 this.contextMenu.showAt(e.getXY());
58142 * Remove {@link Roo.data.Record}s at the specified indices.
58143 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
58145 remove: function(selectedIndices) {
58146 selectedIndices = [].concat(selectedIndices);
58147 for (var i = 0; i < selectedIndices.length; i++) {
58148 var rec = this.store.getAt(selectedIndices[i]);
58149 this.store.remove(rec);
58154 * Double click fires the event, but also, if this is draggable, and there is only one other
58155 * related DropZone, it transfers the selected node.
58157 onDblClick : function(e){
58158 var item = this.findItemFromChild(e.getTarget());
58160 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
58163 if (this.dragGroup) {
58164 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
58165 while (targets.indexOf(this.dropZone) > -1) {
58166 targets.remove(this.dropZone);
58168 if (targets.length == 1) {
58169 this.dragZone.cachedTarget = null;
58170 var el = Roo.get(targets[0].getEl());
58171 var box = el.getBox(true);
58172 targets[0].onNodeDrop(el.dom, {
58174 xy: [box.x, box.y + box.height - 1]
58175 }, null, this.getDragData(e));
58181 handleSelection: function(e) {
58182 this.dragZone.cachedTarget = null;
58183 var item = this.findItemFromChild(e.getTarget());
58185 this.clearSelections(true);
58188 if (item && (this.multiSelect || this.singleSelect)){
58189 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
58190 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58191 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58192 this.unselect(item);
58194 this.select(item, this.multiSelect && e.ctrlKey);
58195 this.lastSelection = item;
58200 onItemClick : function(item, index, e){
58201 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58207 unselect : function(nodeInfo, suppressEvent){
58208 var node = this.getNode(nodeInfo);
58209 if(node && this.isSelected(node)){
58210 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58211 Roo.fly(node).removeClass(this.selectedClass);
58212 this.selections.remove(node);
58213 if(!suppressEvent){
58214 this.fireEvent("selectionchange", this, this.selections);
58222 * Ext JS Library 1.1.1
58223 * Copyright(c) 2006-2007, Ext JS, LLC.
58225 * Originally Released Under LGPL - original licence link has changed is not relivant.
58228 * <script type="text/javascript">
58232 * @class Roo.LayoutManager
58233 * @extends Roo.util.Observable
58234 * Base class for layout managers.
58236 Roo.LayoutManager = function(container, config){
58237 Roo.LayoutManager.superclass.constructor.call(this);
58238 this.el = Roo.get(container);
58239 // ie scrollbar fix
58240 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58241 document.body.scroll = "no";
58242 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58243 this.el.position('relative');
58245 this.id = this.el.id;
58246 this.el.addClass("x-layout-container");
58247 /** false to disable window resize monitoring @type Boolean */
58248 this.monitorWindowResize = true;
58253 * Fires when a layout is performed.
58254 * @param {Roo.LayoutManager} this
58258 * @event regionresized
58259 * Fires when the user resizes a region.
58260 * @param {Roo.LayoutRegion} region The resized region
58261 * @param {Number} newSize The new size (width for east/west, height for north/south)
58263 "regionresized" : true,
58265 * @event regioncollapsed
58266 * Fires when a region is collapsed.
58267 * @param {Roo.LayoutRegion} region The collapsed region
58269 "regioncollapsed" : true,
58271 * @event regionexpanded
58272 * Fires when a region is expanded.
58273 * @param {Roo.LayoutRegion} region The expanded region
58275 "regionexpanded" : true
58277 this.updating = false;
58278 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58281 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58283 * Returns true if this layout is currently being updated
58284 * @return {Boolean}
58286 isUpdating : function(){
58287 return this.updating;
58291 * Suspend the LayoutManager from doing auto-layouts while
58292 * making multiple add or remove calls
58294 beginUpdate : function(){
58295 this.updating = true;
58299 * Restore auto-layouts and optionally disable the manager from performing a layout
58300 * @param {Boolean} noLayout true to disable a layout update
58302 endUpdate : function(noLayout){
58303 this.updating = false;
58309 layout: function(){
58313 onRegionResized : function(region, newSize){
58314 this.fireEvent("regionresized", region, newSize);
58318 onRegionCollapsed : function(region){
58319 this.fireEvent("regioncollapsed", region);
58322 onRegionExpanded : function(region){
58323 this.fireEvent("regionexpanded", region);
58327 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58328 * performs box-model adjustments.
58329 * @return {Object} The size as an object {width: (the width), height: (the height)}
58331 getViewSize : function(){
58333 if(this.el.dom != document.body){
58334 size = this.el.getSize();
58336 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58338 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58339 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58344 * Returns the Element this layout is bound to.
58345 * @return {Roo.Element}
58347 getEl : function(){
58352 * Returns the specified region.
58353 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58354 * @return {Roo.LayoutRegion}
58356 getRegion : function(target){
58357 return this.regions[target.toLowerCase()];
58360 onWindowResize : function(){
58361 if(this.monitorWindowResize){
58367 * Ext JS Library 1.1.1
58368 * Copyright(c) 2006-2007, Ext JS, LLC.
58370 * Originally Released Under LGPL - original licence link has changed is not relivant.
58373 * <script type="text/javascript">
58376 * @class Roo.BorderLayout
58377 * @extends Roo.LayoutManager
58378 * @children Roo.ContentPanel
58379 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58380 * please see: <br><br>
58381 * <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>
58382 * <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>
58385 var layout = new Roo.BorderLayout(document.body, {
58419 preferredTabWidth: 150
58424 var CP = Roo.ContentPanel;
58426 layout.beginUpdate();
58427 layout.add("north", new CP("north", "North"));
58428 layout.add("south", new CP("south", {title: "South", closable: true}));
58429 layout.add("west", new CP("west", {title: "West"}));
58430 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58431 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58432 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58433 layout.getRegion("center").showPanel("center1");
58434 layout.endUpdate();
58437 <b>The container the layout is rendered into can be either the body element or any other element.
58438 If it is not the body element, the container needs to either be an absolute positioned element,
58439 or you will need to add "position:relative" to the css of the container. You will also need to specify
58440 the container size if it is not the body element.</b>
58443 * Create a new BorderLayout
58444 * @param {String/HTMLElement/Element} container The container this layout is bound to
58445 * @param {Object} config Configuration options
58447 Roo.BorderLayout = function(container, config){
58448 config = config || {};
58449 Roo.BorderLayout.superclass.constructor.call(this, container, config);
58450 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58451 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58452 var target = this.factory.validRegions[i];
58453 if(config[target]){
58454 this.addRegion(target, config[target]);
58459 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58462 * @cfg {Roo.LayoutRegion} east
58465 * @cfg {Roo.LayoutRegion} west
58468 * @cfg {Roo.LayoutRegion} north
58471 * @cfg {Roo.LayoutRegion} south
58474 * @cfg {Roo.LayoutRegion} center
58477 * Creates and adds a new region if it doesn't already exist.
58478 * @param {String} target The target region key (north, south, east, west or center).
58479 * @param {Object} config The regions config object
58480 * @return {BorderLayoutRegion} The new region
58482 addRegion : function(target, config){
58483 if(!this.regions[target]){
58484 var r = this.factory.create(target, this, config);
58485 this.bindRegion(target, r);
58487 return this.regions[target];
58491 bindRegion : function(name, r){
58492 this.regions[name] = r;
58493 r.on("visibilitychange", this.layout, this);
58494 r.on("paneladded", this.layout, this);
58495 r.on("panelremoved", this.layout, this);
58496 r.on("invalidated", this.layout, this);
58497 r.on("resized", this.onRegionResized, this);
58498 r.on("collapsed", this.onRegionCollapsed, this);
58499 r.on("expanded", this.onRegionExpanded, this);
58503 * Performs a layout update.
58505 layout : function(){
58506 if(this.updating) {
58509 var size = this.getViewSize();
58510 var w = size.width;
58511 var h = size.height;
58516 //var x = 0, y = 0;
58518 var rs = this.regions;
58519 var north = rs["north"];
58520 var south = rs["south"];
58521 var west = rs["west"];
58522 var east = rs["east"];
58523 var center = rs["center"];
58524 //if(this.hideOnLayout){ // not supported anymore
58525 //c.el.setStyle("display", "none");
58527 if(north && north.isVisible()){
58528 var b = north.getBox();
58529 var m = north.getMargins();
58530 b.width = w - (m.left+m.right);
58533 centerY = b.height + b.y + m.bottom;
58534 centerH -= centerY;
58535 north.updateBox(this.safeBox(b));
58537 if(south && south.isVisible()){
58538 var b = south.getBox();
58539 var m = south.getMargins();
58540 b.width = w - (m.left+m.right);
58542 var totalHeight = (b.height + m.top + m.bottom);
58543 b.y = h - totalHeight + m.top;
58544 centerH -= totalHeight;
58545 south.updateBox(this.safeBox(b));
58547 if(west && west.isVisible()){
58548 var b = west.getBox();
58549 var m = west.getMargins();
58550 b.height = centerH - (m.top+m.bottom);
58552 b.y = centerY + m.top;
58553 var totalWidth = (b.width + m.left + m.right);
58554 centerX += totalWidth;
58555 centerW -= totalWidth;
58556 west.updateBox(this.safeBox(b));
58558 if(east && east.isVisible()){
58559 var b = east.getBox();
58560 var m = east.getMargins();
58561 b.height = centerH - (m.top+m.bottom);
58562 var totalWidth = (b.width + m.left + m.right);
58563 b.x = w - totalWidth + m.left;
58564 b.y = centerY + m.top;
58565 centerW -= totalWidth;
58566 east.updateBox(this.safeBox(b));
58569 var m = center.getMargins();
58571 x: centerX + m.left,
58572 y: centerY + m.top,
58573 width: centerW - (m.left+m.right),
58574 height: centerH - (m.top+m.bottom)
58576 //if(this.hideOnLayout){
58577 //center.el.setStyle("display", "block");
58579 center.updateBox(this.safeBox(centerBox));
58582 this.fireEvent("layout", this);
58586 safeBox : function(box){
58587 box.width = Math.max(0, box.width);
58588 box.height = Math.max(0, box.height);
58593 * Adds a ContentPanel (or subclass) to this layout.
58594 * @param {String} target The target region key (north, south, east, west or center).
58595 * @param {Roo.ContentPanel} panel The panel to add
58596 * @return {Roo.ContentPanel} The added panel
58598 add : function(target, panel){
58600 target = target.toLowerCase();
58601 return this.regions[target].add(panel);
58605 * Remove a ContentPanel (or subclass) to this layout.
58606 * @param {String} target The target region key (north, south, east, west or center).
58607 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58608 * @return {Roo.ContentPanel} The removed panel
58610 remove : function(target, panel){
58611 target = target.toLowerCase();
58612 return this.regions[target].remove(panel);
58616 * Searches all regions for a panel with the specified id
58617 * @param {String} panelId
58618 * @return {Roo.ContentPanel} The panel or null if it wasn't found
58620 findPanel : function(panelId){
58621 var rs = this.regions;
58622 for(var target in rs){
58623 if(typeof rs[target] != "function"){
58624 var p = rs[target].getPanel(panelId);
58634 * Searches all regions for a panel with the specified id and activates (shows) it.
58635 * @param {String/ContentPanel} panelId The panels id or the panel itself
58636 * @return {Roo.ContentPanel} The shown panel or null
58638 showPanel : function(panelId) {
58639 var rs = this.regions;
58640 for(var target in rs){
58641 var r = rs[target];
58642 if(typeof r != "function"){
58643 if(r.hasPanel(panelId)){
58644 return r.showPanel(panelId);
58652 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58653 * @param {Roo.state.Provider} provider (optional) An alternate state provider
58655 restoreState : function(provider){
58657 provider = Roo.state.Manager;
58659 var sm = new Roo.LayoutStateManager();
58660 sm.init(this, provider);
58664 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
58665 * object should contain properties for each region to add ContentPanels to, and each property's value should be
58666 * a valid ContentPanel config object. Example:
58668 // Create the main layout
58669 var layout = new Roo.BorderLayout('main-ct', {
58680 // Create and add multiple ContentPanels at once via configs
58683 id: 'source-files',
58685 title:'Ext Source Files',
58698 * @param {Object} regions An object containing ContentPanel configs by region name
58700 batchAdd : function(regions){
58701 this.beginUpdate();
58702 for(var rname in regions){
58703 var lr = this.regions[rname];
58705 this.addTypedPanels(lr, regions[rname]);
58712 addTypedPanels : function(lr, ps){
58713 if(typeof ps == 'string'){
58714 lr.add(new Roo.ContentPanel(ps));
58716 else if(ps instanceof Array){
58717 for(var i =0, len = ps.length; i < len; i++){
58718 this.addTypedPanels(lr, ps[i]);
58721 else if(!ps.events){ // raw config?
58723 delete ps.el; // prevent conflict
58724 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58726 else { // panel object assumed!
58731 * Adds a xtype elements to the layout.
58735 xtype : 'ContentPanel',
58742 xtype : 'NestedLayoutPanel',
58748 items : [ ... list of content panels or nested layout panels.. ]
58752 * @param {Object} cfg Xtype definition of item to add.
58754 addxtype : function(cfg)
58756 // basically accepts a pannel...
58757 // can accept a layout region..!?!?
58758 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58760 if (!cfg.xtype.match(/Panel$/)) {
58765 if (typeof(cfg.region) == 'undefined') {
58766 Roo.log("Failed to add Panel, region was not set");
58770 var region = cfg.region;
58776 xitems = cfg.items;
58783 case 'ContentPanel': // ContentPanel (el, cfg)
58784 case 'ScrollPanel': // ContentPanel (el, cfg)
58786 if(cfg.autoCreate) {
58787 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58789 var el = this.el.createChild();
58790 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58793 this.add(region, ret);
58797 case 'TreePanel': // our new panel!
58798 cfg.el = this.el.createChild();
58799 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58800 this.add(region, ret);
58803 case 'NestedLayoutPanel':
58804 // create a new Layout (which is a Border Layout...
58805 var el = this.el.createChild();
58806 var clayout = cfg.layout;
58808 clayout.items = clayout.items || [];
58809 // replace this exitems with the clayout ones..
58810 xitems = clayout.items;
58813 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58814 cfg.background = false;
58816 var layout = new Roo.BorderLayout(el, clayout);
58818 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58819 //console.log('adding nested layout panel ' + cfg.toSource());
58820 this.add(region, ret);
58821 nb = {}; /// find first...
58826 // needs grid and region
58828 //var el = this.getRegion(region).el.createChild();
58829 var el = this.el.createChild();
58830 // create the grid first...
58832 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58834 if (region == 'center' && this.active ) {
58835 cfg.background = false;
58837 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58839 this.add(region, ret);
58840 if (cfg.background) {
58841 ret.on('activate', function(gp) {
58842 if (!gp.grid.rendered) {
58857 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58859 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58860 this.add(region, ret);
58863 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58867 // GridPanel (grid, cfg)
58870 this.beginUpdate();
58874 Roo.each(xitems, function(i) {
58875 region = nb && i.region ? i.region : false;
58877 var add = ret.addxtype(i);
58880 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58881 if (!i.background) {
58882 abn[region] = nb[region] ;
58889 // make the last non-background panel active..
58890 //if (nb) { Roo.log(abn); }
58893 for(var r in abn) {
58894 region = this.getRegion(r);
58896 // tried using nb[r], but it does not work..
58898 region.showPanel(abn[r]);
58909 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58910 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
58911 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58912 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
58915 var CP = Roo.ContentPanel;
58917 var layout = Roo.BorderLayout.create({
58921 panels: [new CP("north", "North")]
58930 panels: [new CP("west", {title: "West"})]
58939 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
58948 panels: [new CP("south", {title: "South", closable: true})]
58955 preferredTabWidth: 150,
58957 new CP("center1", {title: "Close Me", closable: true}),
58958 new CP("center2", {title: "Center Panel", closable: false})
58963 layout.getRegion("center").showPanel("center1");
58968 Roo.BorderLayout.create = function(config, targetEl){
58969 var layout = new Roo.BorderLayout(targetEl || document.body, config);
58970 layout.beginUpdate();
58971 var regions = Roo.BorderLayout.RegionFactory.validRegions;
58972 for(var j = 0, jlen = regions.length; j < jlen; j++){
58973 var lr = regions[j];
58974 if(layout.regions[lr] && config[lr].panels){
58975 var r = layout.regions[lr];
58976 var ps = config[lr].panels;
58977 layout.addTypedPanels(r, ps);
58980 layout.endUpdate();
58985 Roo.BorderLayout.RegionFactory = {
58987 validRegions : ["north","south","east","west","center"],
58990 create : function(target, mgr, config){
58991 target = target.toLowerCase();
58992 if(config.lightweight || config.basic){
58993 return new Roo.BasicLayoutRegion(mgr, config, target);
58997 return new Roo.NorthLayoutRegion(mgr, config);
58999 return new Roo.SouthLayoutRegion(mgr, config);
59001 return new Roo.EastLayoutRegion(mgr, config);
59003 return new Roo.WestLayoutRegion(mgr, config);
59005 return new Roo.CenterLayoutRegion(mgr, config);
59007 throw 'Layout region "'+target+'" not supported.';
59011 * Ext JS Library 1.1.1
59012 * Copyright(c) 2006-2007, Ext JS, LLC.
59014 * Originally Released Under LGPL - original licence link has changed is not relivant.
59017 * <script type="text/javascript">
59021 * @class Roo.BasicLayoutRegion
59022 * @extends Roo.util.Observable
59023 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
59024 * and does not have a titlebar, tabs or any other features. All it does is size and position
59025 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
59027 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
59029 this.position = pos;
59032 * @scope Roo.BasicLayoutRegion
59036 * @event beforeremove
59037 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
59038 * @param {Roo.LayoutRegion} this
59039 * @param {Roo.ContentPanel} panel The panel
59040 * @param {Object} e The cancel event object
59042 "beforeremove" : true,
59044 * @event invalidated
59045 * Fires when the layout for this region is changed.
59046 * @param {Roo.LayoutRegion} this
59048 "invalidated" : true,
59050 * @event visibilitychange
59051 * Fires when this region is shown or hidden
59052 * @param {Roo.LayoutRegion} this
59053 * @param {Boolean} visibility true or false
59055 "visibilitychange" : true,
59057 * @event paneladded
59058 * Fires when a panel is added.
59059 * @param {Roo.LayoutRegion} this
59060 * @param {Roo.ContentPanel} panel The panel
59062 "paneladded" : true,
59064 * @event panelremoved
59065 * Fires when a panel is removed.
59066 * @param {Roo.LayoutRegion} this
59067 * @param {Roo.ContentPanel} panel The panel
59069 "panelremoved" : true,
59071 * @event beforecollapse
59072 * Fires when this region before collapse.
59073 * @param {Roo.LayoutRegion} this
59075 "beforecollapse" : true,
59078 * Fires when this region is collapsed.
59079 * @param {Roo.LayoutRegion} this
59081 "collapsed" : true,
59084 * Fires when this region is expanded.
59085 * @param {Roo.LayoutRegion} this
59090 * Fires when this region is slid into view.
59091 * @param {Roo.LayoutRegion} this
59093 "slideshow" : true,
59096 * Fires when this region slides out of view.
59097 * @param {Roo.LayoutRegion} this
59099 "slidehide" : true,
59101 * @event panelactivated
59102 * Fires when a panel is activated.
59103 * @param {Roo.LayoutRegion} this
59104 * @param {Roo.ContentPanel} panel The activated panel
59106 "panelactivated" : true,
59109 * Fires when the user resizes this region.
59110 * @param {Roo.LayoutRegion} this
59111 * @param {Number} newSize The new size (width for east/west, height for north/south)
59115 /** A collection of panels in this region. @type Roo.util.MixedCollection */
59116 this.panels = new Roo.util.MixedCollection();
59117 this.panels.getKey = this.getPanelId.createDelegate(this);
59119 this.activePanel = null;
59120 // ensure listeners are added...
59122 if (config.listeners || config.events) {
59123 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
59124 listeners : config.listeners || {},
59125 events : config.events || {}
59129 if(skipConfig !== true){
59130 this.applyConfig(config);
59134 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
59135 getPanelId : function(p){
59139 applyConfig : function(config){
59140 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59141 this.config = config;
59146 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
59147 * the width, for horizontal (north, south) the height.
59148 * @param {Number} newSize The new width or height
59150 resizeTo : function(newSize){
59151 var el = this.el ? this.el :
59152 (this.activePanel ? this.activePanel.getEl() : null);
59154 switch(this.position){
59157 el.setWidth(newSize);
59158 this.fireEvent("resized", this, newSize);
59162 el.setHeight(newSize);
59163 this.fireEvent("resized", this, newSize);
59169 getBox : function(){
59170 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
59173 getMargins : function(){
59174 return this.margins;
59177 updateBox : function(box){
59179 var el = this.activePanel.getEl();
59180 el.dom.style.left = box.x + "px";
59181 el.dom.style.top = box.y + "px";
59182 this.activePanel.setSize(box.width, box.height);
59186 * Returns the container element for this region.
59187 * @return {Roo.Element}
59189 getEl : function(){
59190 return this.activePanel;
59194 * Returns true if this region is currently visible.
59195 * @return {Boolean}
59197 isVisible : function(){
59198 return this.activePanel ? true : false;
59201 setActivePanel : function(panel){
59202 panel = this.getPanel(panel);
59203 if(this.activePanel && this.activePanel != panel){
59204 this.activePanel.setActiveState(false);
59205 this.activePanel.getEl().setLeftTop(-10000,-10000);
59207 this.activePanel = panel;
59208 panel.setActiveState(true);
59210 panel.setSize(this.box.width, this.box.height);
59212 this.fireEvent("panelactivated", this, panel);
59213 this.fireEvent("invalidated");
59217 * Show the specified panel.
59218 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59219 * @return {Roo.ContentPanel} The shown panel or null
59221 showPanel : function(panel){
59222 if(panel = this.getPanel(panel)){
59223 this.setActivePanel(panel);
59229 * Get the active panel for this region.
59230 * @return {Roo.ContentPanel} The active panel or null
59232 getActivePanel : function(){
59233 return this.activePanel;
59237 * Add the passed ContentPanel(s)
59238 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59239 * @return {Roo.ContentPanel} The panel added (if only one was added)
59241 add : function(panel){
59242 if(arguments.length > 1){
59243 for(var i = 0, len = arguments.length; i < len; i++) {
59244 this.add(arguments[i]);
59248 if(this.hasPanel(panel)){
59249 this.showPanel(panel);
59252 var el = panel.getEl();
59253 if(el.dom.parentNode != this.mgr.el.dom){
59254 this.mgr.el.dom.appendChild(el.dom);
59256 if(panel.setRegion){
59257 panel.setRegion(this);
59259 this.panels.add(panel);
59260 el.setStyle("position", "absolute");
59261 if(!panel.background){
59262 this.setActivePanel(panel);
59263 if(this.config.initialSize && this.panels.getCount()==1){
59264 this.resizeTo(this.config.initialSize);
59267 this.fireEvent("paneladded", this, panel);
59272 * Returns true if the panel is in this region.
59273 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59274 * @return {Boolean}
59276 hasPanel : function(panel){
59277 if(typeof panel == "object"){ // must be panel obj
59278 panel = panel.getId();
59280 return this.getPanel(panel) ? true : false;
59284 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59285 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59286 * @param {Boolean} preservePanel Overrides the config preservePanel option
59287 * @return {Roo.ContentPanel} The panel that was removed
59289 remove : function(panel, preservePanel){
59290 panel = this.getPanel(panel);
59295 this.fireEvent("beforeremove", this, panel, e);
59296 if(e.cancel === true){
59299 var panelId = panel.getId();
59300 this.panels.removeKey(panelId);
59305 * Returns the panel specified or null if it's not in this region.
59306 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59307 * @return {Roo.ContentPanel}
59309 getPanel : function(id){
59310 if(typeof id == "object"){ // must be panel obj
59313 return this.panels.get(id);
59317 * Returns this regions position (north/south/east/west/center).
59320 getPosition: function(){
59321 return this.position;
59325 * Ext JS Library 1.1.1
59326 * Copyright(c) 2006-2007, Ext JS, LLC.
59328 * Originally Released Under LGPL - original licence link has changed is not relivant.
59331 * <script type="text/javascript">
59335 * @class Roo.LayoutRegion
59336 * @extends Roo.BasicLayoutRegion
59337 * This class represents a region in a layout manager.
59338 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
59339 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
59340 * @cfg {Boolean} floatable False to disable floating (defaults to true)
59341 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59342 * @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})
59343 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
59344 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
59345 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
59346 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
59347 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
59348 * @cfg {String} title The title for the region (overrides panel titles)
59349 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
59350 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59351 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
59352 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59353 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
59354 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59355 * the space available, similar to FireFox 1.5 tabs (defaults to false)
59356 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
59357 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
59358 * @cfg {Boolean} showPin True to show a pin button
59359 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
59360 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
59361 * @cfg {Boolean} disableTabTips True to disable tab tooltips
59362 * @cfg {Number} width For East/West panels
59363 * @cfg {Number} height For North/South panels
59364 * @cfg {Boolean} split To show the splitter
59365 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
59367 Roo.LayoutRegion = function(mgr, config, pos){
59368 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59369 var dh = Roo.DomHelper;
59370 /** This region's container element
59371 * @type Roo.Element */
59372 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59373 /** This region's title element
59374 * @type Roo.Element */
59376 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59377 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
59378 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59380 this.titleEl.enableDisplayMode();
59381 /** This region's title text element
59382 * @type HTMLElement */
59383 this.titleTextEl = this.titleEl.dom.firstChild;
59384 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59385 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59386 this.closeBtn.enableDisplayMode();
59387 this.closeBtn.on("click", this.closeClicked, this);
59388 this.closeBtn.hide();
59390 this.createBody(config);
59391 this.visible = true;
59392 this.collapsed = false;
59394 if(config.hideWhenEmpty){
59396 this.on("paneladded", this.validateVisibility, this);
59397 this.on("panelremoved", this.validateVisibility, this);
59399 this.applyConfig(config);
59402 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59404 createBody : function(){
59405 /** This region's body element
59406 * @type Roo.Element */
59407 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59410 applyConfig : function(c){
59411 if(c.collapsible && this.position != "center" && !this.collapsedEl){
59412 var dh = Roo.DomHelper;
59413 if(c.titlebar !== false){
59414 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59415 this.collapseBtn.on("click", this.collapse, this);
59416 this.collapseBtn.enableDisplayMode();
59418 if(c.showPin === true || this.showPin){
59419 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59420 this.stickBtn.enableDisplayMode();
59421 this.stickBtn.on("click", this.expand, this);
59422 this.stickBtn.hide();
59425 /** This region's collapsed element
59426 * @type Roo.Element */
59427 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59428 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59430 if(c.floatable !== false){
59431 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59432 this.collapsedEl.on("click", this.collapseClick, this);
59435 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59436 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59437 id: "message", unselectable: "on", style:{"float":"left"}});
59438 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59440 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59441 this.expandBtn.on("click", this.expand, this);
59443 if(this.collapseBtn){
59444 this.collapseBtn.setVisible(c.collapsible == true);
59446 this.cmargins = c.cmargins || this.cmargins ||
59447 (this.position == "west" || this.position == "east" ?
59448 {top: 0, left: 2, right:2, bottom: 0} :
59449 {top: 2, left: 0, right:0, bottom: 2});
59450 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59451 this.bottomTabs = c.tabPosition != "top";
59452 this.autoScroll = c.autoScroll || false;
59453 if(this.autoScroll){
59454 this.bodyEl.setStyle("overflow", "auto");
59456 this.bodyEl.setStyle("overflow", "hidden");
59458 //if(c.titlebar !== false){
59459 if((!c.titlebar && !c.title) || c.titlebar === false){
59460 this.titleEl.hide();
59462 this.titleEl.show();
59464 this.titleTextEl.innerHTML = c.title;
59468 this.duration = c.duration || .30;
59469 this.slideDuration = c.slideDuration || .45;
59472 this.collapse(true);
59479 * Returns true if this region is currently visible.
59480 * @return {Boolean}
59482 isVisible : function(){
59483 return this.visible;
59487 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59488 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
59490 setCollapsedTitle : function(title){
59491 title = title || " ";
59492 if(this.collapsedTitleTextEl){
59493 this.collapsedTitleTextEl.innerHTML = title;
59497 getBox : function(){
59499 if(!this.collapsed){
59500 b = this.el.getBox(false, true);
59502 b = this.collapsedEl.getBox(false, true);
59507 getMargins : function(){
59508 return this.collapsed ? this.cmargins : this.margins;
59511 highlight : function(){
59512 this.el.addClass("x-layout-panel-dragover");
59515 unhighlight : function(){
59516 this.el.removeClass("x-layout-panel-dragover");
59519 updateBox : function(box){
59521 if(!this.collapsed){
59522 this.el.dom.style.left = box.x + "px";
59523 this.el.dom.style.top = box.y + "px";
59524 this.updateBody(box.width, box.height);
59526 this.collapsedEl.dom.style.left = box.x + "px";
59527 this.collapsedEl.dom.style.top = box.y + "px";
59528 this.collapsedEl.setSize(box.width, box.height);
59531 this.tabs.autoSizeTabs();
59535 updateBody : function(w, h){
59537 this.el.setWidth(w);
59538 w -= this.el.getBorderWidth("rl");
59539 if(this.config.adjustments){
59540 w += this.config.adjustments[0];
59544 this.el.setHeight(h);
59545 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59546 h -= this.el.getBorderWidth("tb");
59547 if(this.config.adjustments){
59548 h += this.config.adjustments[1];
59550 this.bodyEl.setHeight(h);
59552 h = this.tabs.syncHeight(h);
59555 if(this.panelSize){
59556 w = w !== null ? w : this.panelSize.width;
59557 h = h !== null ? h : this.panelSize.height;
59559 if(this.activePanel){
59560 var el = this.activePanel.getEl();
59561 w = w !== null ? w : el.getWidth();
59562 h = h !== null ? h : el.getHeight();
59563 this.panelSize = {width: w, height: h};
59564 this.activePanel.setSize(w, h);
59566 if(Roo.isIE && this.tabs){
59567 this.tabs.el.repaint();
59572 * Returns the container element for this region.
59573 * @return {Roo.Element}
59575 getEl : function(){
59580 * Hides this region.
59583 if(!this.collapsed){
59584 this.el.dom.style.left = "-2000px";
59587 this.collapsedEl.dom.style.left = "-2000px";
59588 this.collapsedEl.hide();
59590 this.visible = false;
59591 this.fireEvent("visibilitychange", this, false);
59595 * Shows this region if it was previously hidden.
59598 if(!this.collapsed){
59601 this.collapsedEl.show();
59603 this.visible = true;
59604 this.fireEvent("visibilitychange", this, true);
59607 closeClicked : function(){
59608 if(this.activePanel){
59609 this.remove(this.activePanel);
59613 collapseClick : function(e){
59615 e.stopPropagation();
59618 e.stopPropagation();
59624 * Collapses this region.
59625 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59627 collapse : function(skipAnim, skipCheck){
59628 if(this.collapsed) {
59632 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59634 this.collapsed = true;
59636 this.split.el.hide();
59638 if(this.config.animate && skipAnim !== true){
59639 this.fireEvent("invalidated", this);
59640 this.animateCollapse();
59642 this.el.setLocation(-20000,-20000);
59644 this.collapsedEl.show();
59645 this.fireEvent("collapsed", this);
59646 this.fireEvent("invalidated", this);
59652 animateCollapse : function(){
59657 * Expands this region if it was previously collapsed.
59658 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59659 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59661 expand : function(e, skipAnim){
59663 e.stopPropagation();
59665 if(!this.collapsed || this.el.hasActiveFx()) {
59669 this.afterSlideIn();
59672 this.collapsed = false;
59673 if(this.config.animate && skipAnim !== true){
59674 this.animateExpand();
59678 this.split.el.show();
59680 this.collapsedEl.setLocation(-2000,-2000);
59681 this.collapsedEl.hide();
59682 this.fireEvent("invalidated", this);
59683 this.fireEvent("expanded", this);
59687 animateExpand : function(){
59691 initTabs : function()
59693 this.bodyEl.setStyle("overflow", "hidden");
59694 var ts = new Roo.TabPanel(
59697 tabPosition: this.bottomTabs ? 'bottom' : 'top',
59698 disableTooltips: this.config.disableTabTips,
59699 toolbar : this.config.toolbar
59702 if(this.config.hideTabs){
59703 ts.stripWrap.setDisplayed(false);
59706 ts.resizeTabs = this.config.resizeTabs === true;
59707 ts.minTabWidth = this.config.minTabWidth || 40;
59708 ts.maxTabWidth = this.config.maxTabWidth || 250;
59709 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59710 ts.monitorResize = false;
59711 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59712 ts.bodyEl.addClass('x-layout-tabs-body');
59713 this.panels.each(this.initPanelAsTab, this);
59716 initPanelAsTab : function(panel){
59717 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59718 this.config.closeOnTab && panel.isClosable());
59719 if(panel.tabTip !== undefined){
59720 ti.setTooltip(panel.tabTip);
59722 ti.on("activate", function(){
59723 this.setActivePanel(panel);
59725 if(this.config.closeOnTab){
59726 ti.on("beforeclose", function(t, e){
59728 this.remove(panel);
59734 updatePanelTitle : function(panel, title){
59735 if(this.activePanel == panel){
59736 this.updateTitle(title);
59739 var ti = this.tabs.getTab(panel.getEl().id);
59741 if(panel.tabTip !== undefined){
59742 ti.setTooltip(panel.tabTip);
59747 updateTitle : function(title){
59748 if(this.titleTextEl && !this.config.title){
59749 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
59753 setActivePanel : function(panel){
59754 panel = this.getPanel(panel);
59755 if(this.activePanel && this.activePanel != panel){
59756 this.activePanel.setActiveState(false);
59758 this.activePanel = panel;
59759 panel.setActiveState(true);
59760 if(this.panelSize){
59761 panel.setSize(this.panelSize.width, this.panelSize.height);
59764 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59766 this.updateTitle(panel.getTitle());
59768 this.fireEvent("invalidated", this);
59770 this.fireEvent("panelactivated", this, panel);
59774 * Shows the specified panel.
59775 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59776 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59778 showPanel : function(panel)
59780 panel = this.getPanel(panel);
59783 var tab = this.tabs.getTab(panel.getEl().id);
59784 if(tab.isHidden()){
59785 this.tabs.unhideTab(tab.id);
59789 this.setActivePanel(panel);
59796 * Get the active panel for this region.
59797 * @return {Roo.ContentPanel} The active panel or null
59799 getActivePanel : function(){
59800 return this.activePanel;
59803 validateVisibility : function(){
59804 if(this.panels.getCount() < 1){
59805 this.updateTitle(" ");
59806 this.closeBtn.hide();
59809 if(!this.isVisible()){
59816 * Adds the passed ContentPanel(s) to this region.
59817 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59818 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59820 add : function(panel){
59821 if(arguments.length > 1){
59822 for(var i = 0, len = arguments.length; i < len; i++) {
59823 this.add(arguments[i]);
59827 if(this.hasPanel(panel)){
59828 this.showPanel(panel);
59831 panel.setRegion(this);
59832 this.panels.add(panel);
59833 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59834 this.bodyEl.dom.appendChild(panel.getEl().dom);
59835 if(panel.background !== true){
59836 this.setActivePanel(panel);
59838 this.fireEvent("paneladded", this, panel);
59844 this.initPanelAsTab(panel);
59846 if(panel.background !== true){
59847 this.tabs.activate(panel.getEl().id);
59849 this.fireEvent("paneladded", this, panel);
59854 * Hides the tab for the specified panel.
59855 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59857 hidePanel : function(panel){
59858 if(this.tabs && (panel = this.getPanel(panel))){
59859 this.tabs.hideTab(panel.getEl().id);
59864 * Unhides the tab for a previously hidden panel.
59865 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59867 unhidePanel : function(panel){
59868 if(this.tabs && (panel = this.getPanel(panel))){
59869 this.tabs.unhideTab(panel.getEl().id);
59873 clearPanels : function(){
59874 while(this.panels.getCount() > 0){
59875 this.remove(this.panels.first());
59880 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59881 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59882 * @param {Boolean} preservePanel Overrides the config preservePanel option
59883 * @return {Roo.ContentPanel} The panel that was removed
59885 remove : function(panel, preservePanel){
59886 panel = this.getPanel(panel);
59891 this.fireEvent("beforeremove", this, panel, e);
59892 if(e.cancel === true){
59895 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59896 var panelId = panel.getId();
59897 this.panels.removeKey(panelId);
59899 document.body.appendChild(panel.getEl().dom);
59902 this.tabs.removeTab(panel.getEl().id);
59903 }else if (!preservePanel){
59904 this.bodyEl.dom.removeChild(panel.getEl().dom);
59906 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59907 var p = this.panels.first();
59908 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59909 tempEl.appendChild(p.getEl().dom);
59910 this.bodyEl.update("");
59911 this.bodyEl.dom.appendChild(p.getEl().dom);
59913 this.updateTitle(p.getTitle());
59915 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59916 this.setActivePanel(p);
59918 panel.setRegion(null);
59919 if(this.activePanel == panel){
59920 this.activePanel = null;
59922 if(this.config.autoDestroy !== false && preservePanel !== true){
59923 try{panel.destroy();}catch(e){}
59925 this.fireEvent("panelremoved", this, panel);
59930 * Returns the TabPanel component used by this region
59931 * @return {Roo.TabPanel}
59933 getTabs : function(){
59937 createTool : function(parentEl, className){
59938 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
59939 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
59940 btn.addClassOnOver("x-layout-tools-button-over");
59945 * Ext JS Library 1.1.1
59946 * Copyright(c) 2006-2007, Ext JS, LLC.
59948 * Originally Released Under LGPL - original licence link has changed is not relivant.
59951 * <script type="text/javascript">
59957 * @class Roo.SplitLayoutRegion
59958 * @extends Roo.LayoutRegion
59959 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
59961 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
59962 this.cursor = cursor;
59963 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
59966 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
59967 splitTip : "Drag to resize.",
59968 collapsibleSplitTip : "Drag to resize. Double click to hide.",
59969 useSplitTips : false,
59971 applyConfig : function(config){
59972 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
59975 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
59976 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
59977 /** The SplitBar for this region
59978 * @type Roo.SplitBar */
59979 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
59980 this.split.on("moved", this.onSplitMove, this);
59981 this.split.useShim = config.useShim === true;
59982 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
59983 if(this.useSplitTips){
59984 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
59986 if(config.collapsible){
59987 this.split.el.on("dblclick", this.collapse, this);
59990 if(typeof config.minSize != "undefined"){
59991 this.split.minSize = config.minSize;
59993 if(typeof config.maxSize != "undefined"){
59994 this.split.maxSize = config.maxSize;
59996 if(config.hideWhenEmpty || config.hidden || config.collapsed){
59997 this.hideSplitter();
60002 getHMaxSize : function(){
60003 var cmax = this.config.maxSize || 10000;
60004 var center = this.mgr.getRegion("center");
60005 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
60008 getVMaxSize : function(){
60009 var cmax = this.config.maxSize || 10000;
60010 var center = this.mgr.getRegion("center");
60011 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
60014 onSplitMove : function(split, newSize){
60015 this.fireEvent("resized", this, newSize);
60019 * Returns the {@link Roo.SplitBar} for this region.
60020 * @return {Roo.SplitBar}
60022 getSplitBar : function(){
60027 this.hideSplitter();
60028 Roo.SplitLayoutRegion.superclass.hide.call(this);
60031 hideSplitter : function(){
60033 this.split.el.setLocation(-2000,-2000);
60034 this.split.el.hide();
60040 this.split.el.show();
60042 Roo.SplitLayoutRegion.superclass.show.call(this);
60045 beforeSlide: function(){
60046 if(Roo.isGecko){// firefox overflow auto bug workaround
60047 this.bodyEl.clip();
60049 this.tabs.bodyEl.clip();
60051 if(this.activePanel){
60052 this.activePanel.getEl().clip();
60054 if(this.activePanel.beforeSlide){
60055 this.activePanel.beforeSlide();
60061 afterSlide : function(){
60062 if(Roo.isGecko){// firefox overflow auto bug workaround
60063 this.bodyEl.unclip();
60065 this.tabs.bodyEl.unclip();
60067 if(this.activePanel){
60068 this.activePanel.getEl().unclip();
60069 if(this.activePanel.afterSlide){
60070 this.activePanel.afterSlide();
60076 initAutoHide : function(){
60077 if(this.autoHide !== false){
60078 if(!this.autoHideHd){
60079 var st = new Roo.util.DelayedTask(this.slideIn, this);
60080 this.autoHideHd = {
60081 "mouseout": function(e){
60082 if(!e.within(this.el, true)){
60086 "mouseover" : function(e){
60092 this.el.on(this.autoHideHd);
60096 clearAutoHide : function(){
60097 if(this.autoHide !== false){
60098 this.el.un("mouseout", this.autoHideHd.mouseout);
60099 this.el.un("mouseover", this.autoHideHd.mouseover);
60103 clearMonitor : function(){
60104 Roo.get(document).un("click", this.slideInIf, this);
60107 // these names are backwards but not changed for compat
60108 slideOut : function(){
60109 if(this.isSlid || this.el.hasActiveFx()){
60112 this.isSlid = true;
60113 if(this.collapseBtn){
60114 this.collapseBtn.hide();
60116 this.closeBtnState = this.closeBtn.getStyle('display');
60117 this.closeBtn.hide();
60119 this.stickBtn.show();
60122 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
60123 this.beforeSlide();
60124 this.el.setStyle("z-index", 10001);
60125 this.el.slideIn(this.getSlideAnchor(), {
60126 callback: function(){
60128 this.initAutoHide();
60129 Roo.get(document).on("click", this.slideInIf, this);
60130 this.fireEvent("slideshow", this);
60137 afterSlideIn : function(){
60138 this.clearAutoHide();
60139 this.isSlid = false;
60140 this.clearMonitor();
60141 this.el.setStyle("z-index", "");
60142 if(this.collapseBtn){
60143 this.collapseBtn.show();
60145 this.closeBtn.setStyle('display', this.closeBtnState);
60147 this.stickBtn.hide();
60149 this.fireEvent("slidehide", this);
60152 slideIn : function(cb){
60153 if(!this.isSlid || this.el.hasActiveFx()){
60157 this.isSlid = false;
60158 this.beforeSlide();
60159 this.el.slideOut(this.getSlideAnchor(), {
60160 callback: function(){
60161 this.el.setLeftTop(-10000, -10000);
60163 this.afterSlideIn();
60171 slideInIf : function(e){
60172 if(!e.within(this.el)){
60177 animateCollapse : function(){
60178 this.beforeSlide();
60179 this.el.setStyle("z-index", 20000);
60180 var anchor = this.getSlideAnchor();
60181 this.el.slideOut(anchor, {
60182 callback : function(){
60183 this.el.setStyle("z-index", "");
60184 this.collapsedEl.slideIn(anchor, {duration:.3});
60186 this.el.setLocation(-10000,-10000);
60188 this.fireEvent("collapsed", this);
60195 animateExpand : function(){
60196 this.beforeSlide();
60197 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60198 this.el.setStyle("z-index", 20000);
60199 this.collapsedEl.hide({
60202 this.el.slideIn(this.getSlideAnchor(), {
60203 callback : function(){
60204 this.el.setStyle("z-index", "");
60207 this.split.el.show();
60209 this.fireEvent("invalidated", this);
60210 this.fireEvent("expanded", this);
60238 getAnchor : function(){
60239 return this.anchors[this.position];
60242 getCollapseAnchor : function(){
60243 return this.canchors[this.position];
60246 getSlideAnchor : function(){
60247 return this.sanchors[this.position];
60250 getAlignAdj : function(){
60251 var cm = this.cmargins;
60252 switch(this.position){
60268 getExpandAdj : function(){
60269 var c = this.collapsedEl, cm = this.cmargins;
60270 switch(this.position){
60272 return [-(cm.right+c.getWidth()+cm.left), 0];
60275 return [cm.right+c.getWidth()+cm.left, 0];
60278 return [0, -(cm.top+cm.bottom+c.getHeight())];
60281 return [0, cm.top+cm.bottom+c.getHeight()];
60287 * Ext JS Library 1.1.1
60288 * Copyright(c) 2006-2007, Ext JS, LLC.
60290 * Originally Released Under LGPL - original licence link has changed is not relivant.
60293 * <script type="text/javascript">
60296 * These classes are private internal classes
60298 Roo.CenterLayoutRegion = function(mgr, config){
60299 Roo.LayoutRegion.call(this, mgr, config, "center");
60300 this.visible = true;
60301 this.minWidth = config.minWidth || 20;
60302 this.minHeight = config.minHeight || 20;
60305 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60307 // center panel can't be hidden
60311 // center panel can't be hidden
60314 getMinWidth: function(){
60315 return this.minWidth;
60318 getMinHeight: function(){
60319 return this.minHeight;
60324 Roo.NorthLayoutRegion = function(mgr, config){
60325 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60327 this.split.placement = Roo.SplitBar.TOP;
60328 this.split.orientation = Roo.SplitBar.VERTICAL;
60329 this.split.el.addClass("x-layout-split-v");
60331 var size = config.initialSize || config.height;
60332 if(typeof size != "undefined"){
60333 this.el.setHeight(size);
60336 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60337 orientation: Roo.SplitBar.VERTICAL,
60338 getBox : function(){
60339 if(this.collapsed){
60340 return this.collapsedEl.getBox();
60342 var box = this.el.getBox();
60344 box.height += this.split.el.getHeight();
60349 updateBox : function(box){
60350 if(this.split && !this.collapsed){
60351 box.height -= this.split.el.getHeight();
60352 this.split.el.setLeft(box.x);
60353 this.split.el.setTop(box.y+box.height);
60354 this.split.el.setWidth(box.width);
60356 if(this.collapsed){
60357 this.updateBody(box.width, null);
60359 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60363 Roo.SouthLayoutRegion = function(mgr, config){
60364 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60366 this.split.placement = Roo.SplitBar.BOTTOM;
60367 this.split.orientation = Roo.SplitBar.VERTICAL;
60368 this.split.el.addClass("x-layout-split-v");
60370 var size = config.initialSize || config.height;
60371 if(typeof size != "undefined"){
60372 this.el.setHeight(size);
60375 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60376 orientation: Roo.SplitBar.VERTICAL,
60377 getBox : function(){
60378 if(this.collapsed){
60379 return this.collapsedEl.getBox();
60381 var box = this.el.getBox();
60383 var sh = this.split.el.getHeight();
60390 updateBox : function(box){
60391 if(this.split && !this.collapsed){
60392 var sh = this.split.el.getHeight();
60395 this.split.el.setLeft(box.x);
60396 this.split.el.setTop(box.y-sh);
60397 this.split.el.setWidth(box.width);
60399 if(this.collapsed){
60400 this.updateBody(box.width, null);
60402 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60406 Roo.EastLayoutRegion = function(mgr, config){
60407 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60409 this.split.placement = Roo.SplitBar.RIGHT;
60410 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60411 this.split.el.addClass("x-layout-split-h");
60413 var size = config.initialSize || config.width;
60414 if(typeof size != "undefined"){
60415 this.el.setWidth(size);
60418 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60419 orientation: Roo.SplitBar.HORIZONTAL,
60420 getBox : function(){
60421 if(this.collapsed){
60422 return this.collapsedEl.getBox();
60424 var box = this.el.getBox();
60426 var sw = this.split.el.getWidth();
60433 updateBox : function(box){
60434 if(this.split && !this.collapsed){
60435 var sw = this.split.el.getWidth();
60437 this.split.el.setLeft(box.x);
60438 this.split.el.setTop(box.y);
60439 this.split.el.setHeight(box.height);
60442 if(this.collapsed){
60443 this.updateBody(null, box.height);
60445 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60449 Roo.WestLayoutRegion = function(mgr, config){
60450 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60452 this.split.placement = Roo.SplitBar.LEFT;
60453 this.split.orientation = Roo.SplitBar.HORIZONTAL;
60454 this.split.el.addClass("x-layout-split-h");
60456 var size = config.initialSize || config.width;
60457 if(typeof size != "undefined"){
60458 this.el.setWidth(size);
60461 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60462 orientation: Roo.SplitBar.HORIZONTAL,
60463 getBox : function(){
60464 if(this.collapsed){
60465 return this.collapsedEl.getBox();
60467 var box = this.el.getBox();
60469 box.width += this.split.el.getWidth();
60474 updateBox : function(box){
60475 if(this.split && !this.collapsed){
60476 var sw = this.split.el.getWidth();
60478 this.split.el.setLeft(box.x+box.width);
60479 this.split.el.setTop(box.y);
60480 this.split.el.setHeight(box.height);
60482 if(this.collapsed){
60483 this.updateBody(null, box.height);
60485 Roo.LayoutRegion.prototype.updateBox.call(this, box);
60490 * Ext JS Library 1.1.1
60491 * Copyright(c) 2006-2007, Ext JS, LLC.
60493 * Originally Released Under LGPL - original licence link has changed is not relivant.
60496 * <script type="text/javascript">
60501 * Private internal class for reading and applying state
60503 Roo.LayoutStateManager = function(layout){
60504 // default empty state
60513 Roo.LayoutStateManager.prototype = {
60514 init : function(layout, provider){
60515 this.provider = provider;
60516 var state = provider.get(layout.id+"-layout-state");
60518 var wasUpdating = layout.isUpdating();
60520 layout.beginUpdate();
60522 for(var key in state){
60523 if(typeof state[key] != "function"){
60524 var rstate = state[key];
60525 var r = layout.getRegion(key);
60528 r.resizeTo(rstate.size);
60530 if(rstate.collapsed == true){
60533 r.expand(null, true);
60539 layout.endUpdate();
60541 this.state = state;
60543 this.layout = layout;
60544 layout.on("regionresized", this.onRegionResized, this);
60545 layout.on("regioncollapsed", this.onRegionCollapsed, this);
60546 layout.on("regionexpanded", this.onRegionExpanded, this);
60549 storeState : function(){
60550 this.provider.set(this.layout.id+"-layout-state", this.state);
60553 onRegionResized : function(region, newSize){
60554 this.state[region.getPosition()].size = newSize;
60558 onRegionCollapsed : function(region){
60559 this.state[region.getPosition()].collapsed = true;
60563 onRegionExpanded : function(region){
60564 this.state[region.getPosition()].collapsed = false;
60569 * Ext JS Library 1.1.1
60570 * Copyright(c) 2006-2007, Ext JS, LLC.
60572 * Originally Released Under LGPL - original licence link has changed is not relivant.
60575 * <script type="text/javascript">
60578 * @class Roo.ContentPanel
60579 * @extends Roo.util.Observable
60580 * @children Roo.form.Form Roo.JsonView Roo.View
60581 * @parent Roo.BorderLayout Roo.LayoutDialog builder
60582 * A basic ContentPanel element.
60583 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
60584 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
60585 * @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
60586 * @cfg {Boolean} closable True if the panel can be closed/removed
60587 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
60588 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60589 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
60590 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
60591 * @cfg {String} title The title for this panel
60592 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60593 * @cfg {String} url Calls {@link #setUrl} with this value
60594 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60595 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
60596 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
60597 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
60598 * @cfg {String} style Extra style to add to the content panel
60599 * @cfg {Roo.menu.Menu} menu popup menu
60602 * Create a new ContentPanel.
60603 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60604 * @param {String/Object} config A string to set only the title or a config object
60605 * @param {String} content (optional) Set the HTML content for this panel
60606 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60608 Roo.ContentPanel = function(el, config, content){
60611 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60615 if (config && config.parentLayout) {
60616 el = config.parentLayout.el.createChild();
60619 if(el.autoCreate){ // xtype is available if this is called from factory
60623 this.el = Roo.get(el);
60624 if(!this.el && config && config.autoCreate){
60625 if(typeof config.autoCreate == "object"){
60626 if(!config.autoCreate.id){
60627 config.autoCreate.id = config.id||el;
60629 this.el = Roo.DomHelper.append(document.body,
60630 config.autoCreate, true);
60632 this.el = Roo.DomHelper.append(document.body,
60633 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60638 this.closable = false;
60639 this.loaded = false;
60640 this.active = false;
60641 if(typeof config == "string"){
60642 this.title = config;
60644 Roo.apply(this, config);
60647 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60648 this.wrapEl = this.el.wrap();
60649 this.toolbar.container = this.el.insertSibling(false, 'before');
60650 this.toolbar = new Roo.Toolbar(this.toolbar);
60653 // xtype created footer. - not sure if will work as we normally have to render first..
60654 if (this.footer && !this.footer.el && this.footer.xtype) {
60655 if (!this.wrapEl) {
60656 this.wrapEl = this.el.wrap();
60659 this.footer.container = this.wrapEl.createChild();
60661 this.footer = Roo.factory(this.footer, Roo);
60666 this.resizeEl = Roo.get(this.resizeEl, true);
60668 this.resizeEl = this.el;
60670 // handle view.xtype
60678 * Fires when this panel is activated.
60679 * @param {Roo.ContentPanel} this
60683 * @event deactivate
60684 * Fires when this panel is activated.
60685 * @param {Roo.ContentPanel} this
60687 "deactivate" : true,
60691 * Fires when this panel is resized if fitToFrame is true.
60692 * @param {Roo.ContentPanel} this
60693 * @param {Number} width The width after any component adjustments
60694 * @param {Number} height The height after any component adjustments
60700 * Fires when this tab is created
60701 * @param {Roo.ContentPanel} this
60711 if(this.autoScroll){
60712 this.resizeEl.setStyle("overflow", "auto");
60714 // fix randome scrolling
60715 this.el.on('scroll', function() {
60716 Roo.log('fix random scolling');
60717 this.scrollTo('top',0);
60720 content = content || this.content;
60722 this.setContent(content);
60724 if(config && config.url){
60725 this.setUrl(this.url, this.params, this.loadOnce);
60730 Roo.ContentPanel.superclass.constructor.call(this);
60732 if (this.view && typeof(this.view.xtype) != 'undefined') {
60733 this.view.el = this.el.appendChild(document.createElement("div"));
60734 this.view = Roo.factory(this.view);
60735 this.view.render && this.view.render(false, '');
60739 this.fireEvent('render', this);
60742 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60744 setRegion : function(region){
60745 this.region = region;
60747 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60749 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60754 * Returns the toolbar for this Panel if one was configured.
60755 * @return {Roo.Toolbar}
60757 getToolbar : function(){
60758 return this.toolbar;
60761 setActiveState : function(active){
60762 this.active = active;
60764 this.fireEvent("deactivate", this);
60766 this.fireEvent("activate", this);
60770 * Updates this panel's element
60771 * @param {String} content The new content
60772 * @param {Boolean} loadScripts (optional) true to look for and process scripts
60774 setContent : function(content, loadScripts){
60775 this.el.update(content, loadScripts);
60778 ignoreResize : function(w, h){
60779 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60782 this.lastSize = {width: w, height: h};
60787 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60788 * @return {Roo.UpdateManager} The UpdateManager
60790 getUpdateManager : function(){
60791 return this.el.getUpdateManager();
60794 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60795 * @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:
60798 url: "your-url.php",
60799 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60800 callback: yourFunction,
60801 scope: yourObject, //(optional scope)
60804 text: "Loading...",
60809 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60810 * 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.
60811 * @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}
60812 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60813 * @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.
60814 * @return {Roo.ContentPanel} this
60817 var um = this.el.getUpdateManager();
60818 um.update.apply(um, arguments);
60824 * 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.
60825 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60826 * @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)
60827 * @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)
60828 * @return {Roo.UpdateManager} The UpdateManager
60830 setUrl : function(url, params, loadOnce){
60831 if(this.refreshDelegate){
60832 this.removeListener("activate", this.refreshDelegate);
60834 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60835 this.on("activate", this.refreshDelegate);
60836 return this.el.getUpdateManager();
60839 _handleRefresh : function(url, params, loadOnce){
60840 if(!loadOnce || !this.loaded){
60841 var updater = this.el.getUpdateManager();
60842 updater.update(url, params, this._setLoaded.createDelegate(this));
60846 _setLoaded : function(){
60847 this.loaded = true;
60851 * Returns this panel's id
60854 getId : function(){
60859 * Returns this panel's element - used by regiosn to add.
60860 * @return {Roo.Element}
60862 getEl : function(){
60863 return this.wrapEl || this.el;
60866 adjustForComponents : function(width, height)
60868 //Roo.log('adjustForComponents ');
60869 if(this.resizeEl != this.el){
60870 width -= this.el.getFrameWidth('lr');
60871 height -= this.el.getFrameWidth('tb');
60874 var te = this.toolbar.getEl();
60875 height -= te.getHeight();
60876 te.setWidth(width);
60879 var te = this.footer.getEl();
60880 //Roo.log("footer:" + te.getHeight());
60882 height -= te.getHeight();
60883 te.setWidth(width);
60887 if(this.adjustments){
60888 width += this.adjustments[0];
60889 height += this.adjustments[1];
60891 return {"width": width, "height": height};
60894 setSize : function(width, height){
60895 if(this.fitToFrame && !this.ignoreResize(width, height)){
60896 if(this.fitContainer && this.resizeEl != this.el){
60897 this.el.setSize(width, height);
60899 var size = this.adjustForComponents(width, height);
60900 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60901 this.fireEvent('resize', this, size.width, size.height);
60906 * Returns this panel's title
60909 getTitle : function(){
60914 * Set this panel's title
60915 * @param {String} title
60917 setTitle : function(title){
60918 this.title = title;
60920 this.region.updatePanelTitle(this, title);
60925 * Returns true is this panel was configured to be closable
60926 * @return {Boolean}
60928 isClosable : function(){
60929 return this.closable;
60932 beforeSlide : function(){
60934 this.resizeEl.clip();
60937 afterSlide : function(){
60939 this.resizeEl.unclip();
60943 * Force a content refresh from the URL specified in the {@link #setUrl} method.
60944 * Will fail silently if the {@link #setUrl} method has not been called.
60945 * This does not activate the panel, just updates its content.
60947 refresh : function(){
60948 if(this.refreshDelegate){
60949 this.loaded = false;
60950 this.refreshDelegate();
60955 * Destroys this panel
60957 destroy : function(){
60958 this.el.removeAllListeners();
60959 var tempEl = document.createElement("span");
60960 tempEl.appendChild(this.el.dom);
60961 tempEl.innerHTML = "";
60967 * form - if the content panel contains a form - this is a reference to it.
60968 * @type {Roo.form.Form}
60972 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
60973 * This contains a reference to it.
60979 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
60989 * @param {Object} cfg Xtype definition of item to add.
60992 addxtype : function(cfg) {
60993 if(cfg.xtype.match(/^UploadCropbox$/)) {
60995 this.cropbox = new Roo.factory(cfg);
60997 this.cropbox.render(this.el);
60999 return this.cropbox;
61002 if (cfg.xtype.match(/^Form$/)) {
61005 //if (this.footer) {
61006 // el = this.footer.container.insertSibling(false, 'before');
61008 el = this.el.createChild();
61011 this.form = new Roo.form.Form(cfg);
61014 if ( this.form.allItems.length) {
61015 this.form.render(el.dom);
61019 // should only have one of theses..
61020 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
61021 // views.. should not be just added - used named prop 'view''
61023 cfg.el = this.el.appendChild(document.createElement("div"));
61026 var ret = new Roo.factory(cfg);
61028 ret.render && ret.render(false, ''); // render blank..
61048 * @class Roo.GridPanel
61049 * @extends Roo.ContentPanel
61050 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61052 * Create a new GridPanel.
61053 * @cfg {Roo.grid.Grid} grid The grid for this panel
61055 Roo.GridPanel = function(grid, config){
61057 // universal ctor...
61058 if (typeof(grid.grid) != 'undefined') {
61060 grid = config.grid;
61062 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
61063 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
61065 this.wrapper.dom.appendChild(grid.getGridEl().dom);
61067 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
61070 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
61072 // xtype created footer. - not sure if will work as we normally have to render first..
61073 if (this.footer && !this.footer.el && this.footer.xtype) {
61075 this.footer.container = this.grid.getView().getFooterPanel(true);
61076 this.footer.dataSource = this.grid.dataSource;
61077 this.footer = Roo.factory(this.footer, Roo);
61081 grid.monitorWindowResize = false; // turn off autosizing
61082 grid.autoHeight = false;
61083 grid.autoWidth = false;
61085 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
61088 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
61089 getId : function(){
61090 return this.grid.id;
61094 * Returns the grid for this panel
61095 * @return {Roo.grid.Grid}
61097 getGrid : function(){
61101 setSize : function(width, height){
61102 if(!this.ignoreResize(width, height)){
61103 var grid = this.grid;
61104 var size = this.adjustForComponents(width, height);
61105 grid.getGridEl().setSize(size.width, size.height);
61110 beforeSlide : function(){
61111 this.grid.getView().scroller.clip();
61114 afterSlide : function(){
61115 this.grid.getView().scroller.unclip();
61118 destroy : function(){
61119 this.grid.destroy();
61121 Roo.GridPanel.superclass.destroy.call(this);
61127 * @class Roo.NestedLayoutPanel
61128 * @extends Roo.ContentPanel
61129 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61130 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
61134 * Create a new NestedLayoutPanel.
61137 * @param {Roo.BorderLayout} layout [required] The layout for this panel
61138 * @param {String/Object} config A string to set only the title or a config object
61140 Roo.NestedLayoutPanel = function(layout, config)
61142 // construct with only one argument..
61143 /* FIXME - implement nicer consturctors
61144 if (layout.layout) {
61146 layout = config.layout;
61147 delete config.layout;
61149 if (layout.xtype && !layout.getEl) {
61150 // then layout needs constructing..
61151 layout = Roo.factory(layout, Roo);
61156 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
61158 layout.monitorWindowResize = false; // turn off autosizing
61159 this.layout = layout;
61160 this.layout.getEl().addClass("x-layout-nested-layout");
61167 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
61171 setSize : function(width, height){
61172 if(!this.ignoreResize(width, height)){
61173 var size = this.adjustForComponents(width, height);
61174 var el = this.layout.getEl();
61175 el.setSize(size.width, size.height);
61176 var touch = el.dom.offsetWidth;
61177 this.layout.layout();
61178 // ie requires a double layout on the first pass
61179 if(Roo.isIE && !this.initialized){
61180 this.initialized = true;
61181 this.layout.layout();
61186 // activate all subpanels if not currently active..
61188 setActiveState : function(active){
61189 this.active = active;
61191 this.fireEvent("deactivate", this);
61195 this.fireEvent("activate", this);
61196 // not sure if this should happen before or after..
61197 if (!this.layout) {
61198 return; // should not happen..
61201 for (var r in this.layout.regions) {
61202 reg = this.layout.getRegion(r);
61203 if (reg.getActivePanel()) {
61204 //reg.showPanel(reg.getActivePanel()); // force it to activate..
61205 reg.setActivePanel(reg.getActivePanel());
61208 if (!reg.panels.length) {
61211 reg.showPanel(reg.getPanel(0));
61220 * Returns the nested BorderLayout for this panel
61221 * @return {Roo.BorderLayout}
61223 getLayout : function(){
61224 return this.layout;
61228 * Adds a xtype elements to the layout of the nested panel
61232 xtype : 'ContentPanel',
61239 xtype : 'NestedLayoutPanel',
61245 items : [ ... list of content panels or nested layout panels.. ]
61249 * @param {Object} cfg Xtype definition of item to add.
61251 addxtype : function(cfg) {
61252 return this.layout.addxtype(cfg);
61257 Roo.ScrollPanel = function(el, config, content){
61258 config = config || {};
61259 config.fitToFrame = true;
61260 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61262 this.el.dom.style.overflow = "hidden";
61263 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61264 this.el.removeClass("x-layout-inactive-content");
61265 this.el.on("mousewheel", this.onWheel, this);
61267 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
61268 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
61269 up.unselectable(); down.unselectable();
61270 up.on("click", this.scrollUp, this);
61271 down.on("click", this.scrollDown, this);
61272 up.addClassOnOver("x-scroller-btn-over");
61273 down.addClassOnOver("x-scroller-btn-over");
61274 up.addClassOnClick("x-scroller-btn-click");
61275 down.addClassOnClick("x-scroller-btn-click");
61276 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61278 this.resizeEl = this.el;
61279 this.el = wrap; this.up = up; this.down = down;
61282 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61284 wheelIncrement : 5,
61285 scrollUp : function(){
61286 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61289 scrollDown : function(){
61290 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61293 afterScroll : function(){
61294 var el = this.resizeEl;
61295 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61296 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61297 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61300 setSize : function(){
61301 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61302 this.afterScroll();
61305 onWheel : function(e){
61306 var d = e.getWheelDelta();
61307 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61308 this.afterScroll();
61312 setContent : function(content, loadScripts){
61313 this.resizeEl.update(content, loadScripts);
61321 * @class Roo.TreePanel
61322 * @extends Roo.ContentPanel
61323 * @parent Roo.BorderLayout Roo.LayoutDialog builder
61324 * Treepanel component
61327 * Create a new TreePanel. - defaults to fit/scoll contents.
61328 * @param {String/Object} config A string to set only the panel's title, or a config object
61330 Roo.TreePanel = function(config){
61331 var el = config.el;
61332 var tree = config.tree;
61333 delete config.tree;
61334 delete config.el; // hopefull!
61336 // wrapper for IE7 strict & safari scroll issue
61338 var treeEl = el.createChild();
61339 config.resizeEl = treeEl;
61343 Roo.TreePanel.superclass.constructor.call(this, el, config);
61346 this.tree = new Roo.tree.TreePanel(treeEl , tree);
61347 //console.log(tree);
61348 this.on('activate', function()
61350 if (this.tree.rendered) {
61353 //console.log('render tree');
61354 this.tree.render();
61356 // this should not be needed.. - it's actually the 'el' that resizes?
61357 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61359 //this.on('resize', function (cp, w, h) {
61360 // this.tree.innerCt.setWidth(w);
61361 // this.tree.innerCt.setHeight(h);
61362 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
61369 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
61373 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61380 * Ext JS Library 1.1.1
61381 * Copyright(c) 2006-2007, Ext JS, LLC.
61383 * Originally Released Under LGPL - original licence link has changed is not relivant.
61386 * <script type="text/javascript">
61391 * @class Roo.ReaderLayout
61392 * @extends Roo.BorderLayout
61393 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
61394 * center region containing two nested regions (a top one for a list view and one for item preview below),
61395 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61396 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61397 * expedites the setup of the overall layout and regions for this common application style.
61400 var reader = new Roo.ReaderLayout();
61401 var CP = Roo.ContentPanel; // shortcut for adding
61403 reader.beginUpdate();
61404 reader.add("north", new CP("north", "North"));
61405 reader.add("west", new CP("west", {title: "West"}));
61406 reader.add("east", new CP("east", {title: "East"}));
61408 reader.regions.listView.add(new CP("listView", "List"));
61409 reader.regions.preview.add(new CP("preview", "Preview"));
61410 reader.endUpdate();
61413 * Create a new ReaderLayout
61414 * @param {Object} config Configuration options
61415 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61416 * document.body if omitted)
61418 Roo.ReaderLayout = function(config, renderTo){
61419 var c = config || {size:{}};
61420 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61421 north: c.north !== false ? Roo.apply({
61425 }, c.north) : false,
61426 west: c.west !== false ? Roo.apply({
61434 margins:{left:5,right:0,bottom:5,top:5},
61435 cmargins:{left:5,right:5,bottom:5,top:5}
61436 }, c.west) : false,
61437 east: c.east !== false ? Roo.apply({
61445 margins:{left:0,right:5,bottom:5,top:5},
61446 cmargins:{left:5,right:5,bottom:5,top:5}
61447 }, c.east) : false,
61448 center: Roo.apply({
61449 tabPosition: 'top',
61453 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61457 this.el.addClass('x-reader');
61459 this.beginUpdate();
61461 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61462 south: c.preview !== false ? Roo.apply({
61469 cmargins:{top:5,left:0, right:0, bottom:0}
61470 }, c.preview) : false,
61471 center: Roo.apply({
61477 this.add('center', new Roo.NestedLayoutPanel(inner,
61478 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61482 this.regions.preview = inner.getRegion('south');
61483 this.regions.listView = inner.getRegion('center');
61486 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61488 * Ext JS Library 1.1.1
61489 * Copyright(c) 2006-2007, Ext JS, LLC.
61491 * Originally Released Under LGPL - original licence link has changed is not relivant.
61494 * <script type="text/javascript">
61498 * @class Roo.grid.Grid
61499 * @extends Roo.util.Observable
61500 * This class represents the primary interface of a component based grid control.
61501 * <br><br>Usage:<pre><code>
61502 var grid = new Roo.grid.Grid("my-container-id", {
61505 selModel: mySelectionModel,
61506 autoSizeColumns: true,
61507 monitorWindowResize: false,
61508 trackMouseOver: true
61513 * <b>Common Problems:</b><br/>
61514 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61515 * element will correct this<br/>
61516 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61517 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61518 * are unpredictable.<br/>
61519 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61520 * grid to calculate dimensions/offsets.<br/>
61522 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61523 * The container MUST have some type of size defined for the grid to fill. The container will be
61524 * automatically set to position relative if it isn't already.
61525 * @param {Object} config A config object that sets properties on this grid.
61527 Roo.grid.Grid = function(container, config){
61528 // initialize the container
61529 this.container = Roo.get(container);
61530 this.container.update("");
61531 this.container.setStyle("overflow", "hidden");
61532 this.container.addClass('x-grid-container');
61534 this.id = this.container.id;
61536 Roo.apply(this, config);
61537 // check and correct shorthanded configs
61539 this.dataSource = this.ds;
61543 this.colModel = this.cm;
61547 this.selModel = this.sm;
61551 if (this.selModel) {
61552 this.selModel = Roo.factory(this.selModel, Roo.grid);
61553 this.sm = this.selModel;
61554 this.sm.xmodule = this.xmodule || false;
61556 if (typeof(this.colModel.config) == 'undefined') {
61557 this.colModel = new Roo.grid.ColumnModel(this.colModel);
61558 this.cm = this.colModel;
61559 this.cm.xmodule = this.xmodule || false;
61561 if (this.dataSource) {
61562 this.dataSource= Roo.factory(this.dataSource, Roo.data);
61563 this.ds = this.dataSource;
61564 this.ds.xmodule = this.xmodule || false;
61571 this.container.setWidth(this.width);
61575 this.container.setHeight(this.height);
61582 * The raw click event for the entire grid.
61583 * @param {Roo.EventObject} e
61588 * The raw dblclick event for the entire grid.
61589 * @param {Roo.EventObject} e
61593 * @event contextmenu
61594 * The raw contextmenu event for the entire grid.
61595 * @param {Roo.EventObject} e
61597 "contextmenu" : true,
61600 * The raw mousedown event for the entire grid.
61601 * @param {Roo.EventObject} e
61603 "mousedown" : true,
61606 * The raw mouseup event for the entire grid.
61607 * @param {Roo.EventObject} e
61612 * The raw mouseover event for the entire grid.
61613 * @param {Roo.EventObject} e
61615 "mouseover" : true,
61618 * The raw mouseout event for the entire grid.
61619 * @param {Roo.EventObject} e
61624 * The raw keypress event for the entire grid.
61625 * @param {Roo.EventObject} e
61630 * The raw keydown event for the entire grid.
61631 * @param {Roo.EventObject} e
61639 * Fires when a cell is clicked
61640 * @param {Grid} this
61641 * @param {Number} rowIndex
61642 * @param {Number} columnIndex
61643 * @param {Roo.EventObject} e
61645 "cellclick" : true,
61647 * @event celldblclick
61648 * Fires when a cell is double clicked
61649 * @param {Grid} this
61650 * @param {Number} rowIndex
61651 * @param {Number} columnIndex
61652 * @param {Roo.EventObject} e
61654 "celldblclick" : true,
61657 * Fires when a row is clicked
61658 * @param {Grid} this
61659 * @param {Number} rowIndex
61660 * @param {Roo.EventObject} e
61664 * @event rowdblclick
61665 * Fires when a row is double clicked
61666 * @param {Grid} this
61667 * @param {Number} rowIndex
61668 * @param {Roo.EventObject} e
61670 "rowdblclick" : true,
61672 * @event headerclick
61673 * Fires when a header is clicked
61674 * @param {Grid} this
61675 * @param {Number} columnIndex
61676 * @param {Roo.EventObject} e
61678 "headerclick" : true,
61680 * @event headerdblclick
61681 * Fires when a header cell is double clicked
61682 * @param {Grid} this
61683 * @param {Number} columnIndex
61684 * @param {Roo.EventObject} e
61686 "headerdblclick" : true,
61688 * @event rowcontextmenu
61689 * Fires when a row is right clicked
61690 * @param {Grid} this
61691 * @param {Number} rowIndex
61692 * @param {Roo.EventObject} e
61694 "rowcontextmenu" : true,
61696 * @event cellcontextmenu
61697 * Fires when a cell is right clicked
61698 * @param {Grid} this
61699 * @param {Number} rowIndex
61700 * @param {Number} cellIndex
61701 * @param {Roo.EventObject} e
61703 "cellcontextmenu" : true,
61705 * @event headercontextmenu
61706 * Fires when a header is right clicked
61707 * @param {Grid} this
61708 * @param {Number} columnIndex
61709 * @param {Roo.EventObject} e
61711 "headercontextmenu" : true,
61713 * @event bodyscroll
61714 * Fires when the body element is scrolled
61715 * @param {Number} scrollLeft
61716 * @param {Number} scrollTop
61718 "bodyscroll" : true,
61720 * @event columnresize
61721 * Fires when the user resizes a column
61722 * @param {Number} columnIndex
61723 * @param {Number} newSize
61725 "columnresize" : true,
61727 * @event columnmove
61728 * Fires when the user moves a column
61729 * @param {Number} oldIndex
61730 * @param {Number} newIndex
61732 "columnmove" : true,
61735 * Fires when row(s) start being dragged
61736 * @param {Grid} this
61737 * @param {Roo.GridDD} dd The drag drop object
61738 * @param {event} e The raw browser event
61740 "startdrag" : true,
61743 * Fires when a drag operation is complete
61744 * @param {Grid} this
61745 * @param {Roo.GridDD} dd The drag drop object
61746 * @param {event} e The raw browser event
61751 * Fires when dragged row(s) are dropped on a valid DD target
61752 * @param {Grid} this
61753 * @param {Roo.GridDD} dd The drag drop object
61754 * @param {String} targetId The target drag drop object
61755 * @param {event} e The raw browser event
61760 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61761 * @param {Grid} this
61762 * @param {Roo.GridDD} dd The drag drop object
61763 * @param {String} targetId The target drag drop object
61764 * @param {event} e The raw browser event
61769 * Fires when the dragged row(s) first cross another DD target while being dragged
61770 * @param {Grid} this
61771 * @param {Roo.GridDD} dd The drag drop object
61772 * @param {String} targetId The target drag drop object
61773 * @param {event} e The raw browser event
61775 "dragenter" : true,
61778 * Fires when the dragged row(s) leave another DD target while being dragged
61779 * @param {Grid} this
61780 * @param {Roo.GridDD} dd The drag drop object
61781 * @param {String} targetId The target drag drop object
61782 * @param {event} e The raw browser event
61787 * Fires when a row is rendered, so you can change add a style to it.
61788 * @param {GridView} gridview The grid view
61789 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
61795 * Fires when the grid is rendered
61796 * @param {Grid} grid
61801 Roo.grid.Grid.superclass.constructor.call(this);
61803 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61806 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61809 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
61812 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61815 * @cfg {Roo.data.Store} ds The data store for the grid
61818 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61821 * @cfg {String} ddGroup - drag drop group.
61824 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61828 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61830 minColumnWidth : 25,
61833 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61834 * <b>on initial render.</b> It is more efficient to explicitly size the columns
61835 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
61837 autoSizeColumns : false,
61840 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61842 autoSizeHeaders : true,
61845 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61847 monitorWindowResize : true,
61850 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61851 * rows measured to get a columns size. Default is 0 (all rows).
61853 maxRowsToMeasure : 0,
61856 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61858 trackMouseOver : true,
61861 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
61864 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
61868 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61870 enableDragDrop : false,
61873 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61875 enableColumnMove : true,
61878 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61880 enableColumnHide : true,
61883 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61885 enableRowHeightSync : false,
61888 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
61893 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61895 autoHeight : false,
61898 * @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.
61900 autoExpandColumn : false,
61903 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61906 autoExpandMin : 50,
61909 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61911 autoExpandMax : 1000,
61914 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61919 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61923 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61927 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
61929 sortColMenu : false,
61935 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
61936 * of a fixed width. Default is false.
61939 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
61944 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
61945 * %0 is replaced with the number of selected rows.
61947 ddText : "{0} selected row{1}",
61951 * Called once after all setup has been completed and the grid is ready to be rendered.
61952 * @return {Roo.grid.Grid} this
61954 render : function()
61956 var c = this.container;
61957 // try to detect autoHeight/width mode
61958 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
61959 this.autoHeight = true;
61961 var view = this.getView();
61964 c.on("click", this.onClick, this);
61965 c.on("dblclick", this.onDblClick, this);
61966 c.on("contextmenu", this.onContextMenu, this);
61967 c.on("keydown", this.onKeyDown, this);
61969 c.on("touchstart", this.onTouchStart, this);
61972 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
61974 this.getSelectionModel().init(this);
61979 this.loadMask = new Roo.LoadMask(this.container,
61980 Roo.apply({store:this.dataSource}, this.loadMask));
61984 if (this.toolbar && this.toolbar.xtype) {
61985 this.toolbar.container = this.getView().getHeaderPanel(true);
61986 this.toolbar = new Roo.Toolbar(this.toolbar);
61988 if (this.footer && this.footer.xtype) {
61989 this.footer.dataSource = this.getDataSource();
61990 this.footer.container = this.getView().getFooterPanel(true);
61991 this.footer = Roo.factory(this.footer, Roo);
61993 if (this.dropTarget && this.dropTarget.xtype) {
61994 delete this.dropTarget.xtype;
61995 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
61999 this.rendered = true;
62000 this.fireEvent('render', this);
62005 * Reconfigures the grid to use a different Store and Column Model.
62006 * The View will be bound to the new objects and refreshed.
62007 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
62008 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
62010 reconfigure : function(dataSource, colModel){
62012 this.loadMask.destroy();
62013 this.loadMask = new Roo.LoadMask(this.container,
62014 Roo.apply({store:dataSource}, this.loadMask));
62016 this.view.bind(dataSource, colModel);
62017 this.dataSource = dataSource;
62018 this.colModel = colModel;
62019 this.view.refresh(true);
62023 * Add's a column, default at the end..
62025 * @param {int} position to add (default end)
62026 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
62028 addColumns : function(pos, ar)
62031 for (var i =0;i< ar.length;i++) {
62033 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
62034 this.cm.lookup[cfg.id] = cfg;
62038 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
62039 pos = this.cm.config.length; //this.cm.config.push(cfg);
62041 pos = Math.max(0,pos);
62044 this.cm.config.splice.apply(this.cm.config, ar);
62048 this.view.generateRules(this.cm);
62049 this.view.refresh(true);
62057 onKeyDown : function(e){
62058 this.fireEvent("keydown", e);
62062 * Destroy this grid.
62063 * @param {Boolean} removeEl True to remove the element
62065 destroy : function(removeEl, keepListeners){
62067 this.loadMask.destroy();
62069 var c = this.container;
62070 c.removeAllListeners();
62071 this.view.destroy();
62072 this.colModel.purgeListeners();
62073 if(!keepListeners){
62074 this.purgeListeners();
62077 if(removeEl === true){
62083 processEvent : function(name, e){
62084 // does this fire select???
62085 //Roo.log('grid:processEvent ' + name);
62087 if (name != 'touchstart' ) {
62088 this.fireEvent(name, e);
62091 var t = e.getTarget();
62093 var header = v.findHeaderIndex(t);
62094 if(header !== false){
62095 var ename = name == 'touchstart' ? 'click' : name;
62097 this.fireEvent("header" + ename, this, header, e);
62099 var row = v.findRowIndex(t);
62100 var cell = v.findCellIndex(t);
62101 if (name == 'touchstart') {
62102 // first touch is always a click.
62103 // hopefull this happens after selection is updated.?
62106 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
62107 var cs = this.selModel.getSelectedCell();
62108 if (row == cs[0] && cell == cs[1]){
62112 if (typeof(this.selModel.getSelections) != 'undefined') {
62113 var cs = this.selModel.getSelections();
62114 var ds = this.dataSource;
62115 if (cs.length == 1 && ds.getAt(row) == cs[0]){
62126 this.fireEvent("row" + name, this, row, e);
62127 if(cell !== false){
62128 this.fireEvent("cell" + name, this, row, cell, e);
62135 onClick : function(e){
62136 this.processEvent("click", e);
62139 onTouchStart : function(e){
62140 this.processEvent("touchstart", e);
62144 onContextMenu : function(e, t){
62145 this.processEvent("contextmenu", e);
62149 onDblClick : function(e){
62150 this.processEvent("dblclick", e);
62154 walkCells : function(row, col, step, fn, scope){
62155 var cm = this.colModel, clen = cm.getColumnCount();
62156 var ds = this.dataSource, rlen = ds.getCount(), first = true;
62168 if(fn.call(scope || this, row, col, cm) === true){
62186 if(fn.call(scope || this, row, col, cm) === true){
62198 getSelections : function(){
62199 return this.selModel.getSelections();
62203 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62204 * but if manual update is required this method will initiate it.
62206 autoSize : function(){
62208 this.view.layout();
62209 if(this.view.adjustForScroll){
62210 this.view.adjustForScroll();
62216 * Returns the grid's underlying element.
62217 * @return {Element} The element
62219 getGridEl : function(){
62220 return this.container;
62223 // private for compatibility, overridden by editor grid
62224 stopEditing : function(){},
62227 * Returns the grid's SelectionModel.
62228 * @return {SelectionModel}
62230 getSelectionModel : function(){
62231 if(!this.selModel){
62232 this.selModel = new Roo.grid.RowSelectionModel();
62234 return this.selModel;
62238 * Returns the grid's DataSource.
62239 * @return {DataSource}
62241 getDataSource : function(){
62242 return this.dataSource;
62246 * Returns the grid's ColumnModel.
62247 * @return {ColumnModel}
62249 getColumnModel : function(){
62250 return this.colModel;
62254 * Returns the grid's GridView object.
62255 * @return {GridView}
62257 getView : function(){
62259 this.view = new Roo.grid.GridView(this.viewConfig);
62260 this.relayEvents(this.view, [
62261 "beforerowremoved", "beforerowsinserted",
62262 "beforerefresh", "rowremoved",
62263 "rowsinserted", "rowupdated" ,"refresh"
62269 * Called to get grid's drag proxy text, by default returns this.ddText.
62270 * Override this to put something different in the dragged text.
62273 getDragDropText : function(){
62274 var count = this.selModel.getCount();
62275 return String.format(this.ddText, count, count == 1 ? '' : 's');
62280 * Ext JS Library 1.1.1
62281 * Copyright(c) 2006-2007, Ext JS, LLC.
62283 * Originally Released Under LGPL - original licence link has changed is not relivant.
62286 * <script type="text/javascript">
62289 * @class Roo.grid.AbstractGridView
62290 * @extends Roo.util.Observable
62292 * Abstract base class for grid Views
62295 Roo.grid.AbstractGridView = function(){
62299 "beforerowremoved" : true,
62300 "beforerowsinserted" : true,
62301 "beforerefresh" : true,
62302 "rowremoved" : true,
62303 "rowsinserted" : true,
62304 "rowupdated" : true,
62307 Roo.grid.AbstractGridView.superclass.constructor.call(this);
62310 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62311 rowClass : "x-grid-row",
62312 cellClass : "x-grid-cell",
62313 tdClass : "x-grid-td",
62314 hdClass : "x-grid-hd",
62315 splitClass : "x-grid-hd-split",
62317 init: function(grid){
62319 var cid = this.grid.getGridEl().id;
62320 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62321 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62322 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62323 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62326 getColumnRenderers : function(){
62327 var renderers = [];
62328 var cm = this.grid.colModel;
62329 var colCount = cm.getColumnCount();
62330 for(var i = 0; i < colCount; i++){
62331 renderers[i] = cm.getRenderer(i);
62336 getColumnIds : function(){
62338 var cm = this.grid.colModel;
62339 var colCount = cm.getColumnCount();
62340 for(var i = 0; i < colCount; i++){
62341 ids[i] = cm.getColumnId(i);
62346 getDataIndexes : function(){
62347 if(!this.indexMap){
62348 this.indexMap = this.buildIndexMap();
62350 return this.indexMap.colToData;
62353 getColumnIndexByDataIndex : function(dataIndex){
62354 if(!this.indexMap){
62355 this.indexMap = this.buildIndexMap();
62357 return this.indexMap.dataToCol[dataIndex];
62361 * Set a css style for a column dynamically.
62362 * @param {Number} colIndex The index of the column
62363 * @param {String} name The css property name
62364 * @param {String} value The css value
62366 setCSSStyle : function(colIndex, name, value){
62367 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62368 Roo.util.CSS.updateRule(selector, name, value);
62371 generateRules : function(cm){
62372 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62373 Roo.util.CSS.removeStyleSheet(rulesId);
62374 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62375 var cid = cm.getColumnId(i);
62376 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62377 this.tdSelector, cid, " {\n}\n",
62378 this.hdSelector, cid, " {\n}\n",
62379 this.splitSelector, cid, " {\n}\n");
62381 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62385 * Ext JS Library 1.1.1
62386 * Copyright(c) 2006-2007, Ext JS, LLC.
62388 * Originally Released Under LGPL - original licence link has changed is not relivant.
62391 * <script type="text/javascript">
62395 // This is a support class used internally by the Grid components
62396 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62398 this.view = grid.getView();
62399 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62400 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62402 this.setHandleElId(Roo.id(hd));
62403 this.setOuterHandleElId(Roo.id(hd2));
62405 this.scroll = false;
62407 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62409 getDragData : function(e){
62410 var t = Roo.lib.Event.getTarget(e);
62411 var h = this.view.findHeaderCell(t);
62413 return {ddel: h.firstChild, header:h};
62418 onInitDrag : function(e){
62419 this.view.headersDisabled = true;
62420 var clone = this.dragData.ddel.cloneNode(true);
62421 clone.id = Roo.id();
62422 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62423 this.proxy.update(clone);
62427 afterValidDrop : function(){
62429 setTimeout(function(){
62430 v.headersDisabled = false;
62434 afterInvalidDrop : function(){
62436 setTimeout(function(){
62437 v.headersDisabled = false;
62443 * Ext JS Library 1.1.1
62444 * Copyright(c) 2006-2007, Ext JS, LLC.
62446 * Originally Released Under LGPL - original licence link has changed is not relivant.
62449 * <script type="text/javascript">
62452 // This is a support class used internally by the Grid components
62453 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62455 this.view = grid.getView();
62456 // split the proxies so they don't interfere with mouse events
62457 this.proxyTop = Roo.DomHelper.append(document.body, {
62458 cls:"col-move-top", html:" "
62460 this.proxyBottom = Roo.DomHelper.append(document.body, {
62461 cls:"col-move-bottom", html:" "
62463 this.proxyTop.hide = this.proxyBottom.hide = function(){
62464 this.setLeftTop(-100,-100);
62465 this.setStyle("visibility", "hidden");
62467 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62468 // temporarily disabled
62469 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62470 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62472 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62473 proxyOffsets : [-4, -9],
62474 fly: Roo.Element.fly,
62476 getTargetFromEvent : function(e){
62477 var t = Roo.lib.Event.getTarget(e);
62478 var cindex = this.view.findCellIndex(t);
62479 if(cindex !== false){
62480 return this.view.getHeaderCell(cindex);
62485 nextVisible : function(h){
62486 var v = this.view, cm = this.grid.colModel;
62489 if(!cm.isHidden(v.getCellIndex(h))){
62497 prevVisible : function(h){
62498 var v = this.view, cm = this.grid.colModel;
62501 if(!cm.isHidden(v.getCellIndex(h))){
62509 positionIndicator : function(h, n, e){
62510 var x = Roo.lib.Event.getPageX(e);
62511 var r = Roo.lib.Dom.getRegion(n.firstChild);
62512 var px, pt, py = r.top + this.proxyOffsets[1];
62513 if((r.right - x) <= (r.right-r.left)/2){
62514 px = r.right+this.view.borderWidth;
62520 var oldIndex = this.view.getCellIndex(h);
62521 var newIndex = this.view.getCellIndex(n);
62523 if(this.grid.colModel.isFixed(newIndex)){
62527 var locked = this.grid.colModel.isLocked(newIndex);
62532 if(oldIndex < newIndex){
62535 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62538 px += this.proxyOffsets[0];
62539 this.proxyTop.setLeftTop(px, py);
62540 this.proxyTop.show();
62541 if(!this.bottomOffset){
62542 this.bottomOffset = this.view.mainHd.getHeight();
62544 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62545 this.proxyBottom.show();
62549 onNodeEnter : function(n, dd, e, data){
62550 if(data.header != n){
62551 this.positionIndicator(data.header, n, e);
62555 onNodeOver : function(n, dd, e, data){
62556 var result = false;
62557 if(data.header != n){
62558 result = this.positionIndicator(data.header, n, e);
62561 this.proxyTop.hide();
62562 this.proxyBottom.hide();
62564 return result ? this.dropAllowed : this.dropNotAllowed;
62567 onNodeOut : function(n, dd, e, data){
62568 this.proxyTop.hide();
62569 this.proxyBottom.hide();
62572 onNodeDrop : function(n, dd, e, data){
62573 var h = data.header;
62575 var cm = this.grid.colModel;
62576 var x = Roo.lib.Event.getPageX(e);
62577 var r = Roo.lib.Dom.getRegion(n.firstChild);
62578 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62579 var oldIndex = this.view.getCellIndex(h);
62580 var newIndex = this.view.getCellIndex(n);
62581 var locked = cm.isLocked(newIndex);
62585 if(oldIndex < newIndex){
62588 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62591 cm.setLocked(oldIndex, locked, true);
62592 cm.moveColumn(oldIndex, newIndex);
62593 this.grid.fireEvent("columnmove", oldIndex, newIndex);
62601 * Ext JS Library 1.1.1
62602 * Copyright(c) 2006-2007, Ext JS, LLC.
62604 * Originally Released Under LGPL - original licence link has changed is not relivant.
62607 * <script type="text/javascript">
62611 * @class Roo.grid.GridView
62612 * @extends Roo.util.Observable
62615 * @param {Object} config
62617 Roo.grid.GridView = function(config){
62618 Roo.grid.GridView.superclass.constructor.call(this);
62621 Roo.apply(this, config);
62624 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62626 unselectable : 'unselectable="on"',
62627 unselectableCls : 'x-unselectable',
62630 rowClass : "x-grid-row",
62632 cellClass : "x-grid-col",
62634 tdClass : "x-grid-td",
62636 hdClass : "x-grid-hd",
62638 splitClass : "x-grid-split",
62640 sortClasses : ["sort-asc", "sort-desc"],
62642 enableMoveAnim : false,
62646 dh : Roo.DomHelper,
62648 fly : Roo.Element.fly,
62650 css : Roo.util.CSS,
62656 scrollIncrement : 22,
62658 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62660 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62662 bind : function(ds, cm){
62664 this.ds.un("load", this.onLoad, this);
62665 this.ds.un("datachanged", this.onDataChange, this);
62666 this.ds.un("add", this.onAdd, this);
62667 this.ds.un("remove", this.onRemove, this);
62668 this.ds.un("update", this.onUpdate, this);
62669 this.ds.un("clear", this.onClear, this);
62672 ds.on("load", this.onLoad, this);
62673 ds.on("datachanged", this.onDataChange, this);
62674 ds.on("add", this.onAdd, this);
62675 ds.on("remove", this.onRemove, this);
62676 ds.on("update", this.onUpdate, this);
62677 ds.on("clear", this.onClear, this);
62682 this.cm.un("widthchange", this.onColWidthChange, this);
62683 this.cm.un("headerchange", this.onHeaderChange, this);
62684 this.cm.un("hiddenchange", this.onHiddenChange, this);
62685 this.cm.un("columnmoved", this.onColumnMove, this);
62686 this.cm.un("columnlockchange", this.onColumnLock, this);
62689 this.generateRules(cm);
62690 cm.on("widthchange", this.onColWidthChange, this);
62691 cm.on("headerchange", this.onHeaderChange, this);
62692 cm.on("hiddenchange", this.onHiddenChange, this);
62693 cm.on("columnmoved", this.onColumnMove, this);
62694 cm.on("columnlockchange", this.onColumnLock, this);
62699 init: function(grid){
62700 Roo.grid.GridView.superclass.init.call(this, grid);
62702 this.bind(grid.dataSource, grid.colModel);
62704 grid.on("headerclick", this.handleHeaderClick, this);
62706 if(grid.trackMouseOver){
62707 grid.on("mouseover", this.onRowOver, this);
62708 grid.on("mouseout", this.onRowOut, this);
62710 grid.cancelTextSelection = function(){};
62711 this.gridId = grid.id;
62713 var tpls = this.templates || {};
62716 tpls.master = new Roo.Template(
62717 '<div class="x-grid" hidefocus="true">',
62718 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62719 '<div class="x-grid-topbar"></div>',
62720 '<div class="x-grid-scroller"><div></div></div>',
62721 '<div class="x-grid-locked">',
62722 '<div class="x-grid-header">{lockedHeader}</div>',
62723 '<div class="x-grid-body">{lockedBody}</div>',
62725 '<div class="x-grid-viewport">',
62726 '<div class="x-grid-header">{header}</div>',
62727 '<div class="x-grid-body">{body}</div>',
62729 '<div class="x-grid-bottombar"></div>',
62731 '<div class="x-grid-resize-proxy"> </div>',
62734 tpls.master.disableformats = true;
62738 tpls.header = new Roo.Template(
62739 '<table border="0" cellspacing="0" cellpadding="0">',
62740 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62743 tpls.header.disableformats = true;
62745 tpls.header.compile();
62748 tpls.hcell = new Roo.Template(
62749 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62750 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62753 tpls.hcell.disableFormats = true;
62755 tpls.hcell.compile();
62758 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62759 this.unselectableCls + '" ' + this.unselectable +'> </div>');
62760 tpls.hsplit.disableFormats = true;
62762 tpls.hsplit.compile();
62765 tpls.body = new Roo.Template(
62766 '<table border="0" cellspacing="0" cellpadding="0">',
62767 "<tbody>{rows}</tbody>",
62770 tpls.body.disableFormats = true;
62772 tpls.body.compile();
62775 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62776 tpls.row.disableFormats = true;
62778 tpls.row.compile();
62781 tpls.cell = new Roo.Template(
62782 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62783 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62784 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62787 tpls.cell.disableFormats = true;
62789 tpls.cell.compile();
62791 this.templates = tpls;
62794 // remap these for backwards compat
62795 onColWidthChange : function(){
62796 this.updateColumns.apply(this, arguments);
62798 onHeaderChange : function(){
62799 this.updateHeaders.apply(this, arguments);
62801 onHiddenChange : function(){
62802 this.handleHiddenChange.apply(this, arguments);
62804 onColumnMove : function(){
62805 this.handleColumnMove.apply(this, arguments);
62807 onColumnLock : function(){
62808 this.handleLockChange.apply(this, arguments);
62811 onDataChange : function(){
62813 this.updateHeaderSortState();
62816 onClear : function(){
62820 onUpdate : function(ds, record){
62821 this.refreshRow(record);
62824 refreshRow : function(record){
62825 var ds = this.ds, index;
62826 if(typeof record == 'number'){
62828 record = ds.getAt(index);
62830 index = ds.indexOf(record);
62832 this.insertRows(ds, index, index, true);
62833 this.onRemove(ds, record, index+1, true);
62834 this.syncRowHeights(index, index);
62836 this.fireEvent("rowupdated", this, index, record);
62839 onAdd : function(ds, records, index){
62840 this.insertRows(ds, index, index + (records.length-1));
62843 onRemove : function(ds, record, index, isUpdate){
62844 if(isUpdate !== true){
62845 this.fireEvent("beforerowremoved", this, index, record);
62847 var bt = this.getBodyTable(), lt = this.getLockedTable();
62848 if(bt.rows[index]){
62849 bt.firstChild.removeChild(bt.rows[index]);
62851 if(lt.rows[index]){
62852 lt.firstChild.removeChild(lt.rows[index]);
62854 if(isUpdate !== true){
62855 this.stripeRows(index);
62856 this.syncRowHeights(index, index);
62858 this.fireEvent("rowremoved", this, index, record);
62862 onLoad : function(){
62863 this.scrollToTop();
62867 * Scrolls the grid to the top
62869 scrollToTop : function(){
62871 this.scroller.dom.scrollTop = 0;
62877 * Gets a panel in the header of the grid that can be used for toolbars etc.
62878 * After modifying the contents of this panel a call to grid.autoSize() may be
62879 * required to register any changes in size.
62880 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62881 * @return Roo.Element
62883 getHeaderPanel : function(doShow){
62885 this.headerPanel.show();
62887 return this.headerPanel;
62891 * Gets a panel in the footer of the grid that can be used for toolbars etc.
62892 * After modifying the contents of this panel a call to grid.autoSize() may be
62893 * required to register any changes in size.
62894 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62895 * @return Roo.Element
62897 getFooterPanel : function(doShow){
62899 this.footerPanel.show();
62901 return this.footerPanel;
62904 initElements : function(){
62905 var E = Roo.Element;
62906 var el = this.grid.getGridEl().dom.firstChild;
62907 var cs = el.childNodes;
62909 this.el = new E(el);
62911 this.focusEl = new E(el.firstChild);
62912 this.focusEl.swallowEvent("click", true);
62914 this.headerPanel = new E(cs[1]);
62915 this.headerPanel.enableDisplayMode("block");
62917 this.scroller = new E(cs[2]);
62918 this.scrollSizer = new E(this.scroller.dom.firstChild);
62920 this.lockedWrap = new E(cs[3]);
62921 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62922 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62924 this.mainWrap = new E(cs[4]);
62925 this.mainHd = new E(this.mainWrap.dom.firstChild);
62926 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
62928 this.footerPanel = new E(cs[5]);
62929 this.footerPanel.enableDisplayMode("block");
62931 this.resizeProxy = new E(cs[6]);
62933 this.headerSelector = String.format(
62934 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
62935 this.lockedHd.id, this.mainHd.id
62938 this.splitterSelector = String.format(
62939 '#{0} div.x-grid-split, #{1} div.x-grid-split',
62940 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
62943 idToCssName : function(s)
62945 return s.replace(/[^a-z0-9]+/ig, '-');
62948 getHeaderCell : function(index){
62949 return Roo.DomQuery.select(this.headerSelector)[index];
62952 getHeaderCellMeasure : function(index){
62953 return this.getHeaderCell(index).firstChild;
62956 getHeaderCellText : function(index){
62957 return this.getHeaderCell(index).firstChild.firstChild;
62960 getLockedTable : function(){
62961 return this.lockedBody.dom.firstChild;
62964 getBodyTable : function(){
62965 return this.mainBody.dom.firstChild;
62968 getLockedRow : function(index){
62969 return this.getLockedTable().rows[index];
62972 getRow : function(index){
62973 return this.getBodyTable().rows[index];
62976 getRowComposite : function(index){
62978 this.rowEl = new Roo.CompositeElementLite();
62980 var els = [], lrow, mrow;
62981 if(lrow = this.getLockedRow(index)){
62984 if(mrow = this.getRow(index)){
62987 this.rowEl.elements = els;
62991 * Gets the 'td' of the cell
62993 * @param {Integer} rowIndex row to select
62994 * @param {Integer} colIndex column to select
62998 getCell : function(rowIndex, colIndex){
62999 var locked = this.cm.getLockedCount();
63001 if(colIndex < locked){
63002 source = this.lockedBody.dom.firstChild;
63004 source = this.mainBody.dom.firstChild;
63005 colIndex -= locked;
63007 return source.rows[rowIndex].childNodes[colIndex];
63010 getCellText : function(rowIndex, colIndex){
63011 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
63014 getCellBox : function(cell){
63015 var b = this.fly(cell).getBox();
63016 if(Roo.isOpera){ // opera fails to report the Y
63017 b.y = cell.offsetTop + this.mainBody.getY();
63022 getCellIndex : function(cell){
63023 var id = String(cell.className).match(this.cellRE);
63025 return parseInt(id[1], 10);
63030 findHeaderIndex : function(n){
63031 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63032 return r ? this.getCellIndex(r) : false;
63035 findHeaderCell : function(n){
63036 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63037 return r ? r : false;
63040 findRowIndex : function(n){
63044 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
63045 return r ? r.rowIndex : false;
63048 findCellIndex : function(node){
63049 var stop = this.el.dom;
63050 while(node && node != stop){
63051 if(this.findRE.test(node.className)){
63052 return this.getCellIndex(node);
63054 node = node.parentNode;
63059 getColumnId : function(index){
63060 return this.cm.getColumnId(index);
63063 getSplitters : function()
63065 if(this.splitterSelector){
63066 return Roo.DomQuery.select(this.splitterSelector);
63072 getSplitter : function(index){
63073 return this.getSplitters()[index];
63076 onRowOver : function(e, t){
63078 if((row = this.findRowIndex(t)) !== false){
63079 this.getRowComposite(row).addClass("x-grid-row-over");
63083 onRowOut : function(e, t){
63085 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
63086 this.getRowComposite(row).removeClass("x-grid-row-over");
63090 renderHeaders : function(){
63092 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
63093 var cb = [], lb = [], sb = [], lsb = [], p = {};
63094 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63095 p.cellId = "x-grid-hd-0-" + i;
63096 p.splitId = "x-grid-csplit-0-" + i;
63097 p.id = cm.getColumnId(i);
63098 p.value = cm.getColumnHeader(i) || "";
63099 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
63100 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
63101 if(!cm.isLocked(i)){
63102 cb[cb.length] = ct.apply(p);
63103 sb[sb.length] = st.apply(p);
63105 lb[lb.length] = ct.apply(p);
63106 lsb[lsb.length] = st.apply(p);
63109 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
63110 ht.apply({cells: cb.join(""), splits:sb.join("")})];
63113 updateHeaders : function(){
63114 var html = this.renderHeaders();
63115 this.lockedHd.update(html[0]);
63116 this.mainHd.update(html[1]);
63120 * Focuses the specified row.
63121 * @param {Number} row The row index
63123 focusRow : function(row)
63125 //Roo.log('GridView.focusRow');
63126 var x = this.scroller.dom.scrollLeft;
63127 this.focusCell(row, 0, false);
63128 this.scroller.dom.scrollLeft = x;
63132 * Focuses the specified cell.
63133 * @param {Number} row The row index
63134 * @param {Number} col The column index
63135 * @param {Boolean} hscroll false to disable horizontal scrolling
63137 focusCell : function(row, col, hscroll)
63139 //Roo.log('GridView.focusCell');
63140 var el = this.ensureVisible(row, col, hscroll);
63141 this.focusEl.alignTo(el, "tl-tl");
63143 this.focusEl.focus();
63145 this.focusEl.focus.defer(1, this.focusEl);
63150 * Scrolls the specified cell into view
63151 * @param {Number} row The row index
63152 * @param {Number} col The column index
63153 * @param {Boolean} hscroll false to disable horizontal scrolling
63155 ensureVisible : function(row, col, hscroll)
63157 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
63158 //return null; //disable for testing.
63159 if(typeof row != "number"){
63160 row = row.rowIndex;
63162 if(row < 0 && row >= this.ds.getCount()){
63165 col = (col !== undefined ? col : 0);
63166 var cm = this.grid.colModel;
63167 while(cm.isHidden(col)){
63171 var el = this.getCell(row, col);
63175 var c = this.scroller.dom;
63177 var ctop = parseInt(el.offsetTop, 10);
63178 var cleft = parseInt(el.offsetLeft, 10);
63179 var cbot = ctop + el.offsetHeight;
63180 var cright = cleft + el.offsetWidth;
63182 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
63183 var stop = parseInt(c.scrollTop, 10);
63184 var sleft = parseInt(c.scrollLeft, 10);
63185 var sbot = stop + ch;
63186 var sright = sleft + c.clientWidth;
63188 Roo.log('GridView.ensureVisible:' +
63190 ' c.clientHeight:' + c.clientHeight +
63191 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
63199 c.scrollTop = ctop;
63200 //Roo.log("set scrolltop to ctop DISABLE?");
63201 }else if(cbot > sbot){
63202 //Roo.log("set scrolltop to cbot-ch");
63203 c.scrollTop = cbot-ch;
63206 if(hscroll !== false){
63208 c.scrollLeft = cleft;
63209 }else if(cright > sright){
63210 c.scrollLeft = cright-c.clientWidth;
63217 updateColumns : function(){
63218 this.grid.stopEditing();
63219 var cm = this.grid.colModel, colIds = this.getColumnIds();
63220 //var totalWidth = cm.getTotalWidth();
63222 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63223 //if(cm.isHidden(i)) continue;
63224 var w = cm.getColumnWidth(i);
63225 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63226 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63228 this.updateSplitters();
63231 generateRules : function(cm){
63232 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63233 Roo.util.CSS.removeStyleSheet(rulesId);
63234 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63235 var cid = cm.getColumnId(i);
63237 if(cm.config[i].align){
63238 align = 'text-align:'+cm.config[i].align+';';
63241 if(cm.isHidden(i)){
63242 hidden = 'display:none;';
63244 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63246 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63247 this.hdSelector, cid, " {\n", align, width, "}\n",
63248 this.tdSelector, cid, " {\n",hidden,"\n}\n",
63249 this.splitSelector, cid, " {\n", hidden , "\n}\n");
63251 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63254 updateSplitters : function(){
63255 var cm = this.cm, s = this.getSplitters();
63256 if(s){ // splitters not created yet
63257 var pos = 0, locked = true;
63258 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63259 if(cm.isHidden(i)) {
63262 var w = cm.getColumnWidth(i); // make sure it's a number
63263 if(!cm.isLocked(i) && locked){
63268 s[i].style.left = (pos-this.splitOffset) + "px";
63273 handleHiddenChange : function(colModel, colIndex, hidden){
63275 this.hideColumn(colIndex);
63277 this.unhideColumn(colIndex);
63281 hideColumn : function(colIndex){
63282 var cid = this.getColumnId(colIndex);
63283 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63284 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63286 this.updateHeaders();
63288 this.updateSplitters();
63292 unhideColumn : function(colIndex){
63293 var cid = this.getColumnId(colIndex);
63294 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63295 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63298 this.updateHeaders();
63300 this.updateSplitters();
63304 insertRows : function(dm, firstRow, lastRow, isUpdate){
63305 if(firstRow == 0 && lastRow == dm.getCount()-1){
63309 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63311 var s = this.getScrollState();
63312 var markup = this.renderRows(firstRow, lastRow);
63313 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63314 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63315 this.restoreScroll(s);
63317 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63318 this.syncRowHeights(firstRow, lastRow);
63319 this.stripeRows(firstRow);
63325 bufferRows : function(markup, target, index){
63326 var before = null, trows = target.rows, tbody = target.tBodies[0];
63327 if(index < trows.length){
63328 before = trows[index];
63330 var b = document.createElement("div");
63331 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63332 var rows = b.firstChild.rows;
63333 for(var i = 0, len = rows.length; i < len; i++){
63335 tbody.insertBefore(rows[0], before);
63337 tbody.appendChild(rows[0]);
63344 deleteRows : function(dm, firstRow, lastRow){
63345 if(dm.getRowCount()<1){
63346 this.fireEvent("beforerefresh", this);
63347 this.mainBody.update("");
63348 this.lockedBody.update("");
63349 this.fireEvent("refresh", this);
63351 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63352 var bt = this.getBodyTable();
63353 var tbody = bt.firstChild;
63354 var rows = bt.rows;
63355 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63356 tbody.removeChild(rows[firstRow]);
63358 this.stripeRows(firstRow);
63359 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63363 updateRows : function(dataSource, firstRow, lastRow){
63364 var s = this.getScrollState();
63366 this.restoreScroll(s);
63369 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63373 this.updateHeaderSortState();
63376 getScrollState : function(){
63378 var sb = this.scroller.dom;
63379 return {left: sb.scrollLeft, top: sb.scrollTop};
63382 stripeRows : function(startRow){
63383 if(!this.grid.stripeRows || this.ds.getCount() < 1){
63386 startRow = startRow || 0;
63387 var rows = this.getBodyTable().rows;
63388 var lrows = this.getLockedTable().rows;
63389 var cls = ' x-grid-row-alt ';
63390 for(var i = startRow, len = rows.length; i < len; i++){
63391 var row = rows[i], lrow = lrows[i];
63392 var isAlt = ((i+1) % 2 == 0);
63393 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63394 if(isAlt == hasAlt){
63398 row.className += " x-grid-row-alt";
63400 row.className = row.className.replace("x-grid-row-alt", "");
63403 lrow.className = row.className;
63408 restoreScroll : function(state){
63409 //Roo.log('GridView.restoreScroll');
63410 var sb = this.scroller.dom;
63411 sb.scrollLeft = state.left;
63412 sb.scrollTop = state.top;
63416 syncScroll : function(){
63417 //Roo.log('GridView.syncScroll');
63418 var sb = this.scroller.dom;
63419 var sh = this.mainHd.dom;
63420 var bs = this.mainBody.dom;
63421 var lv = this.lockedBody.dom;
63422 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63423 lv.scrollTop = bs.scrollTop = sb.scrollTop;
63426 handleScroll : function(e){
63428 var sb = this.scroller.dom;
63429 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63433 handleWheel : function(e){
63434 var d = e.getWheelDelta();
63435 this.scroller.dom.scrollTop -= d*22;
63436 // set this here to prevent jumpy scrolling on large tables
63437 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63441 renderRows : function(startRow, endRow){
63442 // pull in all the crap needed to render rows
63443 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63444 var colCount = cm.getColumnCount();
63446 if(ds.getCount() < 1){
63450 // build a map for all the columns
63452 for(var i = 0; i < colCount; i++){
63453 var name = cm.getDataIndex(i);
63455 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63456 renderer : cm.getRenderer(i),
63457 id : cm.getColumnId(i),
63458 locked : cm.isLocked(i),
63459 has_editor : cm.isCellEditable(i)
63463 startRow = startRow || 0;
63464 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63466 // records to render
63467 var rs = ds.getRange(startRow, endRow);
63469 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63472 // As much as I hate to duplicate code, this was branched because FireFox really hates
63473 // [].join("") on strings. The performance difference was substantial enough to
63474 // branch this function
63475 doRender : Roo.isGecko ?
63476 function(cs, rs, ds, startRow, colCount, stripe){
63477 var ts = this.templates, ct = ts.cell, rt = ts.row;
63479 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63481 var hasListener = this.grid.hasListener('rowclass');
63483 for(var j = 0, len = rs.length; j < len; j++){
63484 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63485 for(var i = 0; i < colCount; i++){
63487 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63489 p.css = p.attr = "";
63490 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63491 if(p.value == undefined || p.value === "") {
63492 p.value = " ";
63495 p.css += ' x-grid-editable-cell';
63497 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63498 p.css += ' x-grid-dirty-cell';
63500 var markup = ct.apply(p);
63508 if(stripe && ((rowIndex+1) % 2 == 0)){
63509 alt.push("x-grid-row-alt")
63512 alt.push( " x-grid-dirty-row");
63515 if(this.getRowClass){
63516 alt.push(this.getRowClass(r, rowIndex));
63522 rowIndex : rowIndex,
63525 this.grid.fireEvent('rowclass', this, rowcfg);
63526 alt.push(rowcfg.rowClass);
63528 rp.alt = alt.join(" ");
63529 lbuf+= rt.apply(rp);
63531 buf+= rt.apply(rp);
63533 return [lbuf, buf];
63535 function(cs, rs, ds, startRow, colCount, stripe){
63536 var ts = this.templates, ct = ts.cell, rt = ts.row;
63538 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63539 var hasListener = this.grid.hasListener('rowclass');
63542 for(var j = 0, len = rs.length; j < len; j++){
63543 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63544 for(var i = 0; i < colCount; i++){
63546 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63548 p.css = p.attr = "";
63549 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63550 if(p.value == undefined || p.value === "") {
63551 p.value = " ";
63555 p.css += ' x-grid-editable-cell';
63557 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63558 p.css += ' x-grid-dirty-cell'
63561 var markup = ct.apply(p);
63563 cb[cb.length] = markup;
63565 lcb[lcb.length] = markup;
63569 if(stripe && ((rowIndex+1) % 2 == 0)){
63570 alt.push( "x-grid-row-alt");
63573 alt.push(" x-grid-dirty-row");
63576 if(this.getRowClass){
63577 alt.push( this.getRowClass(r, rowIndex));
63583 rowIndex : rowIndex,
63586 this.grid.fireEvent('rowclass', this, rowcfg);
63587 alt.push(rowcfg.rowClass);
63590 rp.alt = alt.join(" ");
63591 rp.cells = lcb.join("");
63592 lbuf[lbuf.length] = rt.apply(rp);
63593 rp.cells = cb.join("");
63594 buf[buf.length] = rt.apply(rp);
63596 return [lbuf.join(""), buf.join("")];
63599 renderBody : function(){
63600 var markup = this.renderRows();
63601 var bt = this.templates.body;
63602 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63606 * Refreshes the grid
63607 * @param {Boolean} headersToo
63609 refresh : function(headersToo){
63610 this.fireEvent("beforerefresh", this);
63611 this.grid.stopEditing();
63612 var result = this.renderBody();
63613 this.lockedBody.update(result[0]);
63614 this.mainBody.update(result[1]);
63615 if(headersToo === true){
63616 this.updateHeaders();
63617 this.updateColumns();
63618 this.updateSplitters();
63619 this.updateHeaderSortState();
63621 this.syncRowHeights();
63623 this.fireEvent("refresh", this);
63626 handleColumnMove : function(cm, oldIndex, newIndex){
63627 this.indexMap = null;
63628 var s = this.getScrollState();
63629 this.refresh(true);
63630 this.restoreScroll(s);
63631 this.afterMove(newIndex);
63634 afterMove : function(colIndex){
63635 if(this.enableMoveAnim && Roo.enableFx){
63636 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63638 // if multisort - fix sortOrder, and reload..
63639 if (this.grid.dataSource.multiSort) {
63640 // the we can call sort again..
63641 var dm = this.grid.dataSource;
63642 var cm = this.grid.colModel;
63644 for(var i = 0; i < cm.config.length; i++ ) {
63646 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63647 continue; // dont' bother, it's not in sort list or being set.
63650 so.push(cm.config[i].dataIndex);
63653 dm.load(dm.lastOptions);
63660 updateCell : function(dm, rowIndex, dataIndex){
63661 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63662 if(typeof colIndex == "undefined"){ // not present in grid
63665 var cm = this.grid.colModel;
63666 var cell = this.getCell(rowIndex, colIndex);
63667 var cellText = this.getCellText(rowIndex, colIndex);
63670 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63671 id : cm.getColumnId(colIndex),
63672 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63674 var renderer = cm.getRenderer(colIndex);
63675 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63676 if(typeof val == "undefined" || val === "") {
63679 cellText.innerHTML = val;
63680 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63681 this.syncRowHeights(rowIndex, rowIndex);
63684 calcColumnWidth : function(colIndex, maxRowsToMeasure){
63686 if(this.grid.autoSizeHeaders){
63687 var h = this.getHeaderCellMeasure(colIndex);
63688 maxWidth = Math.max(maxWidth, h.scrollWidth);
63691 if(this.cm.isLocked(colIndex)){
63692 tb = this.getLockedTable();
63695 tb = this.getBodyTable();
63696 index = colIndex - this.cm.getLockedCount();
63699 var rows = tb.rows;
63700 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63701 for(var i = 0; i < stopIndex; i++){
63702 var cell = rows[i].childNodes[index].firstChild;
63703 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63706 return maxWidth + /*margin for error in IE*/ 5;
63709 * Autofit a column to its content.
63710 * @param {Number} colIndex
63711 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63713 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63714 if(this.cm.isHidden(colIndex)){
63715 return; // can't calc a hidden column
63718 var cid = this.cm.getColumnId(colIndex);
63719 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63720 if(this.grid.autoSizeHeaders){
63721 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63724 var newWidth = this.calcColumnWidth(colIndex);
63725 this.cm.setColumnWidth(colIndex,
63726 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63727 if(!suppressEvent){
63728 this.grid.fireEvent("columnresize", colIndex, newWidth);
63733 * Autofits all columns to their content and then expands to fit any extra space in the grid
63735 autoSizeColumns : function(){
63736 var cm = this.grid.colModel;
63737 var colCount = cm.getColumnCount();
63738 for(var i = 0; i < colCount; i++){
63739 this.autoSizeColumn(i, true, true);
63741 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63744 this.updateColumns();
63750 * Autofits all columns to the grid's width proportionate with their current size
63751 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63753 fitColumns : function(reserveScrollSpace){
63754 var cm = this.grid.colModel;
63755 var colCount = cm.getColumnCount();
63759 for (i = 0; i < colCount; i++){
63760 if(!cm.isHidden(i) && !cm.isFixed(i)){
63761 w = cm.getColumnWidth(i);
63767 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63768 if(reserveScrollSpace){
63771 var frac = (avail - cm.getTotalWidth())/width;
63772 while (cols.length){
63775 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63777 this.updateColumns();
63781 onRowSelect : function(rowIndex){
63782 var row = this.getRowComposite(rowIndex);
63783 row.addClass("x-grid-row-selected");
63786 onRowDeselect : function(rowIndex){
63787 var row = this.getRowComposite(rowIndex);
63788 row.removeClass("x-grid-row-selected");
63791 onCellSelect : function(row, col){
63792 var cell = this.getCell(row, col);
63794 Roo.fly(cell).addClass("x-grid-cell-selected");
63798 onCellDeselect : function(row, col){
63799 var cell = this.getCell(row, col);
63801 Roo.fly(cell).removeClass("x-grid-cell-selected");
63805 updateHeaderSortState : function(){
63807 // sort state can be single { field: xxx, direction : yyy}
63808 // or { xxx=>ASC , yyy : DESC ..... }
63811 if (!this.ds.multiSort) {
63812 var state = this.ds.getSortState();
63816 mstate[state.field] = state.direction;
63817 // FIXME... - this is not used here.. but might be elsewhere..
63818 this.sortState = state;
63821 mstate = this.ds.sortToggle;
63823 //remove existing sort classes..
63825 var sc = this.sortClasses;
63826 var hds = this.el.select(this.headerSelector).removeClass(sc);
63828 for(var f in mstate) {
63830 var sortColumn = this.cm.findColumnIndex(f);
63832 if(sortColumn != -1){
63833 var sortDir = mstate[f];
63834 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63843 handleHeaderClick : function(g, index,e){
63845 Roo.log("header click");
63848 // touch events on header are handled by context
63849 this.handleHdCtx(g,index,e);
63854 if(this.headersDisabled){
63857 var dm = g.dataSource, cm = g.colModel;
63858 if(!cm.isSortable(index)){
63863 if (dm.multiSort) {
63864 // update the sortOrder
63866 for(var i = 0; i < cm.config.length; i++ ) {
63868 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63869 continue; // dont' bother, it's not in sort list or being set.
63872 so.push(cm.config[i].dataIndex);
63878 dm.sort(cm.getDataIndex(index));
63882 destroy : function(){
63884 this.colMenu.removeAll();
63885 Roo.menu.MenuMgr.unregister(this.colMenu);
63886 this.colMenu.getEl().remove();
63887 delete this.colMenu;
63890 this.hmenu.removeAll();
63891 Roo.menu.MenuMgr.unregister(this.hmenu);
63892 this.hmenu.getEl().remove();
63895 if(this.grid.enableColumnMove){
63896 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63898 for(var dd in dds){
63899 if(!dds[dd].config.isTarget && dds[dd].dragElId){
63900 var elid = dds[dd].dragElId;
63902 Roo.get(elid).remove();
63903 } else if(dds[dd].config.isTarget){
63904 dds[dd].proxyTop.remove();
63905 dds[dd].proxyBottom.remove();
63908 if(Roo.dd.DDM.locationCache[dd]){
63909 delete Roo.dd.DDM.locationCache[dd];
63912 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63915 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63916 this.bind(null, null);
63917 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63920 handleLockChange : function(){
63921 this.refresh(true);
63924 onDenyColumnLock : function(){
63928 onDenyColumnHide : function(){
63932 handleHdMenuClick : function(item){
63933 var index = this.hdCtxIndex;
63934 var cm = this.cm, ds = this.ds;
63937 ds.sort(cm.getDataIndex(index), "ASC");
63940 ds.sort(cm.getDataIndex(index), "DESC");
63943 var lc = cm.getLockedCount();
63944 if(cm.getColumnCount(true) <= lc+1){
63945 this.onDenyColumnLock();
63949 cm.setLocked(index, true, true);
63950 cm.moveColumn(index, lc);
63951 this.grid.fireEvent("columnmove", index, lc);
63953 cm.setLocked(index, true);
63957 var lc = cm.getLockedCount();
63958 if((lc-1) != index){
63959 cm.setLocked(index, false, true);
63960 cm.moveColumn(index, lc-1);
63961 this.grid.fireEvent("columnmove", index, lc-1);
63963 cm.setLocked(index, false);
63966 case 'wider': // used to expand cols on touch..
63968 var cw = cm.getColumnWidth(index);
63969 cw += (item.id == 'wider' ? 1 : -1) * 50;
63970 cw = Math.max(0, cw);
63971 cw = Math.min(cw,4000);
63972 cm.setColumnWidth(index, cw);
63976 index = cm.getIndexById(item.id.substr(4));
63978 if(item.checked && cm.getColumnCount(true) <= 1){
63979 this.onDenyColumnHide();
63982 cm.setHidden(index, item.checked);
63988 beforeColMenuShow : function(){
63989 var cm = this.cm, colCount = cm.getColumnCount();
63990 this.colMenu.removeAll();
63993 for(var i = 0; i < colCount; i++){
63995 id: "col-"+cm.getColumnId(i),
63996 text: cm.getColumnHeader(i),
63997 checked: !cm.isHidden(i),
64002 if (this.grid.sortColMenu) {
64003 items.sort(function(a,b) {
64004 if (a.text == b.text) {
64007 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
64011 for(var i = 0; i < colCount; i++){
64012 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
64016 handleHdCtx : function(g, index, e){
64018 var hd = this.getHeaderCell(index);
64019 this.hdCtxIndex = index;
64020 var ms = this.hmenu.items, cm = this.cm;
64021 ms.get("asc").setDisabled(!cm.isSortable(index));
64022 ms.get("desc").setDisabled(!cm.isSortable(index));
64023 if(this.grid.enableColLock !== false){
64024 ms.get("lock").setDisabled(cm.isLocked(index));
64025 ms.get("unlock").setDisabled(!cm.isLocked(index));
64027 this.hmenu.show(hd, "tl-bl");
64030 handleHdOver : function(e){
64031 var hd = this.findHeaderCell(e.getTarget());
64032 if(hd && !this.headersDisabled){
64033 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
64034 this.fly(hd).addClass("x-grid-hd-over");
64039 handleHdOut : function(e){
64040 var hd = this.findHeaderCell(e.getTarget());
64042 this.fly(hd).removeClass("x-grid-hd-over");
64046 handleSplitDblClick : function(e, t){
64047 var i = this.getCellIndex(t);
64048 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
64049 this.autoSizeColumn(i, true);
64054 render : function(){
64057 var colCount = cm.getColumnCount();
64059 if(this.grid.monitorWindowResize === true){
64060 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
64062 var header = this.renderHeaders();
64063 var body = this.templates.body.apply({rows:""});
64064 var html = this.templates.master.apply({
64067 lockedHeader: header[0],
64071 //this.updateColumns();
64073 this.grid.getGridEl().dom.innerHTML = html;
64075 this.initElements();
64077 // a kludge to fix the random scolling effect in webkit
64078 this.el.on("scroll", function() {
64079 this.el.dom.scrollTop=0; // hopefully not recursive..
64082 this.scroller.on("scroll", this.handleScroll, this);
64083 this.lockedBody.on("mousewheel", this.handleWheel, this);
64084 this.mainBody.on("mousewheel", this.handleWheel, this);
64086 this.mainHd.on("mouseover", this.handleHdOver, this);
64087 this.mainHd.on("mouseout", this.handleHdOut, this);
64088 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
64089 {delegate: "."+this.splitClass});
64091 this.lockedHd.on("mouseover", this.handleHdOver, this);
64092 this.lockedHd.on("mouseout", this.handleHdOut, this);
64093 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
64094 {delegate: "."+this.splitClass});
64096 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
64097 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64100 this.updateSplitters();
64102 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
64103 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64104 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64107 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
64108 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
64110 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
64111 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
64113 if(this.grid.enableColLock !== false){
64114 this.hmenu.add('-',
64115 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
64116 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
64120 this.hmenu.add('-',
64121 {id:"wider", text: this.columnsWiderText},
64122 {id:"narrow", text: this.columnsNarrowText }
64128 if(this.grid.enableColumnHide !== false){
64130 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
64131 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
64132 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
64134 this.hmenu.add('-',
64135 {id:"columns", text: this.columnsText, menu: this.colMenu}
64138 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
64140 this.grid.on("headercontextmenu", this.handleHdCtx, this);
64143 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
64144 this.dd = new Roo.grid.GridDragZone(this.grid, {
64145 ddGroup : this.grid.ddGroup || 'GridDD'
64151 for(var i = 0; i < colCount; i++){
64152 if(cm.isHidden(i)){
64153 this.hideColumn(i);
64155 if(cm.config[i].align){
64156 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
64157 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
64161 this.updateHeaderSortState();
64163 this.beforeInitialResize();
64166 // two part rendering gives faster view to the user
64167 this.renderPhase2.defer(1, this);
64170 renderPhase2 : function(){
64171 // render the rows now
64173 if(this.grid.autoSizeColumns){
64174 this.autoSizeColumns();
64178 beforeInitialResize : function(){
64182 onColumnSplitterMoved : function(i, w){
64183 this.userResized = true;
64184 var cm = this.grid.colModel;
64185 cm.setColumnWidth(i, w, true);
64186 var cid = cm.getColumnId(i);
64187 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64188 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64189 this.updateSplitters();
64191 this.grid.fireEvent("columnresize", i, w);
64194 syncRowHeights : function(startIndex, endIndex){
64195 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
64196 startIndex = startIndex || 0;
64197 var mrows = this.getBodyTable().rows;
64198 var lrows = this.getLockedTable().rows;
64199 var len = mrows.length-1;
64200 endIndex = Math.min(endIndex || len, len);
64201 for(var i = startIndex; i <= endIndex; i++){
64202 var m = mrows[i], l = lrows[i];
64203 var h = Math.max(m.offsetHeight, l.offsetHeight);
64204 m.style.height = l.style.height = h + "px";
64209 layout : function(initialRender, is2ndPass)
64212 var auto = g.autoHeight;
64213 var scrollOffset = 16;
64214 var c = g.getGridEl(), cm = this.cm,
64215 expandCol = g.autoExpandColumn,
64217 //c.beginMeasure();
64219 if(!c.dom.offsetWidth){ // display:none?
64221 this.lockedWrap.show();
64222 this.mainWrap.show();
64227 var hasLock = this.cm.isLocked(0);
64229 var tbh = this.headerPanel.getHeight();
64230 var bbh = this.footerPanel.getHeight();
64233 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64234 var newHeight = ch + c.getBorderWidth("tb");
64236 newHeight = Math.min(g.maxHeight, newHeight);
64238 c.setHeight(newHeight);
64242 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64245 var s = this.scroller;
64247 var csize = c.getSize(true);
64249 this.el.setSize(csize.width, csize.height);
64251 this.headerPanel.setWidth(csize.width);
64252 this.footerPanel.setWidth(csize.width);
64254 var hdHeight = this.mainHd.getHeight();
64255 var vw = csize.width;
64256 var vh = csize.height - (tbh + bbh);
64260 var bt = this.getBodyTable();
64262 if(cm.getLockedCount() == cm.config.length){
64263 bt = this.getLockedTable();
64266 var ltWidth = hasLock ?
64267 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64269 var scrollHeight = bt.offsetHeight;
64270 var scrollWidth = ltWidth + bt.offsetWidth;
64271 var vscroll = false, hscroll = false;
64273 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64275 var lw = this.lockedWrap, mw = this.mainWrap;
64276 var lb = this.lockedBody, mb = this.mainBody;
64278 setTimeout(function(){
64279 var t = s.dom.offsetTop;
64280 var w = s.dom.clientWidth,
64281 h = s.dom.clientHeight;
64284 lw.setSize(ltWidth, h);
64286 mw.setLeftTop(ltWidth, t);
64287 mw.setSize(w-ltWidth, h);
64289 lb.setHeight(h-hdHeight);
64290 mb.setHeight(h-hdHeight);
64292 if(is2ndPass !== true && !gv.userResized && expandCol){
64293 // high speed resize without full column calculation
64295 var ci = cm.getIndexById(expandCol);
64297 ci = cm.findColumnIndex(expandCol);
64299 ci = Math.max(0, ci); // make sure it's got at least the first col.
64300 var expandId = cm.getColumnId(ci);
64301 var tw = cm.getTotalWidth(false);
64302 var currentWidth = cm.getColumnWidth(ci);
64303 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64304 if(currentWidth != cw){
64305 cm.setColumnWidth(ci, cw, true);
64306 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64307 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64308 gv.updateSplitters();
64309 gv.layout(false, true);
64321 onWindowResize : function(){
64322 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64328 appendFooter : function(parentEl){
64332 sortAscText : "Sort Ascending",
64333 sortDescText : "Sort Descending",
64334 lockText : "Lock Column",
64335 unlockText : "Unlock Column",
64336 columnsText : "Columns",
64338 columnsWiderText : "Wider",
64339 columnsNarrowText : "Thinner"
64343 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64344 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64345 this.proxy.el.addClass('x-grid3-col-dd');
64348 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64349 handleMouseDown : function(e){
64353 callHandleMouseDown : function(e){
64354 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64359 * Ext JS Library 1.1.1
64360 * Copyright(c) 2006-2007, Ext JS, LLC.
64362 * Originally Released Under LGPL - original licence link has changed is not relivant.
64365 * <script type="text/javascript">
64368 * @extends Roo.dd.DDProxy
64369 * @class Roo.grid.SplitDragZone
64370 * Support for Column Header resizing
64372 * @param {Object} config
64375 // This is a support class used internally by the Grid components
64376 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64378 this.view = grid.getView();
64379 this.proxy = this.view.resizeProxy;
64380 Roo.grid.SplitDragZone.superclass.constructor.call(
64383 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64385 dragElId : Roo.id(this.proxy.dom),
64390 this.setHandleElId(Roo.id(hd));
64391 if (hd2 !== false) {
64392 this.setOuterHandleElId(Roo.id(hd2));
64395 this.scroll = false;
64397 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64398 fly: Roo.Element.fly,
64400 b4StartDrag : function(x, y){
64401 this.view.headersDisabled = true;
64402 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64403 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64405 this.proxy.setHeight(h);
64407 // for old system colWidth really stored the actual width?
64408 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64409 // which in reality did not work.. - it worked only for fixed sizes
64410 // for resizable we need to use actual sizes.
64411 var w = this.cm.getColumnWidth(this.cellIndex);
64412 if (!this.view.mainWrap) {
64414 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64419 // this was w-this.grid.minColumnWidth;
64420 // doesnt really make sense? - w = thie curren width or the rendered one?
64421 var minw = Math.max(w-this.grid.minColumnWidth, 0);
64422 this.resetConstraints();
64423 this.setXConstraint(minw, 1000);
64424 this.setYConstraint(0, 0);
64425 this.minX = x - minw;
64426 this.maxX = x + 1000;
64428 if (!this.view.mainWrap) { // this is Bootstrap code..
64429 this.getDragEl().style.display='block';
64432 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64436 handleMouseDown : function(e){
64437 ev = Roo.EventObject.setEvent(e);
64438 var t = this.fly(ev.getTarget());
64439 if(t.hasClass("x-grid-split")){
64440 this.cellIndex = this.view.getCellIndex(t.dom);
64441 this.split = t.dom;
64442 this.cm = this.grid.colModel;
64443 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64444 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64449 endDrag : function(e){
64450 this.view.headersDisabled = false;
64451 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64452 var diff = endX - this.startPos;
64454 var w = this.cm.getColumnWidth(this.cellIndex);
64455 if (!this.view.mainWrap) {
64458 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64461 autoOffset : function(){
64462 this.setDelta(0,0);
64466 * Ext JS Library 1.1.1
64467 * Copyright(c) 2006-2007, Ext JS, LLC.
64469 * Originally Released Under LGPL - original licence link has changed is not relivant.
64472 * <script type="text/javascript">
64476 // This is a support class used internally by the Grid components
64477 Roo.grid.GridDragZone = function(grid, config){
64478 this.view = grid.getView();
64479 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64480 if(this.view.lockedBody){
64481 this.setHandleElId(Roo.id(this.view.mainBody.dom));
64482 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64484 this.scroll = false;
64486 this.ddel = document.createElement('div');
64487 this.ddel.className = 'x-grid-dd-wrap';
64490 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64491 ddGroup : "GridDD",
64493 getDragData : function(e){
64494 var t = Roo.lib.Event.getTarget(e);
64495 var rowIndex = this.view.findRowIndex(t);
64496 var sm = this.grid.selModel;
64498 //Roo.log(rowIndex);
64500 if (sm.getSelectedCell) {
64501 // cell selection..
64502 if (!sm.getSelectedCell()) {
64505 if (rowIndex != sm.getSelectedCell()[0]) {
64510 if (sm.getSelections && sm.getSelections().length < 1) {
64515 // before it used to all dragging of unseleted... - now we dont do that.
64516 if(rowIndex !== false){
64521 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64523 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64526 if (e.hasModifier()){
64527 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64530 Roo.log("getDragData");
64535 rowIndex: rowIndex,
64536 selections: sm.getSelections ? sm.getSelections() : (
64537 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64544 onInitDrag : function(e){
64545 var data = this.dragData;
64546 this.ddel.innerHTML = this.grid.getDragDropText();
64547 this.proxy.update(this.ddel);
64548 // fire start drag?
64551 afterRepair : function(){
64552 this.dragging = false;
64555 getRepairXY : function(e, data){
64559 onEndDrag : function(data, e){
64563 onValidDrop : function(dd, e, id){
64568 beforeInvalidDrop : function(e, id){
64573 * Ext JS Library 1.1.1
64574 * Copyright(c) 2006-2007, Ext JS, LLC.
64576 * Originally Released Under LGPL - original licence link has changed is not relivant.
64579 * <script type="text/javascript">
64584 * @class Roo.grid.ColumnModel
64585 * @extends Roo.util.Observable
64586 * This is the default implementation of a ColumnModel used by the Grid. It defines
64587 * the columns in the grid.
64590 var colModel = new Roo.grid.ColumnModel([
64591 {header: "Ticker", width: 60, sortable: true, locked: true},
64592 {header: "Company Name", width: 150, sortable: true},
64593 {header: "Market Cap.", width: 100, sortable: true},
64594 {header: "$ Sales", width: 100, sortable: true, renderer: money},
64595 {header: "Employees", width: 100, sortable: true, resizable: false}
64600 * The config options listed for this class are options which may appear in each
64601 * individual column definition.
64602 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64604 * @param {Object} config An Array of column config objects. See this class's
64605 * config objects for details.
64607 Roo.grid.ColumnModel = function(config){
64609 * The config passed into the constructor
64611 this.config = []; //config;
64614 // if no id, create one
64615 // if the column does not have a dataIndex mapping,
64616 // map it to the order it is in the config
64617 for(var i = 0, len = config.length; i < len; i++){
64618 this.addColumn(config[i]);
64623 * The width of columns which have no width specified (defaults to 100)
64626 this.defaultWidth = 100;
64629 * Default sortable of columns which have no sortable specified (defaults to false)
64632 this.defaultSortable = false;
64636 * @event widthchange
64637 * Fires when the width of a column changes.
64638 * @param {ColumnModel} this
64639 * @param {Number} columnIndex The column index
64640 * @param {Number} newWidth The new width
64642 "widthchange": true,
64644 * @event headerchange
64645 * Fires when the text of a header changes.
64646 * @param {ColumnModel} this
64647 * @param {Number} columnIndex The column index
64648 * @param {Number} newText The new header text
64650 "headerchange": true,
64652 * @event hiddenchange
64653 * Fires when a column is hidden or "unhidden".
64654 * @param {ColumnModel} this
64655 * @param {Number} columnIndex The column index
64656 * @param {Boolean} hidden true if hidden, false otherwise
64658 "hiddenchange": true,
64660 * @event columnmoved
64661 * Fires when a column is moved.
64662 * @param {ColumnModel} this
64663 * @param {Number} oldIndex
64664 * @param {Number} newIndex
64666 "columnmoved" : true,
64668 * @event columlockchange
64669 * Fires when a column's locked state is changed
64670 * @param {ColumnModel} this
64671 * @param {Number} colIndex
64672 * @param {Boolean} locked true if locked
64674 "columnlockchange" : true
64676 Roo.grid.ColumnModel.superclass.constructor.call(this);
64678 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64680 * @cfg {String} header [required] The header text to display in the Grid view.
64683 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64686 * @cfg {String} smHeader Header at Bootsrap Small width
64689 * @cfg {String} mdHeader Header at Bootsrap Medium width
64692 * @cfg {String} lgHeader Header at Bootsrap Large width
64695 * @cfg {String} xlHeader Header at Bootsrap extra Large width
64698 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
64699 * {@link Roo.data.Record} definition from which to draw the column's value. If not
64700 * specified, the column's index is used as an index into the Record's data Array.
64703 * @cfg {Number} width The initial width in pixels of the column. Using this
64704 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64707 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
64708 * Defaults to the value of the {@link #defaultSortable} property.
64709 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64712 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
64715 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
64718 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
64721 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
64724 * @cfg {Function} renderer A function used to generate HTML markup for a cell
64725 * given the cell's data value. See {@link #setRenderer}. If not specified, the
64726 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64727 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64730 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
64733 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
64736 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
64739 * @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)
64742 * @cfg {String} tooltip mouse over tooltip text
64745 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
64748 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
64751 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
64754 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
64757 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
64760 * Returns the id of the column at the specified index.
64761 * @param {Number} index The column index
64762 * @return {String} the id
64764 getColumnId : function(index){
64765 return this.config[index].id;
64769 * Returns the column for a specified id.
64770 * @param {String} id The column id
64771 * @return {Object} the column
64773 getColumnById : function(id){
64774 return this.lookup[id];
64779 * Returns the column Object for a specified dataIndex.
64780 * @param {String} dataIndex The column dataIndex
64781 * @return {Object|Boolean} the column or false if not found
64783 getColumnByDataIndex: function(dataIndex){
64784 var index = this.findColumnIndex(dataIndex);
64785 return index > -1 ? this.config[index] : false;
64789 * Returns the index for a specified column id.
64790 * @param {String} id The column id
64791 * @return {Number} the index, or -1 if not found
64793 getIndexById : function(id){
64794 for(var i = 0, len = this.config.length; i < len; i++){
64795 if(this.config[i].id == id){
64803 * Returns the index for a specified column dataIndex.
64804 * @param {String} dataIndex The column dataIndex
64805 * @return {Number} the index, or -1 if not found
64808 findColumnIndex : function(dataIndex){
64809 for(var i = 0, len = this.config.length; i < len; i++){
64810 if(this.config[i].dataIndex == dataIndex){
64818 moveColumn : function(oldIndex, newIndex){
64819 var c = this.config[oldIndex];
64820 this.config.splice(oldIndex, 1);
64821 this.config.splice(newIndex, 0, c);
64822 this.dataMap = null;
64823 this.fireEvent("columnmoved", this, oldIndex, newIndex);
64826 isLocked : function(colIndex){
64827 return this.config[colIndex].locked === true;
64830 setLocked : function(colIndex, value, suppressEvent){
64831 if(this.isLocked(colIndex) == value){
64834 this.config[colIndex].locked = value;
64835 if(!suppressEvent){
64836 this.fireEvent("columnlockchange", this, colIndex, value);
64840 getTotalLockedWidth : function(){
64841 var totalWidth = 0;
64842 for(var i = 0; i < this.config.length; i++){
64843 if(this.isLocked(i) && !this.isHidden(i)){
64844 this.totalWidth += this.getColumnWidth(i);
64850 getLockedCount : function(){
64851 for(var i = 0, len = this.config.length; i < len; i++){
64852 if(!this.isLocked(i)){
64857 return this.config.length;
64861 * Returns the number of columns.
64864 getColumnCount : function(visibleOnly){
64865 if(visibleOnly === true){
64867 for(var i = 0, len = this.config.length; i < len; i++){
64868 if(!this.isHidden(i)){
64874 return this.config.length;
64878 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64879 * @param {Function} fn
64880 * @param {Object} scope (optional)
64881 * @return {Array} result
64883 getColumnsBy : function(fn, scope){
64885 for(var i = 0, len = this.config.length; i < len; i++){
64886 var c = this.config[i];
64887 if(fn.call(scope||this, c, i) === true){
64895 * Returns true if the specified column is sortable.
64896 * @param {Number} col The column index
64897 * @return {Boolean}
64899 isSortable : function(col){
64900 if(typeof this.config[col].sortable == "undefined"){
64901 return this.defaultSortable;
64903 return this.config[col].sortable;
64907 * Returns the rendering (formatting) function defined for the column.
64908 * @param {Number} col The column index.
64909 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64911 getRenderer : function(col){
64912 if(!this.config[col].renderer){
64913 return Roo.grid.ColumnModel.defaultRenderer;
64915 return this.config[col].renderer;
64919 * Sets the rendering (formatting) function for a column.
64920 * @param {Number} col The column index
64921 * @param {Function} fn The function to use to process the cell's raw data
64922 * to return HTML markup for the grid view. The render function is called with
64923 * the following parameters:<ul>
64924 * <li>Data value.</li>
64925 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64926 * <li>css A CSS style string to apply to the table cell.</li>
64927 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
64928 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
64929 * <li>Row index</li>
64930 * <li>Column index</li>
64931 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
64933 setRenderer : function(col, fn){
64934 this.config[col].renderer = fn;
64938 * Returns the width for the specified column.
64939 * @param {Number} col The column index
64940 * @param (optional) {String} gridSize bootstrap width size.
64943 getColumnWidth : function(col, gridSize)
64945 var cfg = this.config[col];
64947 if (typeof(gridSize) == 'undefined') {
64948 return cfg.width * 1 || this.defaultWidth;
64950 if (gridSize === false) { // if we set it..
64951 return cfg.width || false;
64953 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
64955 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
64956 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
64959 return cfg[ sizes[i] ];
64966 * Sets the width for a column.
64967 * @param {Number} col The column index
64968 * @param {Number} width The new width
64970 setColumnWidth : function(col, width, suppressEvent){
64971 this.config[col].width = width;
64972 this.totalWidth = null;
64973 if(!suppressEvent){
64974 this.fireEvent("widthchange", this, col, width);
64979 * Returns the total width of all columns.
64980 * @param {Boolean} includeHidden True to include hidden column widths
64983 getTotalWidth : function(includeHidden){
64984 if(!this.totalWidth){
64985 this.totalWidth = 0;
64986 for(var i = 0, len = this.config.length; i < len; i++){
64987 if(includeHidden || !this.isHidden(i)){
64988 this.totalWidth += this.getColumnWidth(i);
64992 return this.totalWidth;
64996 * Returns the header for the specified column.
64997 * @param {Number} col The column index
65000 getColumnHeader : function(col){
65001 return this.config[col].header;
65005 * Sets the header for a column.
65006 * @param {Number} col The column index
65007 * @param {String} header The new header
65009 setColumnHeader : function(col, header){
65010 this.config[col].header = header;
65011 this.fireEvent("headerchange", this, col, header);
65015 * Returns the tooltip for the specified column.
65016 * @param {Number} col The column index
65019 getColumnTooltip : function(col){
65020 return this.config[col].tooltip;
65023 * Sets the tooltip for a column.
65024 * @param {Number} col The column index
65025 * @param {String} tooltip The new tooltip
65027 setColumnTooltip : function(col, tooltip){
65028 this.config[col].tooltip = tooltip;
65032 * Returns the dataIndex for the specified column.
65033 * @param {Number} col The column index
65036 getDataIndex : function(col){
65037 return this.config[col].dataIndex;
65041 * Sets the dataIndex for a column.
65042 * @param {Number} col The column index
65043 * @param {Number} dataIndex The new dataIndex
65045 setDataIndex : function(col, dataIndex){
65046 this.config[col].dataIndex = dataIndex;
65052 * Returns true if the cell is editable.
65053 * @param {Number} colIndex The column index
65054 * @param {Number} rowIndex The row index - this is nto actually used..?
65055 * @return {Boolean}
65057 isCellEditable : function(colIndex, rowIndex){
65058 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
65062 * Returns the editor defined for the cell/column.
65063 * return false or null to disable editing.
65064 * @param {Number} colIndex The column index
65065 * @param {Number} rowIndex The row index
65068 getCellEditor : function(colIndex, rowIndex){
65069 return this.config[colIndex].editor;
65073 * Sets if a column is editable.
65074 * @param {Number} col The column index
65075 * @param {Boolean} editable True if the column is editable
65077 setEditable : function(col, editable){
65078 this.config[col].editable = editable;
65083 * Returns true if the column is hidden.
65084 * @param {Number} colIndex The column index
65085 * @return {Boolean}
65087 isHidden : function(colIndex){
65088 return this.config[colIndex].hidden;
65093 * Returns true if the column width cannot be changed
65095 isFixed : function(colIndex){
65096 return this.config[colIndex].fixed;
65100 * Returns true if the column can be resized
65101 * @return {Boolean}
65103 isResizable : function(colIndex){
65104 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
65107 * Sets if a column is hidden.
65108 * @param {Number} colIndex The column index
65109 * @param {Boolean} hidden True if the column is hidden
65111 setHidden : function(colIndex, hidden){
65112 this.config[colIndex].hidden = hidden;
65113 this.totalWidth = null;
65114 this.fireEvent("hiddenchange", this, colIndex, hidden);
65118 * Sets the editor for a column.
65119 * @param {Number} col The column index
65120 * @param {Object} editor The editor object
65122 setEditor : function(col, editor){
65123 this.config[col].editor = editor;
65126 * Add a column (experimental...) - defaults to adding to the end..
65127 * @param {Object} config
65129 addColumn : function(c)
65132 var i = this.config.length;
65133 this.config[i] = c;
65135 if(typeof c.dataIndex == "undefined"){
65138 if(typeof c.renderer == "string"){
65139 c.renderer = Roo.util.Format[c.renderer];
65141 if(typeof c.id == "undefined"){
65144 if(c.editor && c.editor.xtype){
65145 c.editor = Roo.factory(c.editor, Roo.grid);
65147 if(c.editor && c.editor.isFormField){
65148 c.editor = new Roo.grid.GridEditor(c.editor);
65150 this.lookup[c.id] = c;
65155 Roo.grid.ColumnModel.defaultRenderer = function(value)
65157 if(typeof value == "object") {
65160 if(typeof value == "string" && value.length < 1){
65164 return String.format("{0}", value);
65167 // Alias for backwards compatibility
65168 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
65171 * Ext JS Library 1.1.1
65172 * Copyright(c) 2006-2007, Ext JS, LLC.
65174 * Originally Released Under LGPL - original licence link has changed is not relivant.
65177 * <script type="text/javascript">
65181 * @class Roo.grid.AbstractSelectionModel
65182 * @extends Roo.util.Observable
65184 * Abstract base class for grid SelectionModels. It provides the interface that should be
65185 * implemented by descendant classes. This class should not be directly instantiated.
65188 Roo.grid.AbstractSelectionModel = function(){
65189 this.locked = false;
65190 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
65193 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
65194 /** @ignore Called by the grid automatically. Do not call directly. */
65195 init : function(grid){
65201 * Locks the selections.
65204 this.locked = true;
65208 * Unlocks the selections.
65210 unlock : function(){
65211 this.locked = false;
65215 * Returns true if the selections are locked.
65216 * @return {Boolean}
65218 isLocked : function(){
65219 return this.locked;
65223 * Ext JS Library 1.1.1
65224 * Copyright(c) 2006-2007, Ext JS, LLC.
65226 * Originally Released Under LGPL - original licence link has changed is not relivant.
65229 * <script type="text/javascript">
65232 * @extends Roo.grid.AbstractSelectionModel
65233 * @class Roo.grid.RowSelectionModel
65234 * The default SelectionModel used by {@link Roo.grid.Grid}.
65235 * It supports multiple selections and keyboard selection/navigation.
65237 * @param {Object} config
65239 Roo.grid.RowSelectionModel = function(config){
65240 Roo.apply(this, config);
65241 this.selections = new Roo.util.MixedCollection(false, function(o){
65246 this.lastActive = false;
65250 * @event selectionchange
65251 * Fires when the selection changes
65252 * @param {SelectionModel} this
65254 "selectionchange" : true,
65256 * @event afterselectionchange
65257 * Fires after the selection changes (eg. by key press or clicking)
65258 * @param {SelectionModel} this
65260 "afterselectionchange" : true,
65262 * @event beforerowselect
65263 * Fires when a row is selected being selected, return false to cancel.
65264 * @param {SelectionModel} this
65265 * @param {Number} rowIndex The selected index
65266 * @param {Boolean} keepExisting False if other selections will be cleared
65268 "beforerowselect" : true,
65271 * Fires when a row is selected.
65272 * @param {SelectionModel} this
65273 * @param {Number} rowIndex The selected index
65274 * @param {Roo.data.Record} r The record
65276 "rowselect" : true,
65278 * @event rowdeselect
65279 * Fires when a row is deselected.
65280 * @param {SelectionModel} this
65281 * @param {Number} rowIndex The selected index
65283 "rowdeselect" : true
65285 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65286 this.locked = false;
65289 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
65291 * @cfg {Boolean} singleSelect
65292 * True to allow selection of only one row at a time (defaults to false)
65294 singleSelect : false,
65297 initEvents : function(){
65299 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65300 this.grid.on("mousedown", this.handleMouseDown, this);
65301 }else{ // allow click to work like normal
65302 this.grid.on("rowclick", this.handleDragableRowClick, this);
65304 // bootstrap does not have a view..
65305 var view = this.grid.view ? this.grid.view : this.grid;
65306 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65307 "up" : function(e){
65309 this.selectPrevious(e.shiftKey);
65310 }else if(this.last !== false && this.lastActive !== false){
65311 var last = this.last;
65312 this.selectRange(this.last, this.lastActive-1);
65313 view.focusRow(this.lastActive);
65314 if(last !== false){
65318 this.selectFirstRow();
65320 this.fireEvent("afterselectionchange", this);
65322 "down" : function(e){
65324 this.selectNext(e.shiftKey);
65325 }else if(this.last !== false && this.lastActive !== false){
65326 var last = this.last;
65327 this.selectRange(this.last, this.lastActive+1);
65328 view.focusRow(this.lastActive);
65329 if(last !== false){
65333 this.selectFirstRow();
65335 this.fireEvent("afterselectionchange", this);
65341 view.on("refresh", this.onRefresh, this);
65342 view.on("rowupdated", this.onRowUpdated, this);
65343 view.on("rowremoved", this.onRemove, this);
65347 onRefresh : function(){
65348 var ds = this.grid.ds, i, v = this.grid.view;
65349 var s = this.selections;
65350 s.each(function(r){
65351 if((i = ds.indexOfId(r.id)) != -1){
65353 s.add(ds.getAt(i)); // updating the selection relate data
65361 onRemove : function(v, index, r){
65362 this.selections.remove(r);
65366 onRowUpdated : function(v, index, r){
65367 if(this.isSelected(r)){
65368 v.onRowSelect(index);
65374 * @param {Array} records The records to select
65375 * @param {Boolean} keepExisting (optional) True to keep existing selections
65377 selectRecords : function(records, keepExisting){
65379 this.clearSelections();
65381 var ds = this.grid.ds;
65382 for(var i = 0, len = records.length; i < len; i++){
65383 this.selectRow(ds.indexOf(records[i]), true);
65388 * Gets the number of selected rows.
65391 getCount : function(){
65392 return this.selections.length;
65396 * Selects the first row in the grid.
65398 selectFirstRow : function(){
65403 * Select the last row.
65404 * @param {Boolean} keepExisting (optional) True to keep existing selections
65406 selectLastRow : function(keepExisting){
65407 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65411 * Selects the row immediately following the last selected row.
65412 * @param {Boolean} keepExisting (optional) True to keep existing selections
65414 selectNext : function(keepExisting){
65415 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65416 this.selectRow(this.last+1, keepExisting);
65417 var view = this.grid.view ? this.grid.view : this.grid;
65418 view.focusRow(this.last);
65423 * Selects the row that precedes the last selected row.
65424 * @param {Boolean} keepExisting (optional) True to keep existing selections
65426 selectPrevious : function(keepExisting){
65428 this.selectRow(this.last-1, keepExisting);
65429 var view = this.grid.view ? this.grid.view : this.grid;
65430 view.focusRow(this.last);
65435 * Returns the selected records
65436 * @return {Array} Array of selected records
65438 getSelections : function(){
65439 return [].concat(this.selections.items);
65443 * Returns the first selected record.
65446 getSelected : function(){
65447 return this.selections.itemAt(0);
65452 * Clears all selections.
65454 clearSelections : function(fast){
65459 var ds = this.grid.ds;
65460 var s = this.selections;
65461 s.each(function(r){
65462 this.deselectRow(ds.indexOfId(r.id));
65466 this.selections.clear();
65473 * Selects all rows.
65475 selectAll : function(){
65479 this.selections.clear();
65480 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65481 this.selectRow(i, true);
65486 * Returns True if there is a selection.
65487 * @return {Boolean}
65489 hasSelection : function(){
65490 return this.selections.length > 0;
65494 * Returns True if the specified row is selected.
65495 * @param {Number/Record} record The record or index of the record to check
65496 * @return {Boolean}
65498 isSelected : function(index){
65499 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65500 return (r && this.selections.key(r.id) ? true : false);
65504 * Returns True if the specified record id is selected.
65505 * @param {String} id The id of record to check
65506 * @return {Boolean}
65508 isIdSelected : function(id){
65509 return (this.selections.key(id) ? true : false);
65513 handleMouseDown : function(e, t)
65515 var view = this.grid.view ? this.grid.view : this.grid;
65517 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65520 if(e.shiftKey && this.last !== false){
65521 var last = this.last;
65522 this.selectRange(last, rowIndex, e.ctrlKey);
65523 this.last = last; // reset the last
65524 view.focusRow(rowIndex);
65526 var isSelected = this.isSelected(rowIndex);
65527 if(e.button !== 0 && isSelected){
65528 view.focusRow(rowIndex);
65529 }else if(e.ctrlKey && isSelected){
65530 this.deselectRow(rowIndex);
65531 }else if(!isSelected){
65532 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65533 view.focusRow(rowIndex);
65536 this.fireEvent("afterselectionchange", this);
65539 handleDragableRowClick : function(grid, rowIndex, e)
65541 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65542 this.selectRow(rowIndex, false);
65543 var view = this.grid.view ? this.grid.view : this.grid;
65544 view.focusRow(rowIndex);
65545 this.fireEvent("afterselectionchange", this);
65550 * Selects multiple rows.
65551 * @param {Array} rows Array of the indexes of the row to select
65552 * @param {Boolean} keepExisting (optional) True to keep existing selections
65554 selectRows : function(rows, keepExisting){
65556 this.clearSelections();
65558 for(var i = 0, len = rows.length; i < len; i++){
65559 this.selectRow(rows[i], true);
65564 * Selects a range of rows. All rows in between startRow and endRow are also selected.
65565 * @param {Number} startRow The index of the first row in the range
65566 * @param {Number} endRow The index of the last row in the range
65567 * @param {Boolean} keepExisting (optional) True to retain existing selections
65569 selectRange : function(startRow, endRow, keepExisting){
65574 this.clearSelections();
65576 if(startRow <= endRow){
65577 for(var i = startRow; i <= endRow; i++){
65578 this.selectRow(i, true);
65581 for(var i = startRow; i >= endRow; i--){
65582 this.selectRow(i, true);
65588 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65589 * @param {Number} startRow The index of the first row in the range
65590 * @param {Number} endRow The index of the last row in the range
65592 deselectRange : function(startRow, endRow, preventViewNotify){
65596 for(var i = startRow; i <= endRow; i++){
65597 this.deselectRow(i, preventViewNotify);
65603 * @param {Number} row The index of the row to select
65604 * @param {Boolean} keepExisting (optional) True to keep existing selections
65606 selectRow : function(index, keepExisting, preventViewNotify){
65607 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65610 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65611 if(!keepExisting || this.singleSelect){
65612 this.clearSelections();
65614 var r = this.grid.ds.getAt(index);
65615 this.selections.add(r);
65616 this.last = this.lastActive = index;
65617 if(!preventViewNotify){
65618 var view = this.grid.view ? this.grid.view : this.grid;
65619 view.onRowSelect(index);
65621 this.fireEvent("rowselect", this, index, r);
65622 this.fireEvent("selectionchange", this);
65628 * @param {Number} row The index of the row to deselect
65630 deselectRow : function(index, preventViewNotify){
65634 if(this.last == index){
65637 if(this.lastActive == index){
65638 this.lastActive = false;
65640 var r = this.grid.ds.getAt(index);
65641 this.selections.remove(r);
65642 if(!preventViewNotify){
65643 var view = this.grid.view ? this.grid.view : this.grid;
65644 view.onRowDeselect(index);
65646 this.fireEvent("rowdeselect", this, index);
65647 this.fireEvent("selectionchange", this);
65651 restoreLast : function(){
65653 this.last = this._last;
65658 acceptsNav : function(row, col, cm){
65659 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65663 onEditorKey : function(field, e){
65664 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65669 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65671 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65673 }else if(k == e.ENTER && !e.ctrlKey){
65677 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65679 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65681 }else if(k == e.ESC){
65685 g.startEditing(newCell[0], newCell[1]);
65690 * Ext JS Library 1.1.1
65691 * Copyright(c) 2006-2007, Ext JS, LLC.
65693 * Originally Released Under LGPL - original licence link has changed is not relivant.
65696 * <script type="text/javascript">
65699 * @class Roo.grid.CellSelectionModel
65700 * @extends Roo.grid.AbstractSelectionModel
65701 * This class provides the basic implementation for cell selection in a grid.
65703 * @param {Object} config The object containing the configuration of this model.
65704 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65706 Roo.grid.CellSelectionModel = function(config){
65707 Roo.apply(this, config);
65709 this.selection = null;
65713 * @event beforerowselect
65714 * Fires before a cell is selected.
65715 * @param {SelectionModel} this
65716 * @param {Number} rowIndex The selected row index
65717 * @param {Number} colIndex The selected cell index
65719 "beforecellselect" : true,
65721 * @event cellselect
65722 * Fires when a cell is selected.
65723 * @param {SelectionModel} this
65724 * @param {Number} rowIndex The selected row index
65725 * @param {Number} colIndex The selected cell index
65727 "cellselect" : true,
65729 * @event selectionchange
65730 * Fires when the active selection changes.
65731 * @param {SelectionModel} this
65732 * @param {Object} selection null for no selection or an object (o) with two properties
65734 <li>o.record: the record object for the row the selection is in</li>
65735 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65738 "selectionchange" : true,
65741 * Fires when the tab (or enter) was pressed on the last editable cell
65742 * You can use this to trigger add new row.
65743 * @param {SelectionModel} this
65747 * @event beforeeditnext
65748 * Fires before the next editable sell is made active
65749 * You can use this to skip to another cell or fire the tabend
65750 * if you set cell to false
65751 * @param {Object} eventdata object : { cell : [ row, col ] }
65753 "beforeeditnext" : true
65755 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65758 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
65760 enter_is_tab: false,
65763 initEvents : function(){
65764 this.grid.on("mousedown", this.handleMouseDown, this);
65765 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65766 var view = this.grid.view;
65767 view.on("refresh", this.onViewChange, this);
65768 view.on("rowupdated", this.onRowUpdated, this);
65769 view.on("beforerowremoved", this.clearSelections, this);
65770 view.on("beforerowsinserted", this.clearSelections, this);
65771 if(this.grid.isEditor){
65772 this.grid.on("beforeedit", this.beforeEdit, this);
65777 beforeEdit : function(e){
65778 this.select(e.row, e.column, false, true, e.record);
65782 onRowUpdated : function(v, index, r){
65783 if(this.selection && this.selection.record == r){
65784 v.onCellSelect(index, this.selection.cell[1]);
65789 onViewChange : function(){
65790 this.clearSelections(true);
65794 * Returns the currently selected cell,.
65795 * @return {Array} The selected cell (row, column) or null if none selected.
65797 getSelectedCell : function(){
65798 return this.selection ? this.selection.cell : null;
65802 * Clears all selections.
65803 * @param {Boolean} true to prevent the gridview from being notified about the change.
65805 clearSelections : function(preventNotify){
65806 var s = this.selection;
65808 if(preventNotify !== true){
65809 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65811 this.selection = null;
65812 this.fireEvent("selectionchange", this, null);
65817 * Returns true if there is a selection.
65818 * @return {Boolean}
65820 hasSelection : function(){
65821 return this.selection ? true : false;
65825 handleMouseDown : function(e, t){
65826 var v = this.grid.getView();
65827 if(this.isLocked()){
65830 var row = v.findRowIndex(t);
65831 var cell = v.findCellIndex(t);
65832 if(row !== false && cell !== false){
65833 this.select(row, cell);
65839 * @param {Number} rowIndex
65840 * @param {Number} collIndex
65842 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65843 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65844 this.clearSelections();
65845 r = r || this.grid.dataSource.getAt(rowIndex);
65848 cell : [rowIndex, colIndex]
65850 if(!preventViewNotify){
65851 var v = this.grid.getView();
65852 v.onCellSelect(rowIndex, colIndex);
65853 if(preventFocus !== true){
65854 v.focusCell(rowIndex, colIndex);
65857 this.fireEvent("cellselect", this, rowIndex, colIndex);
65858 this.fireEvent("selectionchange", this, this.selection);
65863 isSelectable : function(rowIndex, colIndex, cm){
65864 return !cm.isHidden(colIndex);
65868 handleKeyDown : function(e){
65869 //Roo.log('Cell Sel Model handleKeyDown');
65870 if(!e.isNavKeyPress()){
65873 var g = this.grid, s = this.selection;
65876 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
65878 this.select(cell[0], cell[1]);
65883 var walk = function(row, col, step){
65884 return g.walkCells(row, col, step, sm.isSelectable, sm);
65886 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65893 // handled by onEditorKey
65894 if (g.isEditor && g.editing) {
65898 newCell = walk(r, c-1, -1);
65900 newCell = walk(r, c+1, 1);
65905 newCell = walk(r+1, c, 1);
65909 newCell = walk(r-1, c, -1);
65913 newCell = walk(r, c+1, 1);
65917 newCell = walk(r, c-1, -1);
65922 if(g.isEditor && !g.editing){
65923 g.startEditing(r, c);
65932 this.select(newCell[0], newCell[1]);
65938 acceptsNav : function(row, col, cm){
65939 return !cm.isHidden(col) && cm.isCellEditable(col, row);
65943 * @param {Number} field (not used) - as it's normally used as a listener
65944 * @param {Number} e - event - fake it by using
65946 * var e = Roo.EventObjectImpl.prototype;
65947 * e.keyCode = e.TAB
65951 onEditorKey : function(field, e){
65953 var k = e.getKey(),
65956 ed = g.activeEditor,
65958 ///Roo.log('onEditorKey' + k);
65961 if (this.enter_is_tab && k == e.ENTER) {
65967 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65969 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65975 } else if(k == e.ENTER && !e.ctrlKey){
65978 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65980 } else if(k == e.ESC){
65985 var ecall = { cell : newCell, forward : forward };
65986 this.fireEvent('beforeeditnext', ecall );
65987 newCell = ecall.cell;
65988 forward = ecall.forward;
65992 //Roo.log('next cell after edit');
65993 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
65994 } else if (forward) {
65995 // tabbed past last
65996 this.fireEvent.defer(100, this, ['tabend',this]);
66001 * Ext JS Library 1.1.1
66002 * Copyright(c) 2006-2007, Ext JS, LLC.
66004 * Originally Released Under LGPL - original licence link has changed is not relivant.
66007 * <script type="text/javascript">
66011 * @class Roo.grid.EditorGrid
66012 * @extends Roo.grid.Grid
66013 * Class for creating and editable grid.
66014 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66015 * The container MUST have some type of size defined for the grid to fill. The container will be
66016 * automatically set to position relative if it isn't already.
66017 * @param {Object} dataSource The data model to bind to
66018 * @param {Object} colModel The column model with info about this grid's columns
66020 Roo.grid.EditorGrid = function(container, config){
66021 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
66022 this.getGridEl().addClass("xedit-grid");
66024 if(!this.selModel){
66025 this.selModel = new Roo.grid.CellSelectionModel();
66028 this.activeEditor = null;
66032 * @event beforeedit
66033 * Fires before cell editing is triggered. The edit event object has the following properties <br />
66034 * <ul style="padding:5px;padding-left:16px;">
66035 * <li>grid - This grid</li>
66036 * <li>record - The record being edited</li>
66037 * <li>field - The field name being edited</li>
66038 * <li>value - The value for the field being edited.</li>
66039 * <li>row - The grid row index</li>
66040 * <li>column - The grid column index</li>
66041 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66043 * @param {Object} e An edit event (see above for description)
66045 "beforeedit" : true,
66048 * Fires after a cell is edited. <br />
66049 * <ul style="padding:5px;padding-left:16px;">
66050 * <li>grid - This grid</li>
66051 * <li>record - The record being edited</li>
66052 * <li>field - The field name being edited</li>
66053 * <li>value - The value being set</li>
66054 * <li>originalValue - The original value for the field, before the edit.</li>
66055 * <li>row - The grid row index</li>
66056 * <li>column - The grid column index</li>
66058 * @param {Object} e An edit event (see above for description)
66060 "afteredit" : true,
66062 * @event validateedit
66063 * Fires after a cell is edited, but before the value is set in the record.
66064 * You can use this to modify the value being set in the field, Return false
66065 * to cancel the change. The edit event object has the following properties <br />
66066 * <ul style="padding:5px;padding-left:16px;">
66067 * <li>editor - This editor</li>
66068 * <li>grid - This grid</li>
66069 * <li>record - The record being edited</li>
66070 * <li>field - The field name being edited</li>
66071 * <li>value - The value being set</li>
66072 * <li>originalValue - The original value for the field, before the edit.</li>
66073 * <li>row - The grid row index</li>
66074 * <li>column - The grid column index</li>
66075 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66077 * @param {Object} e An edit event (see above for description)
66079 "validateedit" : true
66081 this.on("bodyscroll", this.stopEditing, this);
66082 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
66085 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
66087 * @cfg {Number} clicksToEdit
66088 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
66095 trackMouseOver: false, // causes very odd FF errors
66097 onCellDblClick : function(g, row, col){
66098 this.startEditing(row, col);
66101 onEditComplete : function(ed, value, startValue){
66102 this.editing = false;
66103 this.activeEditor = null;
66104 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
66106 var field = this.colModel.getDataIndex(ed.col);
66111 originalValue: startValue,
66118 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
66121 if(String(value) !== String(startValue)){
66123 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
66124 r.set(field, e.value);
66125 // if we are dealing with a combo box..
66126 // then we also set the 'name' colum to be the displayField
66127 if (ed.field.displayField && ed.field.name) {
66128 r.set(ed.field.name, ed.field.el.dom.value);
66131 delete e.cancel; //?? why!!!
66132 this.fireEvent("afteredit", e);
66135 this.fireEvent("afteredit", e); // always fire it!
66137 this.view.focusCell(ed.row, ed.col);
66141 * Starts editing the specified for the specified row/column
66142 * @param {Number} rowIndex
66143 * @param {Number} colIndex
66145 startEditing : function(row, col){
66146 this.stopEditing();
66147 if(this.colModel.isCellEditable(col, row)){
66148 this.view.ensureVisible(row, col, true);
66150 var r = this.dataSource.getAt(row);
66151 var field = this.colModel.getDataIndex(col);
66152 var cell = Roo.get(this.view.getCell(row,col));
66157 value: r.data[field],
66162 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
66163 this.editing = true;
66164 var ed = this.colModel.getCellEditor(col, row);
66170 ed.render(ed.parentEl || document.body);
66176 (function(){ // complex but required for focus issues in safari, ie and opera
66180 ed.on("complete", this.onEditComplete, this, {single: true});
66181 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
66182 this.activeEditor = ed;
66183 var v = r.data[field];
66184 ed.startEdit(this.view.getCell(row, col), v);
66185 // combo's with 'displayField and name set
66186 if (ed.field.displayField && ed.field.name) {
66187 ed.field.el.dom.value = r.data[ed.field.name];
66191 }).defer(50, this);
66197 * Stops any active editing
66199 stopEditing : function(){
66200 if(this.activeEditor){
66201 this.activeEditor.completeEdit();
66203 this.activeEditor = null;
66207 * Called to get grid's drag proxy text, by default returns this.ddText.
66210 getDragDropText : function(){
66211 var count = this.selModel.getSelectedCell() ? 1 : 0;
66212 return String.format(this.ddText, count, count == 1 ? '' : 's');
66217 * Ext JS Library 1.1.1
66218 * Copyright(c) 2006-2007, Ext JS, LLC.
66220 * Originally Released Under LGPL - original licence link has changed is not relivant.
66223 * <script type="text/javascript">
66226 // private - not really -- you end up using it !
66227 // This is a support class used internally by the Grid components
66230 * @class Roo.grid.GridEditor
66231 * @extends Roo.Editor
66232 * Class for creating and editable grid elements.
66233 * @param {Object} config any settings (must include field)
66235 Roo.grid.GridEditor = function(field, config){
66236 if (!config && field.field) {
66238 field = Roo.factory(config.field, Roo.form);
66240 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66241 field.monitorTab = false;
66244 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66247 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66250 alignment: "tl-tl",
66253 cls: "x-small-editor x-grid-editor",
66258 * Ext JS Library 1.1.1
66259 * Copyright(c) 2006-2007, Ext JS, LLC.
66261 * Originally Released Under LGPL - original licence link has changed is not relivant.
66264 * <script type="text/javascript">
66269 Roo.grid.PropertyRecord = Roo.data.Record.create([
66270 {name:'name',type:'string'}, 'value'
66274 Roo.grid.PropertyStore = function(grid, source){
66276 this.store = new Roo.data.Store({
66277 recordType : Roo.grid.PropertyRecord
66279 this.store.on('update', this.onUpdate, this);
66281 this.setSource(source);
66283 Roo.grid.PropertyStore.superclass.constructor.call(this);
66288 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66289 setSource : function(o){
66291 this.store.removeAll();
66294 if(this.isEditableValue(o[k])){
66295 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66298 this.store.loadRecords({records: data}, {}, true);
66301 onUpdate : function(ds, record, type){
66302 if(type == Roo.data.Record.EDIT){
66303 var v = record.data['value'];
66304 var oldValue = record.modified['value'];
66305 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66306 this.source[record.id] = v;
66308 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66315 getProperty : function(row){
66316 return this.store.getAt(row);
66319 isEditableValue: function(val){
66320 if(val && val instanceof Date){
66322 }else if(typeof val == 'object' || typeof val == 'function'){
66328 setValue : function(prop, value){
66329 this.source[prop] = value;
66330 this.store.getById(prop).set('value', value);
66333 getSource : function(){
66334 return this.source;
66338 Roo.grid.PropertyColumnModel = function(grid, store){
66341 g.PropertyColumnModel.superclass.constructor.call(this, [
66342 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66343 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66345 this.store = store;
66346 this.bselect = Roo.DomHelper.append(document.body, {
66347 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66348 {tag: 'option', value: 'true', html: 'true'},
66349 {tag: 'option', value: 'false', html: 'false'}
66352 Roo.id(this.bselect);
66355 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66356 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66357 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66358 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66359 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66361 this.renderCellDelegate = this.renderCell.createDelegate(this);
66362 this.renderPropDelegate = this.renderProp.createDelegate(this);
66365 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66369 valueText : 'Value',
66371 dateFormat : 'm/j/Y',
66374 renderDate : function(dateVal){
66375 return dateVal.dateFormat(this.dateFormat);
66378 renderBool : function(bVal){
66379 return bVal ? 'true' : 'false';
66382 isCellEditable : function(colIndex, rowIndex){
66383 return colIndex == 1;
66386 getRenderer : function(col){
66388 this.renderCellDelegate : this.renderPropDelegate;
66391 renderProp : function(v){
66392 return this.getPropertyName(v);
66395 renderCell : function(val){
66397 if(val instanceof Date){
66398 rv = this.renderDate(val);
66399 }else if(typeof val == 'boolean'){
66400 rv = this.renderBool(val);
66402 return Roo.util.Format.htmlEncode(rv);
66405 getPropertyName : function(name){
66406 var pn = this.grid.propertyNames;
66407 return pn && pn[name] ? pn[name] : name;
66410 getCellEditor : function(colIndex, rowIndex){
66411 var p = this.store.getProperty(rowIndex);
66412 var n = p.data['name'], val = p.data['value'];
66414 if(typeof(this.grid.customEditors[n]) == 'string'){
66415 return this.editors[this.grid.customEditors[n]];
66417 if(typeof(this.grid.customEditors[n]) != 'undefined'){
66418 return this.grid.customEditors[n];
66420 if(val instanceof Date){
66421 return this.editors['date'];
66422 }else if(typeof val == 'number'){
66423 return this.editors['number'];
66424 }else if(typeof val == 'boolean'){
66425 return this.editors['boolean'];
66427 return this.editors['string'];
66433 * @class Roo.grid.PropertyGrid
66434 * @extends Roo.grid.EditorGrid
66435 * This class represents the interface of a component based property grid control.
66436 * <br><br>Usage:<pre><code>
66437 var grid = new Roo.grid.PropertyGrid("my-container-id", {
66445 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66446 * The container MUST have some type of size defined for the grid to fill. The container will be
66447 * automatically set to position relative if it isn't already.
66448 * @param {Object} config A config object that sets properties on this grid.
66450 Roo.grid.PropertyGrid = function(container, config){
66451 config = config || {};
66452 var store = new Roo.grid.PropertyStore(this);
66453 this.store = store;
66454 var cm = new Roo.grid.PropertyColumnModel(this, store);
66455 store.store.sort('name', 'ASC');
66456 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66459 enableColLock:false,
66460 enableColumnMove:false,
66462 trackMouseOver: false,
66465 this.getGridEl().addClass('x-props-grid');
66466 this.lastEditRow = null;
66467 this.on('columnresize', this.onColumnResize, this);
66470 * @event beforepropertychange
66471 * Fires before a property changes (return false to stop?)
66472 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66473 * @param {String} id Record Id
66474 * @param {String} newval New Value
66475 * @param {String} oldval Old Value
66477 "beforepropertychange": true,
66479 * @event propertychange
66480 * Fires after a property changes
66481 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66482 * @param {String} id Record Id
66483 * @param {String} newval New Value
66484 * @param {String} oldval Old Value
66486 "propertychange": true
66488 this.customEditors = this.customEditors || {};
66490 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66493 * @cfg {Object} customEditors map of colnames=> custom editors.
66494 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66495 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66496 * false disables editing of the field.
66500 * @cfg {Object} propertyNames map of property Names to their displayed value
66503 render : function(){
66504 Roo.grid.PropertyGrid.superclass.render.call(this);
66505 this.autoSize.defer(100, this);
66508 autoSize : function(){
66509 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66511 this.view.fitColumns();
66515 onColumnResize : function(){
66516 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66520 * Sets the data for the Grid
66521 * accepts a Key => Value object of all the elements avaiable.
66522 * @param {Object} data to appear in grid.
66524 setSource : function(source){
66525 this.store.setSource(source);
66529 * Gets all the data from the grid.
66530 * @return {Object} data data stored in grid
66532 getSource : function(){
66533 return this.store.getSource();
66542 * @class Roo.grid.Calendar
66543 * @extends Roo.grid.Grid
66544 * This class extends the Grid to provide a calendar widget
66545 * <br><br>Usage:<pre><code>
66546 var grid = new Roo.grid.Calendar("my-container-id", {
66549 selModel: mySelectionModel,
66550 autoSizeColumns: true,
66551 monitorWindowResize: false,
66552 trackMouseOver: true
66553 eventstore : real data store..
66559 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66560 * The container MUST have some type of size defined for the grid to fill. The container will be
66561 * automatically set to position relative if it isn't already.
66562 * @param {Object} config A config object that sets properties on this grid.
66564 Roo.grid.Calendar = function(container, config){
66565 // initialize the container
66566 this.container = Roo.get(container);
66567 this.container.update("");
66568 this.container.setStyle("overflow", "hidden");
66569 this.container.addClass('x-grid-container');
66571 this.id = this.container.id;
66573 Roo.apply(this, config);
66574 // check and correct shorthanded configs
66578 for (var r = 0;r < 6;r++) {
66581 for (var c =0;c < 7;c++) {
66585 if (this.eventStore) {
66586 this.eventStore= Roo.factory(this.eventStore, Roo.data);
66587 this.eventStore.on('load',this.onLoad, this);
66588 this.eventStore.on('beforeload',this.clearEvents, this);
66592 this.dataSource = new Roo.data.Store({
66593 proxy: new Roo.data.MemoryProxy(rows),
66594 reader: new Roo.data.ArrayReader({}, [
66595 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66598 this.dataSource.load();
66599 this.ds = this.dataSource;
66600 this.ds.xmodule = this.xmodule || false;
66603 var cellRender = function(v,x,r)
66605 return String.format(
66606 '<div class="fc-day fc-widget-content"><div>' +
66607 '<div class="fc-event-container"></div>' +
66608 '<div class="fc-day-number">{0}</div>'+
66610 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66611 '</div></div>', v);
66616 this.colModel = new Roo.grid.ColumnModel( [
66618 xtype: 'ColumnModel',
66620 dataIndex : 'weekday0',
66622 renderer : cellRender
66625 xtype: 'ColumnModel',
66627 dataIndex : 'weekday1',
66629 renderer : cellRender
66632 xtype: 'ColumnModel',
66634 dataIndex : 'weekday2',
66635 header : 'Tuesday',
66636 renderer : cellRender
66639 xtype: 'ColumnModel',
66641 dataIndex : 'weekday3',
66642 header : 'Wednesday',
66643 renderer : cellRender
66646 xtype: 'ColumnModel',
66648 dataIndex : 'weekday4',
66649 header : 'Thursday',
66650 renderer : cellRender
66653 xtype: 'ColumnModel',
66655 dataIndex : 'weekday5',
66657 renderer : cellRender
66660 xtype: 'ColumnModel',
66662 dataIndex : 'weekday6',
66663 header : 'Saturday',
66664 renderer : cellRender
66667 this.cm = this.colModel;
66668 this.cm.xmodule = this.xmodule || false;
66672 //this.selModel = new Roo.grid.CellSelectionModel();
66673 //this.sm = this.selModel;
66674 //this.selModel.init(this);
66678 this.container.setWidth(this.width);
66682 this.container.setHeight(this.height);
66689 * The raw click event for the entire grid.
66690 * @param {Roo.EventObject} e
66695 * The raw dblclick event for the entire grid.
66696 * @param {Roo.EventObject} e
66700 * @event contextmenu
66701 * The raw contextmenu event for the entire grid.
66702 * @param {Roo.EventObject} e
66704 "contextmenu" : true,
66707 * The raw mousedown event for the entire grid.
66708 * @param {Roo.EventObject} e
66710 "mousedown" : true,
66713 * The raw mouseup event for the entire grid.
66714 * @param {Roo.EventObject} e
66719 * The raw mouseover event for the entire grid.
66720 * @param {Roo.EventObject} e
66722 "mouseover" : true,
66725 * The raw mouseout event for the entire grid.
66726 * @param {Roo.EventObject} e
66731 * The raw keypress event for the entire grid.
66732 * @param {Roo.EventObject} e
66737 * The raw keydown event for the entire grid.
66738 * @param {Roo.EventObject} e
66746 * Fires when a cell is clicked
66747 * @param {Grid} this
66748 * @param {Number} rowIndex
66749 * @param {Number} columnIndex
66750 * @param {Roo.EventObject} e
66752 "cellclick" : true,
66754 * @event celldblclick
66755 * Fires when a cell is double clicked
66756 * @param {Grid} this
66757 * @param {Number} rowIndex
66758 * @param {Number} columnIndex
66759 * @param {Roo.EventObject} e
66761 "celldblclick" : true,
66764 * Fires when a row is clicked
66765 * @param {Grid} this
66766 * @param {Number} rowIndex
66767 * @param {Roo.EventObject} e
66771 * @event rowdblclick
66772 * Fires when a row is double clicked
66773 * @param {Grid} this
66774 * @param {Number} rowIndex
66775 * @param {Roo.EventObject} e
66777 "rowdblclick" : true,
66779 * @event headerclick
66780 * Fires when a header is clicked
66781 * @param {Grid} this
66782 * @param {Number} columnIndex
66783 * @param {Roo.EventObject} e
66785 "headerclick" : true,
66787 * @event headerdblclick
66788 * Fires when a header cell is double clicked
66789 * @param {Grid} this
66790 * @param {Number} columnIndex
66791 * @param {Roo.EventObject} e
66793 "headerdblclick" : true,
66795 * @event rowcontextmenu
66796 * Fires when a row is right clicked
66797 * @param {Grid} this
66798 * @param {Number} rowIndex
66799 * @param {Roo.EventObject} e
66801 "rowcontextmenu" : true,
66803 * @event cellcontextmenu
66804 * Fires when a cell is right clicked
66805 * @param {Grid} this
66806 * @param {Number} rowIndex
66807 * @param {Number} cellIndex
66808 * @param {Roo.EventObject} e
66810 "cellcontextmenu" : true,
66812 * @event headercontextmenu
66813 * Fires when a header is right clicked
66814 * @param {Grid} this
66815 * @param {Number} columnIndex
66816 * @param {Roo.EventObject} e
66818 "headercontextmenu" : true,
66820 * @event bodyscroll
66821 * Fires when the body element is scrolled
66822 * @param {Number} scrollLeft
66823 * @param {Number} scrollTop
66825 "bodyscroll" : true,
66827 * @event columnresize
66828 * Fires when the user resizes a column
66829 * @param {Number} columnIndex
66830 * @param {Number} newSize
66832 "columnresize" : true,
66834 * @event columnmove
66835 * Fires when the user moves a column
66836 * @param {Number} oldIndex
66837 * @param {Number} newIndex
66839 "columnmove" : true,
66842 * Fires when row(s) start being dragged
66843 * @param {Grid} this
66844 * @param {Roo.GridDD} dd The drag drop object
66845 * @param {event} e The raw browser event
66847 "startdrag" : true,
66850 * Fires when a drag operation is complete
66851 * @param {Grid} this
66852 * @param {Roo.GridDD} dd The drag drop object
66853 * @param {event} e The raw browser event
66858 * Fires when dragged row(s) are dropped on a valid DD target
66859 * @param {Grid} this
66860 * @param {Roo.GridDD} dd The drag drop object
66861 * @param {String} targetId The target drag drop object
66862 * @param {event} e The raw browser event
66867 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66868 * @param {Grid} this
66869 * @param {Roo.GridDD} dd The drag drop object
66870 * @param {String} targetId The target drag drop object
66871 * @param {event} e The raw browser event
66876 * Fires when the dragged row(s) first cross another DD target while being dragged
66877 * @param {Grid} this
66878 * @param {Roo.GridDD} dd The drag drop object
66879 * @param {String} targetId The target drag drop object
66880 * @param {event} e The raw browser event
66882 "dragenter" : true,
66885 * Fires when the dragged row(s) leave another DD target while being dragged
66886 * @param {Grid} this
66887 * @param {Roo.GridDD} dd The drag drop object
66888 * @param {String} targetId The target drag drop object
66889 * @param {event} e The raw browser event
66894 * Fires when a row is rendered, so you can change add a style to it.
66895 * @param {GridView} gridview The grid view
66896 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
66902 * Fires when the grid is rendered
66903 * @param {Grid} grid
66908 * Fires when a date is selected
66909 * @param {DatePicker} this
66910 * @param {Date} date The selected date
66914 * @event monthchange
66915 * Fires when the displayed month changes
66916 * @param {DatePicker} this
66917 * @param {Date} date The selected month
66919 'monthchange': true,
66921 * @event evententer
66922 * Fires when mouse over an event
66923 * @param {Calendar} this
66924 * @param {event} Event
66926 'evententer': true,
66928 * @event eventleave
66929 * Fires when the mouse leaves an
66930 * @param {Calendar} this
66933 'eventleave': true,
66935 * @event eventclick
66936 * Fires when the mouse click an
66937 * @param {Calendar} this
66940 'eventclick': true,
66942 * @event eventrender
66943 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
66944 * @param {Calendar} this
66945 * @param {data} data to be modified
66947 'eventrender': true
66951 Roo.grid.Grid.superclass.constructor.call(this);
66952 this.on('render', function() {
66953 this.view.el.addClass('x-grid-cal');
66955 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
66959 if (!Roo.grid.Calendar.style) {
66960 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
66963 '.x-grid-cal .x-grid-col' : {
66964 height: 'auto !important',
66965 'vertical-align': 'top'
66967 '.x-grid-cal .fc-event-hori' : {
66978 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
66980 * @cfg {Store} eventStore The store that loads events.
66985 activeDate : false,
66988 monitorWindowResize : false,
66991 resizeColumns : function() {
66992 var col = (this.view.el.getWidth() / 7) - 3;
66993 // loop through cols, and setWidth
66994 for(var i =0 ; i < 7 ; i++){
66995 this.cm.setColumnWidth(i, col);
66998 setDate :function(date) {
67000 Roo.log('setDate?');
67002 this.resizeColumns();
67003 var vd = this.activeDate;
67004 this.activeDate = date;
67005 // if(vd && this.el){
67006 // var t = date.getTime();
67007 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
67008 // Roo.log('using add remove');
67010 // this.fireEvent('monthchange', this, date);
67012 // this.cells.removeClass("fc-state-highlight");
67013 // this.cells.each(function(c){
67014 // if(c.dateValue == t){
67015 // c.addClass("fc-state-highlight");
67016 // setTimeout(function(){
67017 // try{c.dom.firstChild.focus();}catch(e){}
67027 var days = date.getDaysInMonth();
67029 var firstOfMonth = date.getFirstDateOfMonth();
67030 var startingPos = firstOfMonth.getDay()-this.startDay;
67032 if(startingPos < this.startDay){
67036 var pm = date.add(Date.MONTH, -1);
67037 var prevStart = pm.getDaysInMonth()-startingPos;
67041 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67043 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
67044 //this.cells.addClassOnOver('fc-state-hover');
67046 var cells = this.cells.elements;
67047 var textEls = this.textNodes;
67049 //Roo.each(cells, function(cell){
67050 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
67053 days += startingPos;
67055 // convert everything to numbers so it's fast
67056 var day = 86400000;
67057 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
67060 //Roo.log(prevStart);
67062 var today = new Date().clearTime().getTime();
67063 var sel = date.clearTime().getTime();
67064 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
67065 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
67066 var ddMatch = this.disabledDatesRE;
67067 var ddText = this.disabledDatesText;
67068 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
67069 var ddaysText = this.disabledDaysText;
67070 var format = this.format;
67072 var setCellClass = function(cal, cell){
67074 //Roo.log('set Cell Class');
67076 var t = d.getTime();
67081 cell.dateValue = t;
67083 cell.className += " fc-today";
67084 cell.className += " fc-state-highlight";
67085 cell.title = cal.todayText;
67088 // disable highlight in other month..
67089 cell.className += " fc-state-highlight";
67094 //cell.className = " fc-state-disabled";
67095 cell.title = cal.minText;
67099 //cell.className = " fc-state-disabled";
67100 cell.title = cal.maxText;
67104 if(ddays.indexOf(d.getDay()) != -1){
67105 // cell.title = ddaysText;
67106 // cell.className = " fc-state-disabled";
67109 if(ddMatch && format){
67110 var fvalue = d.dateFormat(format);
67111 if(ddMatch.test(fvalue)){
67112 cell.title = ddText.replace("%0", fvalue);
67113 cell.className = " fc-state-disabled";
67117 if (!cell.initialClassName) {
67118 cell.initialClassName = cell.dom.className;
67121 cell.dom.className = cell.initialClassName + ' ' + cell.className;
67126 for(; i < startingPos; i++) {
67127 cells[i].dayName = (++prevStart);
67128 Roo.log(textEls[i]);
67129 d.setDate(d.getDate()+1);
67131 //cells[i].className = "fc-past fc-other-month";
67132 setCellClass(this, cells[i]);
67137 for(; i < days; i++){
67138 intDay = i - startingPos + 1;
67139 cells[i].dayName = (intDay);
67140 d.setDate(d.getDate()+1);
67142 cells[i].className = ''; // "x-date-active";
67143 setCellClass(this, cells[i]);
67147 for(; i < 42; i++) {
67148 //textEls[i].innerHTML = (++extraDays);
67150 d.setDate(d.getDate()+1);
67151 cells[i].dayName = (++extraDays);
67152 cells[i].className = "fc-future fc-other-month";
67153 setCellClass(this, cells[i]);
67156 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
67158 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
67160 // this will cause all the cells to mis
67163 for (var r = 0;r < 6;r++) {
67164 for (var c =0;c < 7;c++) {
67165 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
67169 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67170 for(i=0;i<cells.length;i++) {
67172 this.cells.elements[i].dayName = cells[i].dayName ;
67173 this.cells.elements[i].className = cells[i].className;
67174 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
67175 this.cells.elements[i].title = cells[i].title ;
67176 this.cells.elements[i].dateValue = cells[i].dateValue ;
67182 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
67183 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
67185 ////if(totalRows != 6){
67186 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
67187 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
67190 this.fireEvent('monthchange', this, date);
67195 * Returns the grid's SelectionModel.
67196 * @return {SelectionModel}
67198 getSelectionModel : function(){
67199 if(!this.selModel){
67200 this.selModel = new Roo.grid.CellSelectionModel();
67202 return this.selModel;
67206 this.eventStore.load()
67212 findCell : function(dt) {
67213 dt = dt.clearTime().getTime();
67215 this.cells.each(function(c){
67216 //Roo.log("check " +c.dateValue + '?=' + dt);
67217 if(c.dateValue == dt){
67227 findCells : function(rec) {
67228 var s = rec.data.start_dt.clone().clearTime().getTime();
67230 var e= rec.data.end_dt.clone().clearTime().getTime();
67233 this.cells.each(function(c){
67234 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67236 if(c.dateValue > e){
67239 if(c.dateValue < s){
67248 findBestRow: function(cells)
67252 for (var i =0 ; i < cells.length;i++) {
67253 ret = Math.max(cells[i].rows || 0,ret);
67260 addItem : function(rec)
67262 // look for vertical location slot in
67263 var cells = this.findCells(rec);
67265 rec.row = this.findBestRow(cells);
67267 // work out the location.
67271 for(var i =0; i < cells.length; i++) {
67279 if (crow.start.getY() == cells[i].getY()) {
67281 crow.end = cells[i];
67297 for (var i = 0; i < cells.length;i++) {
67298 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67305 clearEvents: function() {
67307 if (!this.eventStore.getCount()) {
67310 // reset number of rows in cells.
67311 Roo.each(this.cells.elements, function(c){
67315 this.eventStore.each(function(e) {
67316 this.clearEvent(e);
67321 clearEvent : function(ev)
67324 Roo.each(ev.els, function(el) {
67325 el.un('mouseenter' ,this.onEventEnter, this);
67326 el.un('mouseleave' ,this.onEventLeave, this);
67334 renderEvent : function(ev,ctr) {
67336 ctr = this.view.el.select('.fc-event-container',true).first();
67340 this.clearEvent(ev);
67346 var cells = ev.cells;
67347 var rows = ev.rows;
67348 this.fireEvent('eventrender', this, ev);
67350 for(var i =0; i < rows.length; i++) {
67354 cls += ' fc-event-start';
67356 if ((i+1) == rows.length) {
67357 cls += ' fc-event-end';
67360 //Roo.log(ev.data);
67361 // how many rows should it span..
67362 var cg = this.eventTmpl.append(ctr,Roo.apply({
67365 }, ev.data) , true);
67368 cg.on('mouseenter' ,this.onEventEnter, this, ev);
67369 cg.on('mouseleave' ,this.onEventLeave, this, ev);
67370 cg.on('click', this.onEventClick, this, ev);
67374 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67375 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67378 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
67379 cg.setWidth(ebox.right - sbox.x -2);
67383 renderEvents: function()
67385 // first make sure there is enough space..
67387 if (!this.eventTmpl) {
67388 this.eventTmpl = new Roo.Template(
67389 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
67390 '<div class="fc-event-inner">' +
67391 '<span class="fc-event-time">{time}</span>' +
67392 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67394 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
67402 this.cells.each(function(c) {
67403 //Roo.log(c.select('.fc-day-content div',true).first());
67404 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67407 var ctr = this.view.el.select('.fc-event-container',true).first();
67410 this.eventStore.each(function(ev){
67412 this.renderEvent(ev);
67416 this.view.layout();
67420 onEventEnter: function (e, el,event,d) {
67421 this.fireEvent('evententer', this, el, event);
67424 onEventLeave: function (e, el,event,d) {
67425 this.fireEvent('eventleave', this, el, event);
67428 onEventClick: function (e, el,event,d) {
67429 this.fireEvent('eventclick', this, el, event);
67432 onMonthChange: function () {
67436 onLoad: function () {
67438 //Roo.log('calendar onload');
67440 if(this.eventStore.getCount() > 0){
67444 this.eventStore.each(function(d){
67449 if (typeof(add.end_dt) == 'undefined') {
67450 Roo.log("Missing End time in calendar data: ");
67454 if (typeof(add.start_dt) == 'undefined') {
67455 Roo.log("Missing Start time in calendar data: ");
67459 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67460 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67461 add.id = add.id || d.id;
67462 add.title = add.title || '??';
67470 this.renderEvents();
67480 render : function ()
67484 if (!this.view.el.hasClass('course-timesheet')) {
67485 this.view.el.addClass('course-timesheet');
67487 if (this.tsStyle) {
67492 Roo.log(_this.grid.view.el.getWidth());
67495 this.tsStyle = Roo.util.CSS.createStyleSheet({
67496 '.course-timesheet .x-grid-row' : {
67499 '.x-grid-row td' : {
67500 'vertical-align' : 0
67502 '.course-edit-link' : {
67504 'text-overflow' : 'ellipsis',
67505 'overflow' : 'hidden',
67506 'white-space' : 'nowrap',
67507 'cursor' : 'pointer'
67512 '.de-act-sup-link' : {
67513 'color' : 'purple',
67514 'text-decoration' : 'line-through'
67518 'text-decoration' : 'line-through'
67520 '.course-timesheet .course-highlight' : {
67521 'border-top-style': 'dashed !important',
67522 'border-bottom-bottom': 'dashed !important'
67524 '.course-timesheet .course-item' : {
67525 'font-family' : 'tahoma, arial, helvetica',
67526 'font-size' : '11px',
67527 'overflow' : 'hidden',
67528 'padding-left' : '10px',
67529 'padding-right' : '10px',
67530 'padding-top' : '10px'
67538 monitorWindowResize : false,
67539 cellrenderer : function(v,x,r)
67544 xtype: 'CellSelectionModel',
67551 beforeload : function (_self, options)
67553 options.params = options.params || {};
67554 options.params._month = _this.monthField.getValue();
67555 options.params.limit = 9999;
67556 options.params['sort'] = 'when_dt';
67557 options.params['dir'] = 'ASC';
67558 this.proxy.loadResponse = this.loadResponse;
67560 //this.addColumns();
67562 load : function (_self, records, options)
67564 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67565 // if you click on the translation.. you can edit it...
67566 var el = Roo.get(this);
67567 var id = el.dom.getAttribute('data-id');
67568 var d = el.dom.getAttribute('data-date');
67569 var t = el.dom.getAttribute('data-time');
67570 //var id = this.child('span').dom.textContent;
67573 Pman.Dialog.CourseCalendar.show({
67577 productitem_active : id ? 1 : 0
67579 _this.grid.ds.load({});
67584 _this.panel.fireEvent('resize', [ '', '' ]);
67587 loadResponse : function(o, success, response){
67588 // this is overridden on before load..
67590 Roo.log("our code?");
67591 //Roo.log(success);
67592 //Roo.log(response)
67593 delete this.activeRequest;
67595 this.fireEvent("loadexception", this, o, response);
67596 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67601 result = o.reader.read(response);
67603 Roo.log("load exception?");
67604 this.fireEvent("loadexception", this, o, response, e);
67605 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67608 Roo.log("ready...");
67609 // loop through result.records;
67610 // and set this.tdate[date] = [] << array of records..
67612 Roo.each(result.records, function(r){
67614 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67615 _this.tdata[r.data.when_dt.format('j')] = [];
67617 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67620 //Roo.log(_this.tdata);
67622 result.records = [];
67623 result.totalRecords = 6;
67625 // let's generate some duumy records for the rows.
67626 //var st = _this.dateField.getValue();
67628 // work out monday..
67629 //st = st.add(Date.DAY, -1 * st.format('w'));
67631 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67633 var firstOfMonth = date.getFirstDayOfMonth();
67634 var days = date.getDaysInMonth();
67636 var firstAdded = false;
67637 for (var i = 0; i < result.totalRecords ; i++) {
67638 //var d= st.add(Date.DAY, i);
67641 for(var w = 0 ; w < 7 ; w++){
67642 if(!firstAdded && firstOfMonth != w){
67649 var dd = (d > 0 && d < 10) ? "0"+d : d;
67650 row['weekday'+w] = String.format(
67651 '<span style="font-size: 16px;"><b>{0}</b></span>'+
67652 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67654 date.format('Y-m-')+dd
67657 if(typeof(_this.tdata[d]) != 'undefined'){
67658 Roo.each(_this.tdata[d], function(r){
67662 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67663 if(r.parent_id*1>0){
67664 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67667 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67668 deactive = 'de-act-link';
67671 row['weekday'+w] += String.format(
67672 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67674 r.product_id_name, //1
67675 r.when_dt.format('h:ia'), //2
67685 // only do this if something added..
67687 result.records.push(_this.grid.dataSource.reader.newRow(row));
67691 // push it twice. (second one with an hour..
67695 this.fireEvent("load", this, o, o.request.arg);
67696 o.request.callback.call(o.request.scope, result, o.request.arg, true);
67698 sortInfo : {field: 'when_dt', direction : 'ASC' },
67700 xtype: 'HttpProxy',
67703 url : baseURL + '/Roo/Shop_course.php'
67706 xtype: 'JsonReader',
67723 'name': 'parent_id',
67727 'name': 'product_id',
67731 'name': 'productitem_id',
67749 click : function (_self, e)
67751 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67752 sd.setMonth(sd.getMonth()-1);
67753 _this.monthField.setValue(sd.format('Y-m-d'));
67754 _this.grid.ds.load({});
67760 xtype: 'Separator',
67764 xtype: 'MonthField',
67767 render : function (_self)
67769 _this.monthField = _self;
67770 // _this.monthField.set today
67772 select : function (combo, date)
67774 _this.grid.ds.load({});
67777 value : (function() { return new Date(); })()
67780 xtype: 'Separator',
67786 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67796 click : function (_self, e)
67798 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67799 sd.setMonth(sd.getMonth()+1);
67800 _this.monthField.setValue(sd.format('Y-m-d'));
67801 _this.grid.ds.load({});
67814 * Ext JS Library 1.1.1
67815 * Copyright(c) 2006-2007, Ext JS, LLC.
67817 * Originally Released Under LGPL - original licence link has changed is not relivant.
67820 * <script type="text/javascript">
67824 * @class Roo.LoadMask
67825 * A simple utility class for generically masking elements while loading data. If the element being masked has
67826 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67827 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
67828 * element's UpdateManager load indicator and will be destroyed after the initial load.
67830 * Create a new LoadMask
67831 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67832 * @param {Object} config The config object
67834 Roo.LoadMask = function(el, config){
67835 this.el = Roo.get(el);
67836 Roo.apply(this, config);
67838 this.store.on('beforeload', this.onBeforeLoad, this);
67839 this.store.on('load', this.onLoad, this);
67840 this.store.on('loadexception', this.onLoadException, this);
67841 this.removeMask = false;
67843 var um = this.el.getUpdateManager();
67844 um.showLoadIndicator = false; // disable the default indicator
67845 um.on('beforeupdate', this.onBeforeLoad, this);
67846 um.on('update', this.onLoad, this);
67847 um.on('failure', this.onLoad, this);
67848 this.removeMask = true;
67852 Roo.LoadMask.prototype = {
67854 * @cfg {Boolean} removeMask
67855 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67856 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
67858 removeMask : false,
67860 * @cfg {String} msg
67861 * The text to display in a centered loading message box (defaults to 'Loading...')
67863 msg : 'Loading...',
67865 * @cfg {String} msgCls
67866 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67868 msgCls : 'x-mask-loading',
67871 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67877 * Disables the mask to prevent it from being displayed
67879 disable : function(){
67880 this.disabled = true;
67884 * Enables the mask so that it can be displayed
67886 enable : function(){
67887 this.disabled = false;
67890 onLoadException : function()
67892 Roo.log(arguments);
67894 if (typeof(arguments[3]) != 'undefined') {
67895 Roo.MessageBox.alert("Error loading",arguments[3]);
67899 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67900 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67907 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67910 onLoad : function()
67912 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67916 onBeforeLoad : function(){
67917 if(!this.disabled){
67918 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67923 destroy : function(){
67925 this.store.un('beforeload', this.onBeforeLoad, this);
67926 this.store.un('load', this.onLoad, this);
67927 this.store.un('loadexception', this.onLoadException, this);
67929 var um = this.el.getUpdateManager();
67930 um.un('beforeupdate', this.onBeforeLoad, this);
67931 um.un('update', this.onLoad, this);
67932 um.un('failure', this.onLoad, this);
67937 * Ext JS Library 1.1.1
67938 * Copyright(c) 2006-2007, Ext JS, LLC.
67940 * Originally Released Under LGPL - original licence link has changed is not relivant.
67943 * <script type="text/javascript">
67948 * @class Roo.XTemplate
67949 * @extends Roo.Template
67950 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
67952 var t = new Roo.XTemplate(
67953 '<select name="{name}">',
67954 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
67958 // then append, applying the master template values
67961 * Supported features:
67966 {a_variable} - output encoded.
67967 {a_variable.format:("Y-m-d")} - call a method on the variable
67968 {a_variable:raw} - unencoded output
67969 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
67970 {a_variable:this.method_on_template(...)} - call a method on the template object.
67975 <tpl for="a_variable or condition.."></tpl>
67976 <tpl if="a_variable or condition"></tpl>
67977 <tpl exec="some javascript"></tpl>
67978 <tpl name="named_template"></tpl> (experimental)
67980 <tpl for="."></tpl> - just iterate the property..
67981 <tpl for=".."></tpl> - iterates with the parent (probably the template)
67985 Roo.XTemplate = function()
67987 Roo.XTemplate.superclass.constructor.apply(this, arguments);
67994 Roo.extend(Roo.XTemplate, Roo.Template, {
67997 * The various sub templates
68002 * basic tag replacing syntax
68005 * // you can fake an object call by doing this
68009 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
68012 * compile the template
68014 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
68017 compile: function()
68021 s = ['<tpl>', s, '</tpl>'].join('');
68023 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
68024 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
68025 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
68026 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
68027 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
68032 while(true == !!(m = s.match(re))){
68033 var forMatch = m[0].match(nameRe),
68034 ifMatch = m[0].match(ifRe),
68035 execMatch = m[0].match(execRe),
68036 namedMatch = m[0].match(namedRe),
68041 name = forMatch && forMatch[1] ? forMatch[1] : '';
68044 // if - puts fn into test..
68045 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
68047 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
68052 // exec - calls a function... returns empty if true is returned.
68053 exp = execMatch && execMatch[1] ? execMatch[1] : null;
68055 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
68063 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
68064 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
68065 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
68068 var uid = namedMatch ? namedMatch[1] : id;
68072 id: namedMatch ? namedMatch[1] : id,
68079 s = s.replace(m[0], '');
68081 s = s.replace(m[0], '{xtpl'+ id + '}');
68086 for(var i = tpls.length-1; i >= 0; --i){
68087 this.compileTpl(tpls[i]);
68088 this.tpls[tpls[i].id] = tpls[i];
68090 this.master = tpls[tpls.length-1];
68094 * same as applyTemplate, except it's done to one of the subTemplates
68095 * when using named templates, you can do:
68097 * var str = pl.applySubTemplate('your-name', values);
68100 * @param {Number} id of the template
68101 * @param {Object} values to apply to template
68102 * @param {Object} parent (normaly the instance of this object)
68104 applySubTemplate : function(id, values, parent)
68108 var t = this.tpls[id];
68112 if(t.test && !t.test.call(this, values, parent)){
68116 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
68117 Roo.log(e.toString());
68123 if(t.exec && t.exec.call(this, values, parent)){
68127 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
68128 Roo.log(e.toString());
68133 var vs = t.target ? t.target.call(this, values, parent) : values;
68134 parent = t.target ? values : parent;
68135 if(t.target && vs instanceof Array){
68137 for(var i = 0, len = vs.length; i < len; i++){
68138 buf[buf.length] = t.compiled.call(this, vs[i], parent);
68140 return buf.join('');
68142 return t.compiled.call(this, vs, parent);
68144 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
68145 Roo.log(e.toString());
68146 Roo.log(t.compiled);
68151 compileTpl : function(tpl)
68153 var fm = Roo.util.Format;
68154 var useF = this.disableFormats !== true;
68155 var sep = Roo.isGecko ? "+" : ",";
68156 var undef = function(str) {
68157 Roo.log("Property not found :" + str);
68161 var fn = function(m, name, format, args)
68163 //Roo.log(arguments);
68164 args = args ? args.replace(/\\'/g,"'") : args;
68165 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
68166 if (typeof(format) == 'undefined') {
68167 format= 'htmlEncode';
68169 if (format == 'raw' ) {
68173 if(name.substr(0, 4) == 'xtpl'){
68174 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
68177 // build an array of options to determine if value is undefined..
68179 // basically get 'xxxx.yyyy' then do
68180 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
68181 // (function () { Roo.log("Property not found"); return ''; })() :
68186 Roo.each(name.split('.'), function(st) {
68187 lookfor += (lookfor.length ? '.': '') + st;
68188 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
68191 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
68194 if(format && useF){
68196 args = args ? ',' + args : "";
68198 if(format.substr(0, 5) != "this."){
68199 format = "fm." + format + '(';
68201 format = 'this.call("'+ format.substr(5) + '", ';
68205 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
68209 // called with xxyx.yuu:(test,test)
68211 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
68213 // raw.. - :raw modifier..
68214 return "'"+ sep + udef_st + name + ")"+sep+"'";
68218 // branched to use + in gecko and [].join() in others
68220 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
68221 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68224 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
68225 body.push(tpl.body.replace(/(\r\n|\n)/g,
68226 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68227 body.push("'].join('');};};");
68228 body = body.join('');
68231 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68233 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
68239 applyTemplate : function(values){
68240 return this.master.compiled.call(this, values, {});
68241 //var s = this.subs;
68244 apply : function(){
68245 return this.applyTemplate.apply(this, arguments);
68250 Roo.XTemplate.from = function(el){
68251 el = Roo.getDom(el);
68252 return new Roo.XTemplate(el.value || el.innerHTML);
68259 * @class Roo.dialog.UploadCropbox
68260 * @extends Roo.BoxComponent
68261 * Dialog UploadCropbox class
68262 * @cfg {String} emptyText show when image has been loaded
68263 * @cfg {String} rotateNotify show when image too small to rotate
68264 * @cfg {Number} errorTimeout default 3000
68265 * @cfg {Number} minWidth default 300
68266 * @cfg {Number} minHeight default 300
68267 * @cfg {Number} outputMaxWidth default 1200
68268 * @cfg {Number} windowSize default 300
68269 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
68270 * @cfg {Boolean} isDocument (true|false) default false
68271 * @cfg {String} url action url
68272 * @cfg {String} paramName default 'imageUpload'
68273 * @cfg {String} method default POST
68274 * @cfg {Boolean} loadMask (true|false) default true
68275 * @cfg {Boolean} loadingText default 'Loading...'
68278 * Create a new UploadCropbox
68279 * @param {Object} config The config object
68282 Roo.dialog.UploadCropbox = function(config){
68283 Roo.dialog.UploadCropbox.superclass.constructor.call(this, config);
68287 * @event beforeselectfile
68288 * Fire before select file
68289 * @param {Roo.dialog.UploadCropbox} this
68291 "beforeselectfile" : true,
68294 * Fire after initEvent
68295 * @param {Roo.dialog.UploadCropbox} this
68300 * Fire after initEvent
68301 * @param {Roo.dialog.UploadCropbox} this
68302 * @param {String} data
68307 * Fire when preparing the file data
68308 * @param {Roo.dialog.UploadCropbox} this
68309 * @param {Object} file
68314 * Fire when get exception
68315 * @param {Roo.dialog.UploadCropbox} this
68316 * @param {XMLHttpRequest} xhr
68318 "exception" : true,
68320 * @event beforeloadcanvas
68321 * Fire before load the canvas
68322 * @param {Roo.dialog.UploadCropbox} this
68323 * @param {String} src
68325 "beforeloadcanvas" : true,
68328 * Fire when trash image
68329 * @param {Roo.dialog.UploadCropbox} this
68334 * Fire when download the image
68335 * @param {Roo.dialog.UploadCropbox} this
68339 * @event footerbuttonclick
68340 * Fire when footerbuttonclick
68341 * @param {Roo.dialog.UploadCropbox} this
68342 * @param {String} type
68344 "footerbuttonclick" : true,
68348 * @param {Roo.dialog.UploadCropbox} this
68353 * Fire when rotate the image
68354 * @param {Roo.dialog.UploadCropbox} this
68355 * @param {String} pos
68360 * Fire when inspect the file
68361 * @param {Roo.dialog.UploadCropbox} this
68362 * @param {Object} file
68367 * Fire when xhr upload the file
68368 * @param {Roo.dialog.UploadCropbox} this
68369 * @param {Object} data
68374 * Fire when arrange the file data
68375 * @param {Roo.dialog.UploadCropbox} this
68376 * @param {Object} formData
68380 * @event loadcanvas
68381 * Fire after load the canvas
68382 * @param {Roo.dialog.UploadCropbox}
68383 * @param {Object} imgEl
68385 "loadcanvas" : true
68388 this.buttons = this.buttons || Roo.dialog.UploadCropbox.footer.STANDARD;
68391 Roo.extend(Roo.dialog.UploadCropbox, Roo.Component, {
68393 emptyText : 'Click to upload image',
68394 rotateNotify : 'Image is too small to rotate',
68395 errorTimeout : 3000,
68406 outputMaxWidth : 1200,
68411 cropType : 'image/jpeg',
68413 canvasLoaded : false,
68414 isDocument : false,
68416 paramName : 'imageUpload',
68418 loadingText : 'Loading...',
68421 getAutoCreate : function()
68425 cls : 'roo-upload-cropbox',
68429 cls : 'roo-upload-cropbox-selector',
68434 cls : 'roo-upload-cropbox-body',
68435 style : 'cursor:pointer',
68439 cls : 'roo-upload-cropbox-preview'
68443 cls : 'roo-upload-cropbox-thumb'
68447 cls : 'roo-upload-cropbox-empty-notify',
68448 html : this.emptyText
68452 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
68453 html : this.rotateNotify
68459 cls : 'roo-upload-cropbox-footer',
68462 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
68472 onRender : function(ct, position)
68474 Roo.dialog.UploadCropbox.superclass.onRender.call(this, ct, position);
68477 if (this.el.attr('xtype')) {
68478 this.el.attr('xtypex', this.el.attr('xtype'));
68479 this.el.dom.removeAttribute('xtype');
68485 var cfg = Roo.apply({}, this.getAutoCreate());
68487 cfg.id = this.id || Roo.id();
68490 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
68493 if (this.style) { // fixme needs to support more complex style data.
68494 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
68497 this.el = ct.createChild(cfg, position);
68502 if (this.buttons.length) {
68504 Roo.each(this.buttons, function(bb) {
68506 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
68508 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
68514 this.maskEl = this.el;
68518 initEvents : function()
68520 this.urlAPI = (window.createObjectURL && window) ||
68521 (window.URL && URL.revokeObjectURL && URL) ||
68522 (window.webkitURL && webkitURL);
68524 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
68525 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68527 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
68528 this.selectorEl.hide();
68530 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
68531 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68533 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
68534 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68535 this.thumbEl.hide();
68537 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
68538 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68540 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
68541 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68542 this.errorEl.hide();
68544 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
68545 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68546 this.footerEl.hide();
68548 this.setThumbBoxSize();
68554 this.fireEvent('initial', this);
68561 window.addEventListener("resize", function() { _this.resize(); } );
68563 this.bodyEl.on('click', this.beforeSelectFile, this);
68566 this.bodyEl.on('touchstart', this.onTouchStart, this);
68567 this.bodyEl.on('touchmove', this.onTouchMove, this);
68568 this.bodyEl.on('touchend', this.onTouchEnd, this);
68572 this.bodyEl.on('mousedown', this.onMouseDown, this);
68573 this.bodyEl.on('mousemove', this.onMouseMove, this);
68574 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
68575 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
68576 Roo.get(document).on('mouseup', this.onMouseUp, this);
68579 this.selectorEl.on('change', this.onFileSelected, this);
68585 this.baseScale = 1;
68587 this.baseRotate = 1;
68588 this.dragable = false;
68589 this.pinching = false;
68592 this.cropData = false;
68593 this.notifyEl.dom.innerHTML = this.emptyText;
68595 // this.selectorEl.dom.value = '';
68599 resize : function()
68601 if(this.fireEvent('resize', this) != false){
68602 this.setThumbBoxPosition();
68603 this.setCanvasPosition();
68607 onFooterButtonClick : function(e, el, o, type)
68610 case 'rotate-left' :
68611 this.onRotateLeft(e);
68613 case 'rotate-right' :
68614 this.onRotateRight(e);
68617 this.beforeSelectFile(e);
68632 this.fireEvent('footerbuttonclick', this, type);
68635 beforeSelectFile : function(e)
68637 e.preventDefault();
68639 if(this.fireEvent('beforeselectfile', this) != false){
68640 this.selectorEl.dom.click();
68644 onFileSelected : function(e)
68646 e.preventDefault();
68648 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
68652 var file = this.selectorEl.dom.files[0];
68654 if(this.fireEvent('inspect', this, file) != false){
68655 this.prepare(file);
68660 trash : function(e)
68662 this.fireEvent('trash', this);
68665 download : function(e)
68667 this.fireEvent('download', this);
68670 loadCanvas : function(src)
68672 if(this.fireEvent('beforeloadcanvas', this, src) != false){
68676 this.imageEl = document.createElement('img');
68680 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
68682 this.imageEl.src = src;
68686 onLoadCanvas : function()
68688 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
68689 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
68691 if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
68693 this.bodyEl.un('click', this.beforeSelectFile, this);
68695 this.notifyEl.hide();
68696 this.thumbEl.show();
68697 this.footerEl.show();
68699 this.baseRotateLevel();
68701 if(this.isDocument){
68702 this.setThumbBoxSize();
68705 this.setThumbBoxPosition();
68707 this.baseScaleLevel();
68713 this.canvasLoaded = true;
68718 this.maskEl.unmask();
68723 setCanvasPosition : function()
68725 if(!this.canvasEl){
68729 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
68730 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
68732 this.previewEl.setLeft(pw);
68733 this.previewEl.setTop(ph);
68737 onMouseDown : function(e)
68741 this.dragable = true;
68742 this.pinching = false;
68744 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
68745 this.dragable = false;
68749 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68750 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68754 onMouseMove : function(e)
68758 if(!this.canvasLoaded){
68762 if (!this.dragable){
68766 var minX = Math.ceil(this.thumbEl.getLeft(true));
68767 var minY = Math.ceil(this.thumbEl.getTop(true));
68769 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
68770 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
68784 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68785 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68787 x = x - this.mouseX;
68788 y = y - this.mouseY;
68790 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
68791 var bgY = Math.ceil(y + this.previewEl.getTop(true));
68793 bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
68794 bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
68796 this.previewEl.setLeft(bgX);
68797 this.previewEl.setTop(bgY);
68799 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68800 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68803 onMouseUp : function(e)
68807 this.dragable = false;
68810 onMouseWheel : function(e)
68814 this.startScale = this.scale;
68815 this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
68817 if(!this.zoomable()){
68818 this.scale = this.startScale;
68828 zoomable : function()
68830 var minScale = this.thumbEl.getWidth() / this.minWidth;
68832 if(this.minWidth < this.minHeight){
68833 minScale = this.thumbEl.getHeight() / this.minHeight;
68836 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
68837 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
68839 var maxWidth = this.imageEl.OriginWidth;
68840 var maxHeight = this.imageEl.OriginHeight;
68844 (this.rotate == 0 || this.rotate == 180) &&
68846 width > this.imageEl.OriginWidth ||
68847 height > this.imageEl.OriginHeight ||
68848 (width < this.minWidth && height < this.minHeight)
68856 (this.rotate == 90 || this.rotate == 270) &&
68858 width > this.imageEl.OriginWidth ||
68859 height > this.imageEl.OriginHeight ||
68860 (width < this.minHeight && height < this.minWidth)
68867 !this.isDocument &&
68868 (this.rotate == 0 || this.rotate == 180) &&
68870 (this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight) && width < this.minWidth ||
68871 (this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight) && height < this.minHeight ||
68872 width > maxWidth ||
68880 !this.isDocument &&
68881 (this.rotate == 90 || this.rotate == 270) &&
68883 width < this.minHeight ||
68884 width > this.imageEl.OriginWidth ||
68885 height < this.minWidth ||
68886 height > this.imageEl.OriginHeight
68896 onRotateLeft : function(e)
68898 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
68900 var minScale = this.thumbEl.getWidth() / this.minWidth;
68902 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
68903 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
68905 this.startScale = this.scale;
68907 while (this.getScaleLevel() < minScale){
68909 this.scale = this.scale + 1;
68911 if(!this.zoomable()){
68916 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
68917 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
68922 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
68929 this.scale = this.startScale;
68931 this.onRotateFail();
68936 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
68938 if(this.isDocument){
68939 this.setThumbBoxSize();
68940 this.setThumbBoxPosition();
68941 this.setCanvasPosition();
68946 this.fireEvent('rotate', this, 'left');
68950 onRotateRight : function(e)
68952 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
68954 var minScale = this.thumbEl.getWidth() / this.minWidth;
68956 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
68957 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
68959 this.startScale = this.scale;
68961 while (this.getScaleLevel() < minScale){
68963 this.scale = this.scale + 1;
68965 if(!this.zoomable()){
68970 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
68971 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
68976 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
68983 this.scale = this.startScale;
68985 this.onRotateFail();
68990 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
68992 if(this.isDocument){
68993 this.setThumbBoxSize();
68994 this.setThumbBoxPosition();
68995 this.setCanvasPosition();
69000 this.fireEvent('rotate', this, 'right');
69003 onRotateFail : function()
69005 this.errorEl.show(true);
69009 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
69014 this.previewEl.dom.innerHTML = '';
69016 var canvasEl = document.createElement("canvas");
69018 var contextEl = canvasEl.getContext("2d");
69020 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69021 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69022 var center = this.imageEl.OriginWidth / 2;
69024 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
69025 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69026 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69027 center = this.imageEl.OriginHeight / 2;
69030 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
69032 contextEl.translate(center, center);
69033 contextEl.rotate(this.rotate * Math.PI / 180);
69035 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69037 this.canvasEl = document.createElement("canvas");
69039 this.contextEl = this.canvasEl.getContext("2d");
69041 switch (this.rotate) {
69044 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69045 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69047 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69052 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69053 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69055 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69056 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);
69060 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69065 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69066 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69068 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69069 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);
69073 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);
69078 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69079 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69081 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69082 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69086 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);
69093 this.previewEl.appendChild(this.canvasEl);
69095 this.setCanvasPosition();
69100 if(!this.canvasLoaded){
69104 var imageCanvas = document.createElement("canvas");
69106 var imageContext = imageCanvas.getContext("2d");
69108 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69109 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69111 var center = imageCanvas.width / 2;
69113 imageContext.translate(center, center);
69115 imageContext.rotate(this.rotate * Math.PI / 180);
69117 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69119 var canvas = document.createElement("canvas");
69121 var context = canvas.getContext("2d");
69123 canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
69125 canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
69127 switch (this.rotate) {
69130 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69131 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69133 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69134 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69136 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
69137 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
69139 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69140 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69142 if(canvas.width > this.outputMaxWidth) {
69143 var scale = this.outputMaxWidth / canvas.width;
69144 canvas.width = canvas.width * scale;
69145 canvas.height = canvas.height * scale;
69146 context.scale(scale, scale);
69149 context.fillStyle = 'white';
69150 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
69153 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69158 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69159 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69161 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69162 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69164 var targetWidth = this.minWidth - 2 * x;
69165 var targetHeight = this.minHeight - 2 * y;
69169 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69170 scale = targetWidth / width;
69173 if(x > 0 && y == 0){
69174 scale = targetHeight / height;
69177 if(x > 0 && y > 0){
69178 scale = targetWidth / width;
69180 if(width < height){
69181 scale = targetHeight / height;
69185 context.scale(scale, scale);
69187 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69188 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69190 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69191 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69193 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69195 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69200 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69201 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69203 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69204 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69206 var targetWidth = this.minWidth - 2 * x;
69207 var targetHeight = this.minHeight - 2 * y;
69211 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69212 scale = targetWidth / width;
69215 if(x > 0 && y == 0){
69216 scale = targetHeight / height;
69219 if(x > 0 && y > 0){
69220 scale = targetWidth / width;
69222 if(width < height){
69223 scale = targetHeight / height;
69227 context.scale(scale, scale);
69229 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69230 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69232 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69233 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69235 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69236 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69238 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69243 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69244 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69246 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69247 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69249 var targetWidth = this.minWidth - 2 * x;
69250 var targetHeight = this.minHeight - 2 * y;
69254 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69255 scale = targetWidth / width;
69258 if(x > 0 && y == 0){
69259 scale = targetHeight / height;
69262 if(x > 0 && y > 0){
69263 scale = targetWidth / width;
69265 if(width < height){
69266 scale = targetHeight / height;
69270 context.scale(scale, scale);
69271 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69272 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69274 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69275 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69277 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69279 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69286 this.cropData = canvas.toDataURL(this.cropType);
69288 if(this.fireEvent('crop', this, this.cropData) !== false){
69289 this.process(this.file, this.cropData);
69296 setThumbBoxSize : function()
69300 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
69301 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
69302 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
69304 this.minWidth = width;
69305 this.minHeight = height;
69307 if(this.rotate == 90 || this.rotate == 270){
69308 this.minWidth = height;
69309 this.minHeight = width;
69313 height = this.windowSize;
69314 width = Math.ceil(this.minWidth * height / this.minHeight);
69316 if(this.minWidth > this.minHeight){
69317 width = this.windowSize;
69318 height = Math.ceil(this.minHeight * width / this.minWidth);
69321 this.thumbEl.setStyle({
69322 width : width + 'px',
69323 height : height + 'px'
69330 setThumbBoxPosition : function()
69332 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
69333 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
69335 this.thumbEl.setLeft(x);
69336 this.thumbEl.setTop(y);
69340 baseRotateLevel : function()
69342 this.baseRotate = 1;
69345 typeof(this.exif) != 'undefined' &&
69346 typeof(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
69347 [1, 3, 6, 8].indexOf(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != -1
69349 this.baseRotate = this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']];
69352 this.rotate = Roo.dialog.UploadCropbox['Orientation'][this.baseRotate];
69356 baseScaleLevel : function()
69360 if(this.isDocument){
69362 if(this.baseRotate == 6 || this.baseRotate == 8){
69364 height = this.thumbEl.getHeight();
69365 this.baseScale = height / this.imageEl.OriginWidth;
69367 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
69368 width = this.thumbEl.getWidth();
69369 this.baseScale = width / this.imageEl.OriginHeight;
69375 height = this.thumbEl.getHeight();
69376 this.baseScale = height / this.imageEl.OriginHeight;
69378 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
69379 width = this.thumbEl.getWidth();
69380 this.baseScale = width / this.imageEl.OriginWidth;
69386 if(this.baseRotate == 6 || this.baseRotate == 8){
69388 width = this.thumbEl.getHeight();
69389 this.baseScale = width / this.imageEl.OriginHeight;
69391 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
69392 height = this.thumbEl.getWidth();
69393 this.baseScale = height / this.imageEl.OriginHeight;
69396 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69397 height = this.thumbEl.getWidth();
69398 this.baseScale = height / this.imageEl.OriginHeight;
69400 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
69401 width = this.thumbEl.getHeight();
69402 this.baseScale = width / this.imageEl.OriginWidth;
69409 width = this.thumbEl.getWidth();
69410 this.baseScale = width / this.imageEl.OriginWidth;
69412 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
69413 height = this.thumbEl.getHeight();
69414 this.baseScale = height / this.imageEl.OriginHeight;
69417 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69419 height = this.thumbEl.getHeight();
69420 this.baseScale = height / this.imageEl.OriginHeight;
69422 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
69423 width = this.thumbEl.getWidth();
69424 this.baseScale = width / this.imageEl.OriginWidth;
69429 if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
69430 this.baseScale = width / this.minWidth;
69436 getScaleLevel : function()
69438 return this.baseScale * Math.pow(1.02, this.scale);
69441 onTouchStart : function(e)
69443 if(!this.canvasLoaded){
69444 this.beforeSelectFile(e);
69448 var touches = e.browserEvent.touches;
69454 if(touches.length == 1){
69455 this.onMouseDown(e);
69459 if(touches.length != 2){
69465 for(var i = 0, finger; finger = touches[i]; i++){
69466 coords.push(finger.pageX, finger.pageY);
69469 var x = Math.pow(coords[0] - coords[2], 2);
69470 var y = Math.pow(coords[1] - coords[3], 2);
69472 this.startDistance = Math.sqrt(x + y);
69474 this.startScale = this.scale;
69476 this.pinching = true;
69477 this.dragable = false;
69481 onTouchMove : function(e)
69483 if(!this.pinching && !this.dragable){
69487 var touches = e.browserEvent.touches;
69494 this.onMouseMove(e);
69500 for(var i = 0, finger; finger = touches[i]; i++){
69501 coords.push(finger.pageX, finger.pageY);
69504 var x = Math.pow(coords[0] - coords[2], 2);
69505 var y = Math.pow(coords[1] - coords[3], 2);
69507 this.endDistance = Math.sqrt(x + y);
69509 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
69511 if(!this.zoomable()){
69512 this.scale = this.startScale;
69520 onTouchEnd : function(e)
69522 this.pinching = false;
69523 this.dragable = false;
69527 process : function(file, crop)
69530 this.maskEl.mask(this.loadingText);
69533 this.xhr = new XMLHttpRequest();
69535 file.xhr = this.xhr;
69537 this.xhr.open(this.method, this.url, true);
69540 "Accept": "application/json",
69541 "Cache-Control": "no-cache",
69542 "X-Requested-With": "XMLHttpRequest"
69545 for (var headerName in headers) {
69546 var headerValue = headers[headerName];
69548 this.xhr.setRequestHeader(headerName, headerValue);
69554 this.xhr.onload = function()
69556 _this.xhrOnLoad(_this.xhr);
69559 this.xhr.onerror = function()
69561 _this.xhrOnError(_this.xhr);
69564 var formData = new FormData();
69566 formData.append('returnHTML', 'NO');
69569 formData.append('crop', crop);
69570 var blobBin = atob(crop.split(',')[1]);
69572 for(var i = 0; i < blobBin.length; i++) {
69573 array.push(blobBin.charCodeAt(i));
69575 var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
69576 formData.append(this.paramName, croppedFile, file.name);
69579 if(typeof(file.filename) != 'undefined'){
69580 formData.append('filename', file.filename);
69583 if(typeof(file.mimetype) != 'undefined'){
69584 formData.append('mimetype', file.mimetype);
69587 if(this.fireEvent('arrange', this, formData) != false){
69588 this.xhr.send(formData);
69592 xhrOnLoad : function(xhr)
69595 this.maskEl.unmask();
69598 if (xhr.readyState !== 4) {
69599 this.fireEvent('exception', this, xhr);
69603 var response = Roo.decode(xhr.responseText);
69605 if(!response.success){
69606 this.fireEvent('exception', this, xhr);
69610 var response = Roo.decode(xhr.responseText);
69612 this.fireEvent('upload', this, response);
69616 xhrOnError : function()
69619 this.maskEl.unmask();
69622 Roo.log('xhr on error');
69624 var response = Roo.decode(xhr.responseText);
69630 prepare : function(file)
69633 this.maskEl.mask(this.loadingText);
69639 if(typeof(file) === 'string'){
69640 this.loadCanvas(file);
69644 if(!file || !this.urlAPI){
69649 if(typeof(file.type) != 'undefined' && file.type.length != 0) {
69650 this.cropType = file.type;
69655 if(this.fireEvent('prepare', this, this.file) != false){
69657 var reader = new FileReader();
69659 reader.onload = function (e) {
69660 if (e.target.error) {
69661 Roo.log(e.target.error);
69665 var buffer = e.target.result,
69666 dataView = new DataView(buffer),
69668 maxOffset = dataView.byteLength - 4,
69672 if (dataView.getUint16(0) === 0xffd8) {
69673 while (offset < maxOffset) {
69674 markerBytes = dataView.getUint16(offset);
69676 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
69677 markerLength = dataView.getUint16(offset + 2) + 2;
69678 if (offset + markerLength > dataView.byteLength) {
69679 Roo.log('Invalid meta data: Invalid segment size.');
69683 if(markerBytes == 0xffe1){
69684 _this.parseExifData(
69691 offset += markerLength;
69701 var url = _this.urlAPI.createObjectURL(_this.file);
69703 _this.loadCanvas(url);
69708 reader.readAsArrayBuffer(this.file);
69714 parseExifData : function(dataView, offset, length)
69716 var tiffOffset = offset + 10,
69720 if (dataView.getUint32(offset + 4) !== 0x45786966) {
69721 // No Exif data, might be XMP data instead
69725 // Check for the ASCII code for "Exif" (0x45786966):
69726 if (dataView.getUint32(offset + 4) !== 0x45786966) {
69727 // No Exif data, might be XMP data instead
69730 if (tiffOffset + 8 > dataView.byteLength) {
69731 Roo.log('Invalid Exif data: Invalid segment size.');
69734 // Check for the two null bytes:
69735 if (dataView.getUint16(offset + 8) !== 0x0000) {
69736 Roo.log('Invalid Exif data: Missing byte alignment offset.');
69739 // Check the byte alignment:
69740 switch (dataView.getUint16(tiffOffset)) {
69742 littleEndian = true;
69745 littleEndian = false;
69748 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
69751 // Check for the TIFF tag marker (0x002A):
69752 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
69753 Roo.log('Invalid Exif data: Missing TIFF marker.');
69756 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
69757 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
69759 this.parseExifTags(
69762 tiffOffset + dirOffset,
69767 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
69772 if (dirOffset + 6 > dataView.byteLength) {
69773 Roo.log('Invalid Exif data: Invalid directory offset.');
69776 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
69777 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
69778 if (dirEndOffset + 4 > dataView.byteLength) {
69779 Roo.log('Invalid Exif data: Invalid directory size.');
69782 for (i = 0; i < tagsNumber; i += 1) {
69786 dirOffset + 2 + 12 * i, // tag offset
69790 // Return the offset to the next directory:
69791 return dataView.getUint32(dirEndOffset, littleEndian);
69794 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
69796 var tag = dataView.getUint16(offset, littleEndian);
69798 this.exif[tag] = this.getExifValue(
69802 dataView.getUint16(offset + 2, littleEndian), // tag type
69803 dataView.getUint32(offset + 4, littleEndian), // tag length
69808 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
69810 var tagType = Roo.dialog.UploadCropbox.exifTagTypes[type],
69819 Roo.log('Invalid Exif data: Invalid tag type.');
69823 tagSize = tagType.size * length;
69824 // Determine if the value is contained in the dataOffset bytes,
69825 // or if the value at the dataOffset is a pointer to the actual data:
69826 dataOffset = tagSize > 4 ?
69827 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
69828 if (dataOffset + tagSize > dataView.byteLength) {
69829 Roo.log('Invalid Exif data: Invalid data offset.');
69832 if (length === 1) {
69833 return tagType.getValue(dataView, dataOffset, littleEndian);
69836 for (i = 0; i < length; i += 1) {
69837 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
69840 if (tagType.ascii) {
69842 // Concatenate the chars:
69843 for (i = 0; i < values.length; i += 1) {
69845 // Ignore the terminating NULL byte(s):
69846 if (c === '\u0000') {
69858 Roo.apply(Roo.dialog.UploadCropbox, {
69860 'Orientation': 0x0112
69864 1: 0, //'top-left',
69866 3: 180, //'bottom-right',
69867 // 4: 'bottom-left',
69869 6: 90, //'right-top',
69870 // 7: 'right-bottom',
69871 8: 270 //'left-bottom'
69875 // byte, 8-bit unsigned int:
69877 getValue: function (dataView, dataOffset) {
69878 return dataView.getUint8(dataOffset);
69882 // ascii, 8-bit byte:
69884 getValue: function (dataView, dataOffset) {
69885 return String.fromCharCode(dataView.getUint8(dataOffset));
69890 // short, 16 bit int:
69892 getValue: function (dataView, dataOffset, littleEndian) {
69893 return dataView.getUint16(dataOffset, littleEndian);
69897 // long, 32 bit int:
69899 getValue: function (dataView, dataOffset, littleEndian) {
69900 return dataView.getUint32(dataOffset, littleEndian);
69904 // rational = two long values, first is numerator, second is denominator:
69906 getValue: function (dataView, dataOffset, littleEndian) {
69907 return dataView.getUint32(dataOffset, littleEndian) /
69908 dataView.getUint32(dataOffset + 4, littleEndian);
69912 // slong, 32 bit signed int:
69914 getValue: function (dataView, dataOffset, littleEndian) {
69915 return dataView.getInt32(dataOffset, littleEndian);
69919 // srational, two slongs, first is numerator, second is denominator:
69921 getValue: function (dataView, dataOffset, littleEndian) {
69922 return dataView.getInt32(dataOffset, littleEndian) /
69923 dataView.getInt32(dataOffset + 4, littleEndian);
69933 cls : 'btn-group roo-upload-cropbox-rotate-left',
69934 action : 'rotate-left',
69938 cls : 'btn btn-default',
69939 html : '<i class="fa fa-undo"></i>'
69945 cls : 'btn-group roo-upload-cropbox-picture',
69946 action : 'picture',
69950 cls : 'btn btn-default',
69951 html : '<i class="fa fa-picture-o"></i>'
69957 cls : 'btn-group roo-upload-cropbox-rotate-right',
69958 action : 'rotate-right',
69962 cls : 'btn btn-default',
69963 html : '<i class="fa fa-repeat"></i>'
69971 cls : 'btn-group roo-upload-cropbox-rotate-left',
69972 action : 'rotate-left',
69976 cls : 'btn btn-default',
69977 html : '<i class="fa fa-undo"></i>'
69983 cls : 'btn-group roo-upload-cropbox-download',
69984 action : 'download',
69988 cls : 'btn btn-default',
69989 html : '<i class="fa fa-download"></i>'
69995 cls : 'btn-group roo-upload-cropbox-crop',
70000 cls : 'btn btn-default',
70001 html : '<i class="fa fa-crop"></i>'
70007 cls : 'btn-group roo-upload-cropbox-trash',
70012 cls : 'btn btn-default',
70013 html : '<i class="fa fa-trash"></i>'
70019 cls : 'btn-group roo-upload-cropbox-rotate-right',
70020 action : 'rotate-right',
70024 cls : 'btn btn-default',
70025 html : '<i class="fa fa-repeat"></i>'
70033 cls : 'btn-group roo-upload-cropbox-rotate-left',
70034 action : 'rotate-left',
70038 cls : 'btn btn-default',
70039 html : '<i class="fa fa-undo"></i>'
70045 cls : 'btn-group roo-upload-cropbox-rotate-right',
70046 action : 'rotate-right',
70050 cls : 'btn btn-default',
70051 html : '<i class="fa fa-repeat"></i>'